Move files to subdirectories (#6599)
authorVitaliy <silverunicorn2011@yandex.ru>
Wed, 8 Nov 2017 22:56:20 +0000 (01:56 +0300)
committerLoïc Blot <nerzhul@users.noreply.github.com>
Wed, 8 Nov 2017 22:56:20 +0000 (23:56 +0100)
* Move files around

189 files changed:
build/android/jni/Android.mk
src/CMakeLists.txt
src/cavegen.cpp [deleted file]
src/cavegen.h [deleted file]
src/client.cpp
src/client/clientlauncher.cpp
src/client/inputhandler.cpp
src/client/inputhandler.h
src/client/joystick_controller.cpp
src/database-dummy.cpp [deleted file]
src/database-dummy.h [deleted file]
src/database-files.cpp [deleted file]
src/database-files.h [deleted file]
src/database-leveldb.cpp [deleted file]
src/database-leveldb.h [deleted file]
src/database-postgresql.cpp [deleted file]
src/database-postgresql.h [deleted file]
src/database-redis.cpp [deleted file]
src/database-redis.h [deleted file]
src/database-sqlite3.cpp [deleted file]
src/database-sqlite3.h [deleted file]
src/database.cpp [deleted file]
src/database.h [deleted file]
src/database/CMakeLists.txt [new file with mode: 0644]
src/database/database-dummy.cpp [new file with mode: 0644]
src/database/database-dummy.h [new file with mode: 0644]
src/database/database-files.cpp [new file with mode: 0644]
src/database/database-files.h [new file with mode: 0644]
src/database/database-leveldb.cpp [new file with mode: 0644]
src/database/database-leveldb.h [new file with mode: 0644]
src/database/database-postgresql.cpp [new file with mode: 0644]
src/database/database-postgresql.h [new file with mode: 0644]
src/database/database-redis.cpp [new file with mode: 0644]
src/database/database-redis.h [new file with mode: 0644]
src/database/database-sqlite3.cpp [new file with mode: 0644]
src/database/database-sqlite3.h [new file with mode: 0644]
src/database/database.cpp [new file with mode: 0644]
src/database/database.h [new file with mode: 0644]
src/dungeongen.cpp [deleted file]
src/dungeongen.h [deleted file]
src/emerge.cpp
src/emerge.h
src/game.cpp
src/gui/CMakeLists.txt [new file with mode: 0644]
src/gui/guiChatConsole.cpp [new file with mode: 0644]
src/gui/guiChatConsole.h [new file with mode: 0644]
src/gui/guiEditBoxWithScrollbar.cpp [new file with mode: 0644]
src/gui/guiEditBoxWithScrollbar.h [new file with mode: 0644]
src/gui/guiEngine.cpp [new file with mode: 0644]
src/gui/guiEngine.h [new file with mode: 0644]
src/gui/guiFormSpecMenu.cpp [new file with mode: 0644]
src/gui/guiFormSpecMenu.h [new file with mode: 0644]
src/gui/guiKeyChangeMenu.cpp [new file with mode: 0644]
src/gui/guiKeyChangeMenu.h [new file with mode: 0644]
src/gui/guiMainMenu.h [new file with mode: 0644]
src/gui/guiPasswordChange.cpp [new file with mode: 0644]
src/gui/guiPasswordChange.h [new file with mode: 0644]
src/gui/guiPathSelectMenu.cpp [new file with mode: 0644]
src/gui/guiPathSelectMenu.h [new file with mode: 0644]
src/gui/guiTable.cpp [new file with mode: 0644]
src/gui/guiTable.h [new file with mode: 0644]
src/gui/guiVolumeChange.cpp [new file with mode: 0644]
src/gui/guiVolumeChange.h [new file with mode: 0644]
src/gui/intlGUIEditBox.cpp [new file with mode: 0644]
src/gui/intlGUIEditBox.h [new file with mode: 0644]
src/gui/mainmenumanager.h [new file with mode: 0644]
src/gui/modalMenu.h [new file with mode: 0644]
src/gui/touchscreengui.cpp [new file with mode: 0644]
src/gui/touchscreengui.h [new file with mode: 0644]
src/guiChatConsole.cpp [deleted file]
src/guiChatConsole.h [deleted file]
src/guiEditBoxWithScrollbar.cpp [deleted file]
src/guiEditBoxWithScrollbar.h [deleted file]
src/guiEngine.cpp [deleted file]
src/guiEngine.h [deleted file]
src/guiFormSpecMenu.cpp [deleted file]
src/guiFormSpecMenu.h [deleted file]
src/guiKeyChangeMenu.cpp [deleted file]
src/guiKeyChangeMenu.h [deleted file]
src/guiMainMenu.h [deleted file]
src/guiPasswordChange.cpp [deleted file]
src/guiPasswordChange.h [deleted file]
src/guiPathSelectMenu.cpp [deleted file]
src/guiPathSelectMenu.h [deleted file]
src/guiTable.cpp [deleted file]
src/guiTable.h [deleted file]
src/guiVolumeChange.cpp [deleted file]
src/guiVolumeChange.h [deleted file]
src/hud.cpp
src/intlGUIEditBox.cpp [deleted file]
src/intlGUIEditBox.h [deleted file]
src/main.cpp
src/mainmenumanager.h [deleted file]
src/map.cpp
src/map_settings_manager.cpp
src/mapblock.h
src/mapgen.cpp [deleted file]
src/mapgen.h [deleted file]
src/mapgen/CMakeLists.txt [new file with mode: 0644]
src/mapgen/cavegen.cpp [new file with mode: 0644]
src/mapgen/cavegen.h [new file with mode: 0644]
src/mapgen/dungeongen.cpp [new file with mode: 0644]
src/mapgen/dungeongen.h [new file with mode: 0644]
src/mapgen/mapgen.cpp [new file with mode: 0644]
src/mapgen/mapgen.h [new file with mode: 0644]
src/mapgen/mapgen_carpathian.cpp [new file with mode: 0644]
src/mapgen/mapgen_carpathian.h [new file with mode: 0644]
src/mapgen/mapgen_flat.cpp [new file with mode: 0644]
src/mapgen/mapgen_flat.h [new file with mode: 0644]
src/mapgen/mapgen_fractal.cpp [new file with mode: 0644]
src/mapgen/mapgen_fractal.h [new file with mode: 0644]
src/mapgen/mapgen_singlenode.cpp [new file with mode: 0644]
src/mapgen/mapgen_singlenode.h [new file with mode: 0644]
src/mapgen/mapgen_v5.cpp [new file with mode: 0644]
src/mapgen/mapgen_v5.h [new file with mode: 0644]
src/mapgen/mapgen_v6.cpp [new file with mode: 0644]
src/mapgen/mapgen_v6.h [new file with mode: 0644]
src/mapgen/mapgen_v7.cpp [new file with mode: 0644]
src/mapgen/mapgen_v7.h [new file with mode: 0644]
src/mapgen/mapgen_valleys.cpp [new file with mode: 0644]
src/mapgen/mapgen_valleys.h [new file with mode: 0644]
src/mapgen/mg_biome.cpp [new file with mode: 0644]
src/mapgen/mg_biome.h [new file with mode: 0644]
src/mapgen/mg_decoration.cpp [new file with mode: 0644]
src/mapgen/mg_decoration.h [new file with mode: 0644]
src/mapgen/mg_ore.cpp [new file with mode: 0644]
src/mapgen/mg_ore.h [new file with mode: 0644]
src/mapgen/mg_schematic.cpp [new file with mode: 0644]
src/mapgen/mg_schematic.h [new file with mode: 0644]
src/mapgen/treegen.cpp [new file with mode: 0644]
src/mapgen/treegen.h [new file with mode: 0644]
src/mapgen_carpathian.cpp [deleted file]
src/mapgen_carpathian.h [deleted file]
src/mapgen_flat.cpp [deleted file]
src/mapgen_flat.h [deleted file]
src/mapgen_fractal.cpp [deleted file]
src/mapgen_fractal.h [deleted file]
src/mapgen_singlenode.cpp [deleted file]
src/mapgen_singlenode.h [deleted file]
src/mapgen_v5.cpp [deleted file]
src/mapgen_v5.h [deleted file]
src/mapgen_v6.cpp [deleted file]
src/mapgen_v6.h [deleted file]
src/mapgen_v7.cpp [deleted file]
src/mapgen_v7.h [deleted file]
src/mapgen_valleys.cpp [deleted file]
src/mapgen_valleys.h [deleted file]
src/mg_biome.cpp [deleted file]
src/mg_biome.h [deleted file]
src/mg_decoration.cpp [deleted file]
src/mg_decoration.h [deleted file]
src/mg_ore.cpp [deleted file]
src/mg_ore.h [deleted file]
src/mg_schematic.cpp [deleted file]
src/mg_schematic.h [deleted file]
src/modalMenu.h [deleted file]
src/network/connectionthreads.h
src/script/common/c_content.cpp
src/script/cpp_api/s_env.cpp
src/script/cpp_api/s_mainmenu.h
src/script/lua_api/l_client.cpp
src/script/lua_api/l_env.cpp
src/script/lua_api/l_mainmenu.cpp
src/script/lua_api/l_mapgen.cpp
src/script/lua_api/l_sound.cpp
src/script/lua_api/l_vmanip.cpp
src/server.cpp
src/serverenvironment.cpp
src/subgame.cpp
src/touchscreengui.cpp [deleted file]
src/touchscreengui.h [deleted file]
src/treegen.cpp [deleted file]
src/treegen.h [deleted file]
src/unittest/test_map_settings_manager.cpp
src/unittest/test_schematic.cpp
src/util/container.h
src/util/directiontables.h
src/util/numeric.cpp
src/util/numeric.h
src/util/pointedthing.cpp
src/util/pointedthing.h
src/util/pointer.h
src/util/serialize.cpp
src/util/serialize.h
src/util/string.cpp
src/util/thread.h
src/util/timetaker.cpp
src/util/timetaker.h
util/travis/clang-format-whitelist.txt

index 854ab75a713f9042e0ad3b55e0b2fd17bcb6c0d6..375ab5ce47a0516e5b8629d50cde9c003a32d494 100644 (file)
@@ -114,7 +114,7 @@ LOCAL_C_INCLUDES := \
 LOCAL_SRC_FILES := \
                jni/src/ban.cpp                           \
                jni/src/camera.cpp                        \
-               jni/src/cavegen.cpp                       \
+               jni/src/mapgen/cavegen.cpp                \
                jni/src/chat.cpp                          \
                jni/src/client.cpp                        \
                jni/src/clientenvironment.cpp             \
@@ -132,13 +132,13 @@ LOCAL_SRC_FILES := \
                jni/src/content_sao.cpp                   \
                jni/src/convert_json.cpp                  \
                jni/src/craftdef.cpp                      \
-               jni/src/database-dummy.cpp                \
-               jni/src/database-files.cpp                \
-               jni/src/database-sqlite3.cpp              \
-               jni/src/database.cpp                      \
+               jni/src/database/database-dummy.cpp       \
+               jni/src/database/database-files.cpp       \
+               jni/src/database/database-sqlite3.cpp     \
+               jni/src/database/database.cpp             \
                jni/src/debug.cpp                         \
                jni/src/defaultsettings.cpp               \
-               jni/src/dungeongen.cpp                    \
+               jni/src/mapgen/dungeongen.cpp             \
                jni/src/emerge.cpp                        \
                jni/src/environment.cpp                   \
                jni/src/face_position_cache.cpp           \
@@ -148,20 +148,20 @@ LOCAL_SRC_FILES := \
                jni/src/game.cpp                          \
                jni/src/genericobject.cpp                 \
                jni/src/gettext.cpp                       \
-               jni/src/guiChatConsole.cpp                \
-               jni/src/guiEditBoxWithScrollbar.cpp       \
-               jni/src/guiEngine.cpp                     \
-               jni/src/guiPathSelectMenu.cpp             \
-               jni/src/guiFormSpecMenu.cpp               \
-               jni/src/guiKeyChangeMenu.cpp              \
-               jni/src/guiPasswordChange.cpp             \
-               jni/src/guiTable.cpp                      \
+               jni/src/gui/guiChatConsole.cpp            \
+               jni/src/gui/guiEditBoxWithScrollbar.cpp   \
+               jni/src/gui/guiEngine.cpp                 \
+               jni/src/gui/guiPathSelectMenu.cpp         \
+               jni/src/gui/guiFormSpecMenu.cpp           \
+               jni/src/gui/guiKeyChangeMenu.cpp          \
+               jni/src/gui/guiPasswordChange.cpp         \
+               jni/src/gui/guiTable.cpp                  \
                jni/src/guiscalingfilter.cpp              \
-               jni/src/guiVolumeChange.cpp               \
+               jni/src/gui/guiVolumeChange.cpp           \
                jni/src/httpfetch.cpp                     \
                jni/src/hud.cpp                           \
                jni/src/imagefilters.cpp                  \
-               jni/src/intlGUIEditBox.cpp                \
+               jni/src/gui/intlGUIEditBox.cpp            \
                jni/src/inventory.cpp                     \
                jni/src/inventorymanager.cpp              \
                jni/src/itemdef.cpp                       \
@@ -175,24 +175,24 @@ LOCAL_SRC_FILES := \
                jni/src/map_settings_manager.cpp          \
                jni/src/mapblock.cpp                      \
                jni/src/mapblock_mesh.cpp                 \
-               jni/src/mapgen.cpp                        \
-               jni/src/mapgen_carpathian.cpp             \
-               jni/src/mapgen_flat.cpp                   \
-               jni/src/mapgen_fractal.cpp                \
-               jni/src/mapgen_singlenode.cpp             \
-               jni/src/mapgen_v5.cpp                     \
-               jni/src/mapgen_v6.cpp                     \
-               jni/src/mapgen_v7.cpp                     \
-               jni/src/mapgen_valleys.cpp                \
+               jni/src/mapgen/mapgen.cpp                 \
+               jni/src/mapgen/mapgen_carpathian.cpp      \
+               jni/src/mapgen/mapgen_flat.cpp            \
+               jni/src/mapgen/mapgen_fractal.cpp         \
+               jni/src/mapgen/mapgen_singlenode.cpp      \
+               jni/src/mapgen/mapgen_v5.cpp              \
+               jni/src/mapgen/mapgen_v6.cpp              \
+               jni/src/mapgen/mapgen_v7.cpp              \
+               jni/src/mapgen/mapgen_valleys.cpp         \
                jni/src/mapnode.cpp                       \
                jni/src/mapsector.cpp                     \
                jni/src/mesh.cpp                          \
                jni/src/mesh_generator_thread.cpp         \
                jni/src/metadata.cpp                      \
-               jni/src/mg_biome.cpp                      \
-               jni/src/mg_decoration.cpp                 \
-               jni/src/mg_ore.cpp                        \
-               jni/src/mg_schematic.cpp                  \
+               jni/src/mapgen/mg_biome.cpp               \
+               jni/src/mapgen/mg_decoration.cpp          \
+               jni/src/mapgen/mg_ore.cpp                 \
+               jni/src/mapgen/mg_schematic.cpp           \
                jni/src/minimap.cpp                       \
                jni/src/mods.cpp                          \
                jni/src/nameidmapping.cpp                 \
@@ -228,7 +228,7 @@ LOCAL_SRC_FILES := \
                jni/src/subgame.cpp                       \
                jni/src/tileanimation.cpp                 \
                jni/src/tool.cpp                          \
-               jni/src/treegen.cpp                       \
+               jni/src/mapgen/treegen.cpp                \
                jni/src/version.cpp                       \
                jni/src/voxel.cpp                         \
                jni/src/voxelalgorithms.cpp               \
index 82f60be868f48f5c6c67988c0d1a2b8f4c4eb5c4..9964b828b5b8f178d48c6041d02deb3e55a82dc6 100644 (file)
@@ -371,6 +371,9 @@ add_custom_target(GenerateVersion
 
 
 add_subdirectory(threading)
+add_subdirectory(database)
+add_subdirectory(gui)
+add_subdirectory(mapgen)
 add_subdirectory(network)
 add_subdirectory(script)
 add_subdirectory(unittest)
@@ -378,8 +381,9 @@ add_subdirectory(util)
 add_subdirectory(irrlicht_changes)
 
 set(common_SRCS
+       ${database_SRCS}
+       ${mapgen_SRCS}
        ban.cpp
-       cavegen.cpp
        chat.cpp
        clientiface.cpp
        collision.cpp
@@ -388,16 +392,8 @@ set(common_SRCS
        content_sao.cpp
        convert_json.cpp
        craftdef.cpp
-       database-dummy.cpp
-       database-files.cpp
-       database-leveldb.cpp
-       database-postgresql.cpp
-       database-redis.cpp
-       database-sqlite3.cpp
-       database.cpp
        debug.cpp
        defaultsettings.cpp
-       dungeongen.cpp
        emerge.cpp
        environment.cpp
        face_position_cache.cpp
@@ -414,22 +410,9 @@ set(common_SRCS
        map.cpp
        map_settings_manager.cpp
        mapblock.cpp
-       mapgen.cpp
-       mapgen_carpathian.cpp
-       mapgen_flat.cpp
-       mapgen_fractal.cpp
-       mapgen_singlenode.cpp
-       mapgen_v5.cpp
-       mapgen_v6.cpp
-       mapgen_v7.cpp
-       mapgen_valleys.cpp
        mapnode.cpp
        mapsector.cpp
        metadata.cpp
-       mg_biome.cpp
-       mg_decoration.cpp
-       mg_ore.cpp
-       mg_schematic.cpp
        modchannels.cpp
        mods.cpp
        nameidmapping.cpp
@@ -462,7 +445,6 @@ set(common_SRCS
        tileanimation.cpp
        tool.cpp
        translation.cpp
-       treegen.cpp
        version.cpp
        voxel.cpp
        voxelalgorithms.cpp
@@ -503,6 +485,7 @@ endif(BUILD_CLIENT)
 set(client_SRCS
        ${client_SRCS}
        ${common_SRCS}
+       ${gui_SRCS}
        ${sound_SRCS}
        ${client_network_SRCS}
        ${client_irrlicht_changes_SRCS}
@@ -520,19 +503,9 @@ set(client_SRCS
        filecache.cpp
        fontengine.cpp
        game.cpp
-       guiChatConsole.cpp
-       guiEditBoxWithScrollbar.cpp
-       guiEngine.cpp
-       guiPathSelectMenu.cpp
-       guiFormSpecMenu.cpp
-       guiKeyChangeMenu.cpp
-       guiPasswordChange.cpp
        guiscalingfilter.cpp
-       guiTable.cpp
-       guiVolumeChange.cpp
        hud.cpp
        imagefilters.cpp
-       intlGUIEditBox.cpp
        keycode.cpp
        localplayer.cpp
        main.cpp
diff --git a/src/cavegen.cpp b/src/cavegen.cpp
deleted file mode 100644 (file)
index e666345..0000000
+++ /dev/null
@@ -1,882 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "util/numeric.h"
-#include "map.h"
-#include "mapgen.h"
-#include "mapgen_v5.h"
-#include "mapgen_v6.h"
-#include "mapgen_v7.h"
-#include "mg_biome.h"
-#include "cavegen.h"
-
-static NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0);
-
-
-////
-//// CavesNoiseIntersection
-////
-
-CavesNoiseIntersection::CavesNoiseIntersection(
-       INodeDefManager *nodedef, BiomeManager *biomemgr, v3s16 chunksize,
-       NoiseParams *np_cave1, NoiseParams *np_cave2, s32 seed, float cave_width)
-{
-       assert(nodedef);
-       assert(biomemgr);
-
-       m_ndef = nodedef;
-       m_bmgr = biomemgr;
-
-       m_csize = chunksize;
-       m_cave_width = cave_width;
-
-       m_ystride    = m_csize.X;
-       m_zstride_1d = m_csize.X * (m_csize.Y + 1);
-
-       // Noises are created using 1-down overgeneration
-       // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for
-       // re-carving the solid overtop placed for blocking sunlight
-       noise_cave1 = new Noise(np_cave1, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
-       noise_cave2 = new Noise(np_cave2, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
-}
-
-
-CavesNoiseIntersection::~CavesNoiseIntersection()
-{
-       delete noise_cave1;
-       delete noise_cave2;
-}
-
-
-void CavesNoiseIntersection::generateCaves(MMVManip *vm,
-       v3s16 nmin, v3s16 nmax, u8 *biomemap)
-{
-       assert(vm);
-       assert(biomemap);
-
-       noise_cave1->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
-       noise_cave2->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
-
-       const v3s16 &em = vm->m_area.getExtent();
-       u32 index2d = 0;  // Biomemap index
-
-       for (s16 z = nmin.Z; z <= nmax.Z; z++)
-       for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) {
-               bool column_is_open = false;  // Is column open to overground
-               bool is_under_river = false;  // Is column under river water
-               bool is_under_tunnel = false;  // Is tunnel or is under tunnel
-               bool is_top_filler_above = false;  // Is top or filler above node
-               // Indexes at column top
-               u32 vi = vm->m_area.index(x, nmax.Y, z);
-               u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
-                       (x - nmin.X);  // 3D noise index
-               // Biome of column
-               Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index2d]);
-               u16 depth_top = biome->depth_top;
-               u16 base_filler = depth_top + biome->depth_filler;
-               u16 depth_riverbed = biome->depth_riverbed;
-               u16 nplaced = 0;
-               // Don't excavate the overgenerated stone at nmax.Y + 1,
-               // this creates a 'roof' over the tunnel, preventing light in
-               // tunnels at mapchunk borders when generating mapchunks upwards.
-               // This 'roof' is removed when the mapchunk above is generated.
-               for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
-                               index3d -= m_ystride,
-                               vm->m_area.add_y(em, vi, -1)) {
-                       content_t c = vm->m_data[vi].getContent();
-
-                       if (c == CONTENT_AIR || c == biome->c_water_top ||
-                                       c == biome->c_water) {
-                               column_is_open = true;
-                               is_top_filler_above = false;
-                               continue;
-                       }
-
-                       if (c == biome->c_river_water) {
-                               column_is_open = true;
-                               is_under_river = true;
-                               is_top_filler_above = false;
-                               continue;
-                       }
-
-                       // Ground
-                       float d1 = contour(noise_cave1->result[index3d]);
-                       float d2 = contour(noise_cave2->result[index3d]);
-
-                       if (d1 * d2 > m_cave_width && m_ndef->get(c).is_ground_content) {
-                               // In tunnel and ground content, excavate
-                               vm->m_data[vi] = MapNode(CONTENT_AIR);
-                               is_under_tunnel = true;
-                               // If tunnel roof is top or filler, replace with stone
-                               if (is_top_filler_above)
-                                       vm->m_data[vi + em.X] = MapNode(biome->c_stone);
-                               is_top_filler_above = false;
-                       } else if (column_is_open && is_under_tunnel &&
-                                       (c == biome->c_stone || c == biome->c_filler)) {
-                               // Tunnel entrance floor, place biome surface nodes
-                               if (is_under_river) {
-                                       if (nplaced < depth_riverbed) {
-                                               vm->m_data[vi] = MapNode(biome->c_riverbed);
-                                               is_top_filler_above = true;
-                                               nplaced++;
-                                       } else {
-                                               // Disable top/filler placement
-                                               column_is_open = false;
-                                               is_under_river = false;
-                                               is_under_tunnel = false;
-                                       }
-                               } else if (nplaced < depth_top) {
-                                       vm->m_data[vi] = MapNode(biome->c_top);
-                                       is_top_filler_above = true;
-                                       nplaced++;
-                               } else if (nplaced < base_filler) {
-                                       vm->m_data[vi] = MapNode(biome->c_filler);
-                                       is_top_filler_above = true;
-                                       nplaced++;
-                               } else {
-                                       // Disable top/filler placement
-                                       column_is_open = false;
-                                       is_under_tunnel = false;
-                               }
-                       } else {
-                               // Not tunnel or tunnel entrance floor
-                               // Check node for possible replacing with stone for tunnel roof
-                               if (c == biome->c_top || c == biome->c_filler)
-                                       is_top_filler_above = true;
-
-                               column_is_open = false;
-                       }
-               }
-       }
-}
-
-
-////
-//// CavernsNoise
-////
-
-CavernsNoise::CavernsNoise(
-       INodeDefManager *nodedef, v3s16 chunksize, NoiseParams *np_cavern,
-       s32 seed, float cavern_limit, float cavern_taper, float cavern_threshold)
-{
-       assert(nodedef);
-
-       m_ndef  = nodedef;
-
-       m_csize            = chunksize;
-       m_cavern_limit     = cavern_limit;
-       m_cavern_taper     = cavern_taper;
-       m_cavern_threshold = cavern_threshold;
-
-       m_ystride = m_csize.X;
-       m_zstride_1d = m_csize.X * (m_csize.Y + 1);
-
-       // Noise is created using 1-down overgeneration
-       // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for
-       // re-carving the solid overtop placed for blocking sunlight
-       noise_cavern = new Noise(np_cavern, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
-
-       c_water_source = m_ndef->getId("mapgen_water_source");
-       if (c_water_source == CONTENT_IGNORE)
-               c_water_source = CONTENT_AIR;
-
-       c_lava_source = m_ndef->getId("mapgen_lava_source");
-       if (c_lava_source == CONTENT_IGNORE)
-               c_lava_source = CONTENT_AIR;
-}
-
-
-CavernsNoise::~CavernsNoise()
-{
-       delete noise_cavern;
-}
-
-
-bool CavernsNoise::generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax)
-{
-       assert(vm);
-
-       // Calculate noise
-       noise_cavern->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
-
-       // Cache cavern_amp values
-       float *cavern_amp = new float[m_csize.Y + 1];
-       u8 cavern_amp_index = 0;  // Index zero at column top
-       for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, cavern_amp_index++) {
-               cavern_amp[cavern_amp_index] =
-                       MYMIN((m_cavern_limit - y) / (float)m_cavern_taper, 1.0f);
-       }
-
-       //// Place nodes
-       bool near_cavern = false;
-       const v3s16 &em = vm->m_area.getExtent();
-       u32 index2d = 0;
-
-       for (s16 z = nmin.Z; z <= nmax.Z; z++)
-       for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) {
-               // Reset cave_amp index to column top
-               cavern_amp_index = 0;
-               // Initial voxelmanip index at column top
-               u32 vi = vm->m_area.index(x, nmax.Y, z);
-               // Initial 3D noise index at column top
-               u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
-                       (x - nmin.X);
-               // Don't excavate the overgenerated stone at node_max.Y + 1,
-               // this creates a 'roof' over the cavern, preventing light in
-               // caverns at mapchunk borders when generating mapchunks upwards.
-               // This 'roof' is excavated when the mapchunk above is generated.
-               for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
-                               index3d -= m_ystride,
-                               vm->m_area.add_y(em, vi, -1),
-                               cavern_amp_index++) {
-                       content_t c = vm->m_data[vi].getContent();
-                       float n_absamp_cavern = fabs(noise_cavern->result[index3d]) *
-                               cavern_amp[cavern_amp_index];
-                       // Disable CavesRandomWalk at a safe distance from caverns
-                       // to avoid excessively spreading liquids in caverns.
-                       if (n_absamp_cavern > m_cavern_threshold - 0.1f) {
-                               near_cavern = true;
-                               if (n_absamp_cavern > m_cavern_threshold &&
-                                               m_ndef->get(c).is_ground_content)
-                                       vm->m_data[vi] = MapNode(CONTENT_AIR);
-                       }
-               }
-       }
-
-       delete[] cavern_amp;
-
-       return near_cavern;
-}
-
-
-////
-//// CavesRandomWalk
-////
-
-CavesRandomWalk::CavesRandomWalk(
-       INodeDefManager *ndef,
-       GenerateNotifier *gennotify,
-       s32 seed,
-       int water_level,
-       content_t water_source,
-       content_t lava_source,
-       int lava_depth)
-{
-       assert(ndef);
-
-       this->ndef           = ndef;
-       this->gennotify      = gennotify;
-       this->seed           = seed;
-       this->water_level    = water_level;
-       this->np_caveliquids = &nparams_caveliquids;
-       this->lava_depth     = lava_depth;
-
-       c_water_source = water_source;
-       if (c_water_source == CONTENT_IGNORE)
-               c_water_source = ndef->getId("mapgen_water_source");
-       if (c_water_source == CONTENT_IGNORE)
-               c_water_source = CONTENT_AIR;
-
-       c_lava_source = lava_source;
-       if (c_lava_source == CONTENT_IGNORE)
-               c_lava_source = ndef->getId("mapgen_lava_source");
-       if (c_lava_source == CONTENT_IGNORE)
-               c_lava_source = CONTENT_AIR;
-}
-
-
-void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
-       PseudoRandom *ps, bool is_large_cave, int max_stone_height, s16 *heightmap)
-{
-       assert(vm);
-       assert(ps);
-
-       this->vm         = vm;
-       this->ps         = ps;
-       this->node_min   = nmin;
-       this->node_max   = nmax;
-       this->heightmap  = heightmap;
-       this->large_cave = is_large_cave;
-
-       this->ystride = nmax.X - nmin.X + 1;
-
-       // Set initial parameters from randomness
-       int dswitchint = ps->range(1, 14);
-       flooded = ps->range(1, 2) == 2;
-
-       if (large_cave) {
-               part_max_length_rs = ps->range(2, 4);
-               tunnel_routepoints = ps->range(5, ps->range(15, 30));
-               min_tunnel_diameter = 5;
-               max_tunnel_diameter = ps->range(7, ps->range(8, 24));
-       } else {
-               part_max_length_rs = ps->range(2, 9);
-               tunnel_routepoints = ps->range(10, ps->range(15, 30));
-               min_tunnel_diameter = 2;
-               max_tunnel_diameter = ps->range(2, 6);
-       }
-
-       large_cave_is_flat = (ps->range(0, 1) == 0);
-
-       main_direction = v3f(0, 0, 0);
-
-       // Allowed route area size in nodes
-       ar = node_max - node_min + v3s16(1, 1, 1);
-       // Area starting point in nodes
-       of = node_min;
-
-       // Allow a bit more
-       //(this should be more than the maximum radius of the tunnel)
-       const s16 insure = 10;
-       s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
-       ar += v3s16(1, 0, 1) * more * 2;
-       of -= v3s16(1, 0, 1) * more;
-
-       route_y_min = 0;
-       // Allow half a diameter + 7 over stone surface
-       route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
-
-       // Limit maximum to area
-       route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
-
-       if (large_cave) {
-               s16 minpos = 0;
-               if (node_min.Y < water_level && node_max.Y > water_level) {
-                       minpos = water_level - max_tunnel_diameter / 3 - of.Y;
-                       route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
-               }
-               route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
-               route_y_min = rangelim(route_y_min, 0, route_y_max);
-       }
-
-       s16 route_start_y_min = route_y_min;
-       s16 route_start_y_max = route_y_max;
-
-       route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
-       route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
-
-       // Randomize starting position
-       orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
-       orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
-       orp.X = (float)(ps->next() % ar.X) + 0.5f;
-
-       // Add generation notify begin event
-       if (gennotify) {
-               v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
-               GenNotifyType notifytype = large_cave ?
-                       GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
-               gennotify->addEvent(notifytype, abs_pos);
-       }
-
-       // Generate some tunnel starting from orp
-       for (u16 j = 0; j < tunnel_routepoints; j++)
-               makeTunnel(j % dswitchint == 0);
-
-       // Add generation notify end event
-       if (gennotify) {
-               v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
-               GenNotifyType notifytype = large_cave ?
-                       GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
-               gennotify->addEvent(notifytype, abs_pos);
-       }
-}
-
-
-void CavesRandomWalk::makeTunnel(bool dirswitch)
-{
-       if (dirswitch && !large_cave) {
-               main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
-               main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
-               main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
-
-               main_direction *= (float)ps->range(0, 10) / 10;
-       }
-
-       // Randomize size
-       s16 min_d = min_tunnel_diameter;
-       s16 max_d = max_tunnel_diameter;
-       rs = ps->range(min_d, max_d);
-       s16 rs_part_max_length_rs = rs * part_max_length_rs;
-
-       v3s16 maxlen;
-       if (large_cave) {
-               maxlen = v3s16(
-                       rs_part_max_length_rs,
-                       rs_part_max_length_rs / 2,
-                       rs_part_max_length_rs
-               );
-       } else {
-               maxlen = v3s16(
-                       rs_part_max_length_rs,
-                       ps->range(1, rs_part_max_length_rs),
-                       rs_part_max_length_rs
-               );
-       }
-
-       v3f vec;
-       // Jump downward sometimes
-       if (!large_cave && ps->range(0, 12) == 0) {
-               vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2;
-               vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
-               vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2;
-       } else {
-               vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2;
-               vec.Y = (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2;
-               vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2;
-       }
-
-       // Do not make caves that are above ground.
-       // It is only necessary to check the startpoint and endpoint.
-       v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
-       v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
-       if (isPosAboveSurface(p1) || isPosAboveSurface(p2))
-               return;
-
-       vec += main_direction;
-
-       v3f rp = orp + vec;
-       if (rp.X < 0)
-               rp.X = 0;
-       else if (rp.X >= ar.X)
-               rp.X = ar.X - 1;
-
-       if (rp.Y < route_y_min)
-               rp.Y = route_y_min;
-       else if (rp.Y >= route_y_max)
-               rp.Y = route_y_max - 1;
-
-       if (rp.Z < 0)
-               rp.Z = 0;
-       else if (rp.Z >= ar.Z)
-               rp.Z = ar.Z - 1;
-
-       vec = rp - orp;
-
-       float veclen = vec.getLength();
-       if (veclen < 0.05f)
-               veclen = 1.0f;
-
-       // Every second section is rough
-       bool randomize_xz = (ps->range(1, 2) == 1);
-
-       // Carve routes
-       for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
-               carveRoute(vec, f, randomize_xz);
-
-       orp = rp;
-}
-
-
-void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz)
-{
-       MapNode airnode(CONTENT_AIR);
-       MapNode waternode(c_water_source);
-       MapNode lavanode(c_lava_source);
-
-       v3s16 startp(orp.X, orp.Y, orp.Z);
-       startp += of;
-
-       float nval = NoisePerlin3D(np_caveliquids, startp.X,
-               startp.Y, startp.Z, seed);
-       MapNode liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ?
-               lavanode : waternode;
-
-       v3f fp = orp + vec * f;
-       fp.X += 0.1f * ps->range(-10, 10);
-       fp.Z += 0.1f * ps->range(-10, 10);
-       v3s16 cp(fp.X, fp.Y, fp.Z);
-
-       s16 d0 = -rs / 2;
-       s16 d1 = d0 + rs;
-       if (randomize_xz) {
-               d0 += ps->range(-1, 1);
-               d1 += ps->range(-1, 1);
-       }
-
-       bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2;
-
-       for (s16 z0 = d0; z0 <= d1; z0++) {
-               s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
-               for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
-                       s16 maxabsxz = MYMAX(abs(x0), abs(z0));
-
-                       s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
-
-                       for (s16 y0 = -si2; y0 <= si2; y0++) {
-                               // Make better floors in small caves
-                               if (flat_cave_floor && y0 <= -rs / 2 && rs <= 7)
-                                       continue;
-
-                               if (large_cave_is_flat) {
-                                       // Make large caves not so tall
-                                       if (rs > 7 && abs(y0) >= rs / 3)
-                                               continue;
-                               }
-
-                               v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
-                               p += of;
-
-                               if (!vm->m_area.contains(p))
-                                       continue;
-
-                               u32 i = vm->m_area.index(p);
-                               content_t c = vm->m_data[i].getContent();
-                               if (!ndef->get(c).is_ground_content)
-                                       continue;
-
-                               if (large_cave) {
-                                       int full_ymin = node_min.Y - MAP_BLOCKSIZE;
-                                       int full_ymax = node_max.Y + MAP_BLOCKSIZE;
-
-                                       if (flooded && full_ymin < water_level && full_ymax > water_level)
-                                               vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
-                                       else if (flooded && full_ymax < water_level)
-                                               vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode;
-                                       else
-                                               vm->m_data[i] = airnode;
-                               } else {
-                                       if (c == CONTENT_IGNORE)
-                                               continue;
-
-                                       vm->m_data[i] = airnode;
-                                       vm->m_flags[i] |= VMANIP_FLAG_CAVE;
-                               }
-                       }
-               }
-       }
-}
-
-
-inline bool CavesRandomWalk::isPosAboveSurface(v3s16 p)
-{
-       if (heightmap != NULL &&
-                       p.Z >= node_min.Z && p.Z <= node_max.Z &&
-                       p.X >= node_min.X && p.X <= node_max.X) {
-               u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
-               if (heightmap[index] < p.Y)
-                       return true;
-       } else if (p.Y > water_level) {
-               return true;
-       }
-
-       return false;
-}
-
-
-////
-//// CavesV6
-////
-
-CavesV6::CavesV6(INodeDefManager *ndef, GenerateNotifier *gennotify,
-       int water_level, content_t water_source, content_t lava_source)
-{
-       assert(ndef);
-
-       this->ndef        = ndef;
-       this->gennotify   = gennotify;
-       this->water_level = water_level;
-
-       c_water_source = water_source;
-       if (c_water_source == CONTENT_IGNORE)
-               c_water_source = ndef->getId("mapgen_water_source");
-       if (c_water_source == CONTENT_IGNORE)
-               c_water_source = CONTENT_AIR;
-
-       c_lava_source = lava_source;
-       if (c_lava_source == CONTENT_IGNORE)
-               c_lava_source = ndef->getId("mapgen_lava_source");
-       if (c_lava_source == CONTENT_IGNORE)
-               c_lava_source = CONTENT_AIR;
-}
-
-
-void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
-       PseudoRandom *ps, PseudoRandom *ps2,
-       bool is_large_cave, int max_stone_height, s16 *heightmap)
-{
-       assert(vm);
-       assert(ps);
-       assert(ps2);
-
-       this->vm         = vm;
-       this->ps         = ps;
-       this->ps2        = ps2;
-       this->node_min   = nmin;
-       this->node_max   = nmax;
-       this->heightmap  = heightmap;
-       this->large_cave = is_large_cave;
-
-       this->ystride = nmax.X - nmin.X + 1;
-
-       // Set initial parameters from randomness
-       min_tunnel_diameter = 2;
-       max_tunnel_diameter = ps->range(2, 6);
-       int dswitchint      = ps->range(1, 14);
-       if (large_cave) {
-               part_max_length_rs  = ps->range(2, 4);
-               tunnel_routepoints  = ps->range(5, ps->range(15, 30));
-               min_tunnel_diameter = 5;
-               max_tunnel_diameter = ps->range(7, ps->range(8, 24));
-       } else {
-               part_max_length_rs = ps->range(2, 9);
-               tunnel_routepoints = ps->range(10, ps->range(15, 30));
-       }
-       large_cave_is_flat = (ps->range(0, 1) == 0);
-
-       main_direction = v3f(0, 0, 0);
-
-       // Allowed route area size in nodes
-       ar = node_max - node_min + v3s16(1, 1, 1);
-       // Area starting point in nodes
-       of = node_min;
-
-       // Allow a bit more
-       //(this should be more than the maximum radius of the tunnel)
-       const s16 max_spread_amount = MAP_BLOCKSIZE;
-       const s16 insure = 10;
-       s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1);
-       ar += v3s16(1, 0, 1) * more * 2;
-       of -= v3s16(1, 0, 1) * more;
-
-       route_y_min = 0;
-       // Allow half a diameter + 7 over stone surface
-       route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7;
-
-       // Limit maximum to area
-       route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
-
-       if (large_cave) {
-               s16 minpos = 0;
-               if (node_min.Y < water_level && node_max.Y > water_level) {
-                       minpos = water_level - max_tunnel_diameter / 3 - of.Y;
-                       route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
-               }
-               route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
-               route_y_min = rangelim(route_y_min, 0, route_y_max);
-       }
-
-       s16 route_start_y_min = route_y_min;
-       s16 route_start_y_max = route_y_max;
-
-       route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
-       route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
-
-       // Randomize starting position
-       orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
-       orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
-       orp.X = (float)(ps->next() % ar.X) + 0.5f;
-
-       // Add generation notify begin event
-       if (gennotify != NULL) {
-               v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
-               GenNotifyType notifytype = large_cave ?
-                       GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
-               gennotify->addEvent(notifytype, abs_pos);
-       }
-
-       // Generate some tunnel starting from orp
-       for (u16 j = 0; j < tunnel_routepoints; j++)
-               makeTunnel(j % dswitchint == 0);
-
-       // Add generation notify end event
-       if (gennotify != NULL) {
-               v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
-               GenNotifyType notifytype = large_cave ?
-                       GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
-               gennotify->addEvent(notifytype, abs_pos);
-       }
-}
-
-
-void CavesV6::makeTunnel(bool dirswitch)
-{
-       if (dirswitch && !large_cave) {
-               main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
-               main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
-               main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
-
-               main_direction *= (float)ps->range(0, 10) / 10;
-       }
-
-       // Randomize size
-       s16 min_d = min_tunnel_diameter;
-       s16 max_d = max_tunnel_diameter;
-       rs = ps->range(min_d, max_d);
-       s16 rs_part_max_length_rs = rs * part_max_length_rs;
-
-       v3s16 maxlen;
-       if (large_cave) {
-               maxlen = v3s16(
-                       rs_part_max_length_rs,
-                       rs_part_max_length_rs / 2,
-                       rs_part_max_length_rs
-               );
-       } else {
-               maxlen = v3s16(
-                       rs_part_max_length_rs,
-                       ps->range(1, rs_part_max_length_rs),
-                       rs_part_max_length_rs
-               );
-       }
-
-       v3f vec;
-       vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
-       vec.Y = (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2;
-       vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
-
-       // Jump downward sometimes
-       if (!large_cave && ps->range(0, 12) == 0) {
-               vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
-               vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
-               vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
-       }
-
-       // Do not make caves that are entirely above ground, to fix shadow bugs
-       // caused by overgenerated large caves.
-       // It is only necessary to check the startpoint and endpoint.
-       v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
-       v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
-
-       // If startpoint and endpoint are above ground, disable placement of nodes
-       // in carveRoute while still running all PseudoRandom calls to ensure caves
-       // are consistent with existing worlds.
-       bool tunnel_above_ground =
-               p1.Y > getSurfaceFromHeightmap(p1) &&
-               p2.Y > getSurfaceFromHeightmap(p2);
-
-       vec += main_direction;
-
-       v3f rp = orp + vec;
-       if (rp.X < 0)
-               rp.X = 0;
-       else if (rp.X >= ar.X)
-               rp.X = ar.X - 1;
-
-       if (rp.Y < route_y_min)
-               rp.Y = route_y_min;
-       else if (rp.Y >= route_y_max)
-               rp.Y = route_y_max - 1;
-
-       if (rp.Z < 0)
-               rp.Z = 0;
-       else if (rp.Z >= ar.Z)
-               rp.Z = ar.Z - 1;
-
-       vec = rp - orp;
-
-       float veclen = vec.getLength();
-       // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE
-       if (veclen < 0.05f)
-               veclen = 1.0f;
-
-       // Every second section is rough
-       bool randomize_xz = (ps2->range(1, 2) == 1);
-
-       // Carve routes
-       for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
-               carveRoute(vec, f, randomize_xz, tunnel_above_ground);
-
-       orp = rp;
-}
-
-
-void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz,
-       bool tunnel_above_ground)
-{
-       MapNode airnode(CONTENT_AIR);
-       MapNode waternode(c_water_source);
-       MapNode lavanode(c_lava_source);
-
-       v3s16 startp(orp.X, orp.Y, orp.Z);
-       startp += of;
-
-       v3f fp = orp + vec * f;
-       fp.X += 0.1f * ps->range(-10, 10);
-       fp.Z += 0.1f * ps->range(-10, 10);
-       v3s16 cp(fp.X, fp.Y, fp.Z);
-
-       s16 d0 = -rs / 2;
-       s16 d1 = d0 + rs;
-       if (randomize_xz) {
-               d0 += ps->range(-1, 1);
-               d1 += ps->range(-1, 1);
-       }
-
-       for (s16 z0 = d0; z0 <= d1; z0++) {
-               s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
-               for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
-                       if (tunnel_above_ground)
-                               continue;
-
-                       s16 maxabsxz = MYMAX(abs(x0), abs(z0));
-                       s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
-                       for (s16 y0 = -si2; y0 <= si2; y0++) {
-                               if (large_cave_is_flat) {
-                                       // Make large caves not so tall
-                                       if (rs > 7 && abs(y0) >= rs / 3)
-                                               continue;
-                               }
-
-                               v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
-                               p += of;
-
-                               if (!vm->m_area.contains(p))
-                                       continue;
-
-                               u32 i = vm->m_area.index(p);
-                               content_t c = vm->m_data[i].getContent();
-                               if (!ndef->get(c).is_ground_content)
-                                       continue;
-
-                               if (large_cave) {
-                                       int full_ymin = node_min.Y - MAP_BLOCKSIZE;
-                                       int full_ymax = node_max.Y + MAP_BLOCKSIZE;
-
-                                       if (full_ymin < water_level && full_ymax > water_level) {
-                                               vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
-                                       } else if (full_ymax < water_level) {
-                                               vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode;
-                                       } else {
-                                               vm->m_data[i] = airnode;
-                                       }
-                               } else {
-                                       if (c == CONTENT_IGNORE || c == CONTENT_AIR)
-                                               continue;
-
-                                       vm->m_data[i] = airnode;
-                                       vm->m_flags[i] |= VMANIP_FLAG_CAVE;
-                               }
-                       }
-               }
-       }
-}
-
-
-inline s16 CavesV6::getSurfaceFromHeightmap(v3s16 p)
-{
-       if (heightmap != NULL &&
-                       p.Z >= node_min.Z && p.Z <= node_max.Z &&
-                       p.X >= node_min.X && p.X <= node_max.X) {
-               u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
-               return heightmap[index];
-       }
-
-       return water_level;
-
-}
diff --git a/src/cavegen.h b/src/cavegen.h
deleted file mode 100644 (file)
index ce146e0..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1
-
-class GenerateNotifier;
-
-/*
-       CavesNoiseIntersection is a cave digging algorithm that carves smooth,
-       web-like, continuous tunnels at points where the density of the intersection
-       between two separate 3d noises is above a certain value.  This value,
-       cave_width, can be modified to set the effective width of these tunnels.
-
-       This algorithm is relatively heavyweight, taking ~80ms to generate an
-       80x80x80 chunk of map on a modern processor.  Use sparingly!
-
-       TODO(hmmmm): Remove dependency on biomes
-       TODO(hmmmm): Find alternative to overgeneration as solution for sunlight issue
-*/
-class CavesNoiseIntersection
-{
-public:
-       CavesNoiseIntersection(INodeDefManager *nodedef, BiomeManager *biomemgr,
-                       v3s16 chunksize, NoiseParams *np_cave1, NoiseParams *np_cave2,
-                       s32 seed, float cave_width);
-       ~CavesNoiseIntersection();
-
-       void generateCaves(MMVManip *vm, v3s16 nmin, v3s16 nmax, u8 *biomemap);
-
-private:
-       INodeDefManager *m_ndef;
-       BiomeManager *m_bmgr;
-
-       // configurable parameters
-       v3s16 m_csize;
-       float m_cave_width;
-
-       // intermediate state variables
-       u16 m_ystride;
-       u16 m_zstride_1d;
-
-       Noise *noise_cave1;
-       Noise *noise_cave2;
-};
-
-/*
-       CavernsNoise is a cave digging algorithm
-*/
-class CavernsNoise
-{
-public:
-       CavernsNoise(INodeDefManager *nodedef, v3s16 chunksize, NoiseParams *np_cavern,
-                       s32 seed, float cavern_limit, float cavern_taper,
-                       float cavern_threshold);
-       ~CavernsNoise();
-
-       bool generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax);
-
-private:
-       INodeDefManager *m_ndef;
-
-       // configurable parameters
-       v3s16 m_csize;
-       float m_cavern_limit;
-       float m_cavern_taper;
-       float m_cavern_threshold;
-
-       // intermediate state variables
-       u16 m_ystride;
-       u16 m_zstride_1d;
-
-       Noise *noise_cavern;
-
-       content_t c_water_source;
-       content_t c_lava_source;
-};
-
-/*
-       CavesRandomWalk is an implementation of a cave-digging algorithm that
-       operates on the principle of a "random walk" to approximate the stochiastic
-       activity of cavern development.
-
-       In summary, this algorithm works by carving a randomly sized tunnel in a
-       random direction a random amount of times, randomly varying in width.
-       All randomness here is uniformly distributed; alternative distributions have
-       not yet been implemented.
-
-       This algorithm is very fast, executing in less than 1ms on average for an
-       80x80x80 chunk of map on a modern processor.
-*/
-class CavesRandomWalk
-{
-public:
-       MMVManip *vm;
-       INodeDefManager *ndef;
-       GenerateNotifier *gennotify;
-       s16 *heightmap;
-
-       // configurable parameters
-       s32 seed;
-       int water_level;
-       int lava_depth;
-       NoiseParams *np_caveliquids;
-
-       // intermediate state variables
-       u16 ystride;
-
-       s16 min_tunnel_diameter;
-       s16 max_tunnel_diameter;
-       u16 tunnel_routepoints;
-       int part_max_length_rs;
-
-       bool large_cave;
-       bool large_cave_is_flat;
-       bool flooded;
-
-       s16 max_stone_y;
-       v3s16 node_min;
-       v3s16 node_max;
-
-       v3f orp;  // starting point, relative to caved space
-       v3s16 of; // absolute coordinates of caved space
-       v3s16 ar; // allowed route area
-       s16 rs;   // tunnel radius size
-       v3f main_direction;
-
-       s16 route_y_min;
-       s16 route_y_max;
-
-       PseudoRandom *ps;
-
-       content_t c_water_source;
-       content_t c_lava_source;
-
-       // ndef is a mandatory parameter.
-       // If gennotify is NULL, generation events are not logged.
-       CavesRandomWalk(INodeDefManager *ndef, GenerateNotifier *gennotify = NULL,
-                       s32 seed = 0, int water_level = 1,
-                       content_t water_source = CONTENT_IGNORE,
-                       content_t lava_source = CONTENT_IGNORE, int lava_depth = -256);
-
-       // vm and ps are mandatory parameters.
-       // If heightmap is NULL, the surface level at all points is assumed to
-       // be water_level.
-       void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps,
-                       bool is_large_cave, int max_stone_height, s16 *heightmap);
-
-private:
-       void makeTunnel(bool dirswitch);
-       void carveRoute(v3f vec, float f, bool randomize_xz);
-
-       inline bool isPosAboveSurface(v3s16 p);
-};
-
-/*
-       CavesV6 is the original version of caves used with Mapgen V6.
-
-       Though it uses the same fundamental algorithm as CavesRandomWalk, it is made
-       separate to preserve the exact sequence of PseudoRandom calls - any change
-       to this ordering results in the output being radically different.
-       Because caves in Mapgen V6 are responsible for a large portion of the basic
-       terrain shape, modifying this will break our contract of reverse
-       compatibility for a 'stable' mapgen such as V6.
-
-       tl;dr,
-       *** DO NOT TOUCH THIS CLASS UNLESS YOU KNOW WHAT YOU ARE DOING ***
-*/
-class CavesV6
-{
-public:
-       MMVManip *vm;
-       INodeDefManager *ndef;
-       GenerateNotifier *gennotify;
-       PseudoRandom *ps;
-       PseudoRandom *ps2;
-
-       // configurable parameters
-       s16 *heightmap;
-       content_t c_water_source;
-       content_t c_lava_source;
-       int water_level;
-
-       // intermediate state variables
-       u16 ystride;
-
-       s16 min_tunnel_diameter;
-       s16 max_tunnel_diameter;
-       u16 tunnel_routepoints;
-       int part_max_length_rs;
-
-       bool large_cave;
-       bool large_cave_is_flat;
-
-       v3s16 node_min;
-       v3s16 node_max;
-
-       v3f orp;  // starting point, relative to caved space
-       v3s16 of; // absolute coordinates of caved space
-       v3s16 ar; // allowed route area
-       s16 rs;   // tunnel radius size
-       v3f main_direction;
-
-       s16 route_y_min;
-       s16 route_y_max;
-
-       // ndef is a mandatory parameter.
-       // If gennotify is NULL, generation events are not logged.
-       CavesV6(INodeDefManager *ndef, GenerateNotifier *gennotify = NULL,
-                       int water_level = 1, content_t water_source = CONTENT_IGNORE,
-                       content_t lava_source = CONTENT_IGNORE);
-
-       // vm, ps, and ps2 are mandatory parameters.
-       // If heightmap is NULL, the surface level at all points is assumed to
-       // be water_level.
-       void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps,
-                       PseudoRandom *ps2, bool is_large_cave, int max_stone_height,
-                       s16 *heightmap = NULL);
-
-private:
-       void makeTunnel(bool dirswitch);
-       void carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground);
-
-       inline s16 getSurfaceFromHeightmap(v3s16 p);
-};
index 2538f52aa6b8055a04415fddb7e183b2719ce11a..bca9f41d04b827da051fa3cf12485168eb817b3f 100644 (file)
@@ -47,7 +47,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "clientmap.h"
 #include "clientmedia.h"
 #include "version.h"
-#include "database-sqlite3.h"
+#include "database/database-sqlite3.h"
 #include "serialization.h"
 #include "guiscalingfilter.h"
 #include "script/scripting_client.h"
index dbaba80408321b590f3f94df91a6c540bee8af2c..741a90d9f4db9f6486ef7e4c11b6f18cc89a68b0 100644 (file)
@@ -17,18 +17,18 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
-#include "mainmenumanager.h"
+#include "gui/mainmenumanager.h"
 #include "clouds.h"
 #include "server.h"
 #include "filesys.h"
-#include "guiMainMenu.h"
+#include "gui/guiMainMenu.h"
 #include "game.h"
 #include "player.h"
 #include "chat.h"
 #include "gettext.h"
 #include "profiler.h"
 #include "serverlist.h"
-#include "guiEngine.h"
+#include "gui/guiEngine.h"
 #include "fontengine.h"
 #include "clientlauncher.h"
 #include "version.h"
index b176f3ad7cf8dbe06fa7b6647c5091abb188c684..48b94ae955df649916d00dadfe6c99ebb69dd672 100644 (file)
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "util/numeric.h"
 #include "inputhandler.h"
-#include "mainmenumanager.h"
+#include "gui/mainmenumanager.h"
 
 bool MyEventReceiver::OnEvent(const SEvent &event)
 {
index 249336947a1399390ae986f4ca7f7c34f22f491b..165c759904ca9986e7209f471f222b3d8673a8d1 100644 (file)
@@ -26,7 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "renderingengine.h"
 
 #ifdef HAVE_TOUCHSCREENGUI
-#include "touchscreengui.h"
+#include "gui/touchscreengui.h"
 #endif
 
 class KeyList : private std::list<KeyPress>
index 95bd77bc4a928131f172c3db4b4ebef52defc62f..c29e8b639858e81f3d1ba2bcf1e17c648f70a201 100644 (file)
@@ -23,7 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "gettime.h"
 #include "porting.h"
-#include "../util/string.h"
+#include "util/string.h"
 
 bool JoystickButtonCmb::isTriggered(const irr::SEvent::SJoystickEvent &ev) const
 {
diff --git a/src/database-dummy.cpp b/src/database-dummy.cpp
deleted file mode 100644 (file)
index a3d8cd5..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-/*
-Dummy database class
-*/
-
-#include "database-dummy.h"
-
-
-bool Database_Dummy::saveBlock(const v3s16 &pos, const std::string &data)
-{
-       m_database[getBlockAsInteger(pos)] = data;
-       return true;
-}
-
-void Database_Dummy::loadBlock(const v3s16 &pos, std::string *block)
-{
-       s64 i = getBlockAsInteger(pos);
-       auto it = m_database.find(i);
-       if (it == m_database.end()) {
-               *block = "";
-               return;
-       }
-
-       *block = it->second;
-}
-
-bool Database_Dummy::deleteBlock(const v3s16 &pos)
-{
-       m_database.erase(getBlockAsInteger(pos));
-       return true;
-}
-
-void Database_Dummy::listAllLoadableBlocks(std::vector<v3s16> &dst)
-{
-       dst.reserve(m_database.size());
-       for (std::map<s64, std::string>::const_iterator x = m_database.begin();
-                       x != m_database.end(); ++x) {
-               dst.push_back(getIntegerAsBlock(x->first));
-       }
-}
-
diff --git a/src/database-dummy.h b/src/database-dummy.h
deleted file mode 100644 (file)
index 2d87d58..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include <map>
-#include <string>
-#include "database.h"
-#include "irrlichttypes.h"
-
-class Database_Dummy : public MapDatabase, public PlayerDatabase
-{
-public:
-       bool saveBlock(const v3s16 &pos, const std::string &data);
-       void loadBlock(const v3s16 &pos, std::string *block);
-       bool deleteBlock(const v3s16 &pos);
-       void listAllLoadableBlocks(std::vector<v3s16> &dst);
-
-       void savePlayer(RemotePlayer *player) {}
-       bool loadPlayer(RemotePlayer *player, PlayerSAO *sao) { return true; }
-       bool removePlayer(const std::string &name) { return true; }
-       void listPlayers(std::vector<std::string> &res) {}
-
-       void beginSave() {}
-       void endSave() {}
-
-private:
-       std::map<s64, std::string> m_database;
-};
diff --git a/src/database-files.cpp b/src/database-files.cpp
deleted file mode 100644 (file)
index 70de8c8..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
-Minetest
-Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 <cassert>
-#include <json/json.h>
-#include "database-files.h"
-#include "content_sao.h"
-#include "remoteplayer.h"
-#include "settings.h"
-#include "porting.h"
-#include "filesys.h"
-
-// !!! WARNING !!!
-// This backend is intended to be used on Minetest 0.4.16 only for the transition backend
-// for player files
-
-void PlayerDatabaseFiles::serialize(std::ostringstream &os, RemotePlayer *player)
-{
-       // Utilize a Settings object for storing values
-       Settings args;
-       args.setS32("version", 1);
-       args.set("name", player->getName());
-
-       sanity_check(player->getPlayerSAO());
-       args.setS32("hp", player->getPlayerSAO()->getHP());
-       args.setV3F("position", player->getPlayerSAO()->getBasePosition());
-       args.setFloat("pitch", player->getPlayerSAO()->getPitch());
-       args.setFloat("yaw", player->getPlayerSAO()->getYaw());
-       args.setS32("breath", player->getPlayerSAO()->getBreath());
-
-       std::string extended_attrs;
-       player->serializeExtraAttributes(extended_attrs);
-       args.set("extended_attributes", extended_attrs);
-
-       args.writeLines(os);
-
-       os << "PlayerArgsEnd\n";
-
-       player->inventory.serialize(os);
-}
-
-void PlayerDatabaseFiles::savePlayer(RemotePlayer *player)
-{
-       std::string savedir = m_savedir + DIR_DELIM;
-       std::string path = savedir + player->getName();
-       bool path_found = false;
-       RemotePlayer testplayer("", NULL);
-
-       for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES && !path_found; i++) {
-               if (!fs::PathExists(path)) {
-                       path_found = true;
-                       continue;
-               }
-
-               // Open and deserialize file to check player name
-               std::ifstream is(path.c_str(), std::ios_base::binary);
-               if (!is.good()) {
-                       errorstream << "Failed to open " << path << std::endl;
-                       return;
-               }
-
-               testplayer.deSerialize(is, path, NULL);
-               is.close();
-               if (strcmp(testplayer.getName(), player->getName()) == 0) {
-                       path_found = true;
-                       continue;
-               }
-
-               path = savedir + player->getName() + itos(i);
-       }
-
-       if (!path_found) {
-               errorstream << "Didn't find free file for player " << player->getName()
-                               << std::endl;
-               return;
-       }
-
-       // Open and serialize file
-       std::ostringstream ss(std::ios_base::binary);
-       serialize(ss, player);
-       if (!fs::safeWriteToFile(path, ss.str())) {
-               infostream << "Failed to write " << path << std::endl;
-       }
-       player->setModified(false);
-}
-
-bool PlayerDatabaseFiles::removePlayer(const std::string &name)
-{
-       std::string players_path = m_savedir + DIR_DELIM;
-       std::string path = players_path + name;
-
-       RemotePlayer temp_player("", NULL);
-       for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
-               // Open file and deserialize
-               std::ifstream is(path.c_str(), std::ios_base::binary);
-               if (!is.good())
-                       continue;
-
-               temp_player.deSerialize(is, path, NULL);
-               is.close();
-
-               if (temp_player.getName() == name) {
-                       fs::DeleteSingleFileOrEmptyDirectory(path);
-                       return true;
-               }
-
-               path = players_path + name + itos(i);
-       }
-
-       return false;
-}
-
-bool PlayerDatabaseFiles::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
-{
-       std::string players_path = m_savedir + DIR_DELIM;
-       std::string path = players_path + player->getName();
-
-       const std::string player_to_load = player->getName();
-       for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
-               // Open file and deserialize
-               std::ifstream is(path.c_str(), std::ios_base::binary);
-               if (!is.good())
-                       continue;
-
-               player->deSerialize(is, path, sao);
-               is.close();
-
-               if (player->getName() == player_to_load)
-                       return true;
-
-               path = players_path + player_to_load + itos(i);
-       }
-
-       infostream << "Player file for player " << player_to_load << " not found" << std::endl;
-       return false;
-}
-
-void PlayerDatabaseFiles::listPlayers(std::vector<std::string> &res)
-{
-       std::vector<fs::DirListNode> files = fs::GetDirListing(m_savedir);
-       // list files into players directory
-       for (std::vector<fs::DirListNode>::const_iterator it = files.begin(); it !=
-               files.end(); ++it) {
-               // Ignore directories
-               if (it->dir)
-                       continue;
-
-               const std::string &filename = it->name;
-               std::string full_path = m_savedir + DIR_DELIM + filename;
-               std::ifstream is(full_path.c_str(), std::ios_base::binary);
-               if (!is.good())
-                       continue;
-
-               RemotePlayer player(filename.c_str(), NULL);
-               // Null env & dummy peer_id
-               PlayerSAO playerSAO(NULL, &player, 15789, false);
-
-               player.deSerialize(is, "", &playerSAO);
-               is.close();
-
-               res.emplace_back(player.getName());
-       }
-}
diff --git a/src/database-files.h b/src/database-files.h
deleted file mode 100644 (file)
index f0824a3..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-Minetest
-Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-// !!! WARNING !!!
-// This backend is intended to be used on Minetest 0.4.16 only for the transition backend
-// for player files
-
-#include "database.h"
-
-class PlayerDatabaseFiles : public PlayerDatabase
-{
-public:
-       PlayerDatabaseFiles(const std::string &savedir) : m_savedir(savedir) {}
-       virtual ~PlayerDatabaseFiles() = default;
-
-       void savePlayer(RemotePlayer *player);
-       bool loadPlayer(RemotePlayer *player, PlayerSAO *sao);
-       bool removePlayer(const std::string &name);
-       void listPlayers(std::vector<std::string> &res);
-
-private:
-       void serialize(std::ostringstream &os, RemotePlayer *player);
-
-       std::string m_savedir;
-};
diff --git a/src/database-leveldb.cpp b/src/database-leveldb.cpp
deleted file mode 100644 (file)
index 4a4904c..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "config.h"
-
-#if USE_LEVELDB
-
-#include "database-leveldb.h"
-
-#include "log.h"
-#include "filesys.h"
-#include "exceptions.h"
-#include "util/string.h"
-
-#include "leveldb/db.h"
-
-
-#define ENSURE_STATUS_OK(s) \
-       if (!(s).ok()) { \
-               throw DatabaseException(std::string("LevelDB error: ") + \
-                               (s).ToString()); \
-       }
-
-
-Database_LevelDB::Database_LevelDB(const std::string &savedir)
-{
-       leveldb::Options options;
-       options.create_if_missing = true;
-       leveldb::Status status = leveldb::DB::Open(options,
-               savedir + DIR_DELIM + "map.db", &m_database);
-       ENSURE_STATUS_OK(status);
-}
-
-Database_LevelDB::~Database_LevelDB()
-{
-       delete m_database;
-}
-
-bool Database_LevelDB::saveBlock(const v3s16 &pos, const std::string &data)
-{
-       leveldb::Status status = m_database->Put(leveldb::WriteOptions(),
-                       i64tos(getBlockAsInteger(pos)), data);
-       if (!status.ok()) {
-               warningstream << "saveBlock: LevelDB error saving block "
-                       << PP(pos) << ": " << status.ToString() << std::endl;
-               return false;
-       }
-
-       return true;
-}
-
-void Database_LevelDB::loadBlock(const v3s16 &pos, std::string *block)
-{
-       std::string datastr;
-       leveldb::Status status = m_database->Get(leveldb::ReadOptions(),
-               i64tos(getBlockAsInteger(pos)), &datastr);
-
-       *block = (status.ok()) ? datastr : "";
-}
-
-bool Database_LevelDB::deleteBlock(const v3s16 &pos)
-{
-       leveldb::Status status = m_database->Delete(leveldb::WriteOptions(),
-                       i64tos(getBlockAsInteger(pos)));
-       if (!status.ok()) {
-               warningstream << "deleteBlock: LevelDB error deleting block "
-                       << PP(pos) << ": " << status.ToString() << std::endl;
-               return false;
-       }
-
-       return true;
-}
-
-void Database_LevelDB::listAllLoadableBlocks(std::vector<v3s16> &dst)
-{
-       leveldb::Iterator* it = m_database->NewIterator(leveldb::ReadOptions());
-       for (it->SeekToFirst(); it->Valid(); it->Next()) {
-               dst.push_back(getIntegerAsBlock(stoi64(it->key().ToString())));
-       }
-       ENSURE_STATUS_OK(it->status());  // Check for any errors found during the scan
-       delete it;
-}
-
-#endif // USE_LEVELDB
-
diff --git a/src/database-leveldb.h b/src/database-leveldb.h
deleted file mode 100644 (file)
index d30f9f8..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "config.h"
-
-#if USE_LEVELDB
-
-#include <string>
-#include "database.h"
-#include "leveldb/db.h"
-
-class Database_LevelDB : public MapDatabase
-{
-public:
-       Database_LevelDB(const std::string &savedir);
-       ~Database_LevelDB();
-
-       bool saveBlock(const v3s16 &pos, const std::string &data);
-       void loadBlock(const v3s16 &pos, std::string *block);
-       bool deleteBlock(const v3s16 &pos);
-       void listAllLoadableBlocks(std::vector<v3s16> &dst);
-
-       void beginSave() {}
-       void endSave() {}
-
-private:
-       leveldb::DB *m_database;
-};
-
-#endif // USE_LEVELDB
diff --git a/src/database-postgresql.cpp b/src/database-postgresql.cpp
deleted file mode 100644 (file)
index 7465113..0000000
+++ /dev/null
@@ -1,631 +0,0 @@
-/*
-Copyright (C) 2016 Loic Blot <loic.blot@unix-experience.fr>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "config.h"
-
-#if USE_POSTGRESQL
-
-#include "database-postgresql.h"
-
-#ifdef _WIN32
-        // Without this some of the network functions are not found on mingw
-        #ifndef _WIN32_WINNT
-                #define _WIN32_WINNT 0x0501
-        #endif
-        #include <windows.h>
-        #include <winsock2.h>
-#else
-#include <netinet/in.h>
-#endif
-
-#include "debug.h"
-#include "exceptions.h"
-#include "settings.h"
-#include "content_sao.h"
-#include "remoteplayer.h"
-
-Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) :
-       m_connect_string(connect_string)
-{
-       if (m_connect_string.empty()) {
-               throw SettingNotFoundException(
-                       "Set pgsql_connection string in world.mt to "
-                       "use the postgresql backend\n"
-                       "Notes:\n"
-                       "pgsql_connection has the following form: \n"
-                       "\tpgsql_connection = host=127.0.0.1 port=5432 user=mt_user "
-                       "password=mt_password dbname=minetest_world\n"
-                       "mt_user should have CREATE TABLE, INSERT, SELECT, UPDATE and "
-                       "DELETE rights on the database.\n"
-                       "Don't create mt_user as a SUPERUSER!");
-       }
-}
-
-Database_PostgreSQL::~Database_PostgreSQL()
-{
-       PQfinish(m_conn);
-}
-
-void Database_PostgreSQL::connectToDatabase()
-{
-       m_conn = PQconnectdb(m_connect_string.c_str());
-
-       if (PQstatus(m_conn) != CONNECTION_OK) {
-               throw DatabaseException(std::string(
-                       "PostgreSQL database error: ") +
-                       PQerrorMessage(m_conn));
-       }
-
-       m_pgversion = PQserverVersion(m_conn);
-
-       /*
-       * We are using UPSERT feature from PostgreSQL 9.5
-       * to have the better performance where possible.
-       */
-       if (m_pgversion < 90500) {
-               warningstream << "Your PostgreSQL server lacks UPSERT "
-                       << "support. Use version 9.5 or better if possible."
-                       << std::endl;
-       }
-
-       infostream << "PostgreSQL Database: Version " << m_pgversion
-                       << " Connection made." << std::endl;
-
-       createDatabase();
-       initStatements();
-}
-
-void Database_PostgreSQL::verifyDatabase()
-{
-       if (PQstatus(m_conn) == CONNECTION_OK)
-               return;
-
-       PQreset(m_conn);
-       ping();
-}
-
-void Database_PostgreSQL::ping()
-{
-       if (PQping(m_connect_string.c_str()) != PQPING_OK) {
-               throw DatabaseException(std::string(
-                       "PostgreSQL database error: ") +
-                       PQerrorMessage(m_conn));
-       }
-}
-
-bool Database_PostgreSQL::initialized() const
-{
-       return (PQstatus(m_conn) == CONNECTION_OK);
-}
-
-PGresult *Database_PostgreSQL::checkResults(PGresult *result, bool clear)
-{
-       ExecStatusType statusType = PQresultStatus(result);
-
-       switch (statusType) {
-       case PGRES_COMMAND_OK:
-       case PGRES_TUPLES_OK:
-               break;
-       case PGRES_FATAL_ERROR:
-       default:
-               throw DatabaseException(
-                       std::string("PostgreSQL database error: ") +
-                       PQresultErrorMessage(result));
-       }
-
-       if (clear)
-               PQclear(result);
-
-       return result;
-}
-
-void Database_PostgreSQL::createTableIfNotExists(const std::string &table_name,
-               const std::string &definition)
-{
-       std::string sql_check_table = "SELECT relname FROM pg_class WHERE relname='" +
-               table_name + "';";
-       PGresult *result = checkResults(PQexec(m_conn, sql_check_table.c_str()), false);
-
-       // If table doesn't exist, create it
-       if (!PQntuples(result)) {
-               checkResults(PQexec(m_conn, definition.c_str()));
-       }
-
-       PQclear(result);
-}
-
-void Database_PostgreSQL::beginSave()
-{
-       verifyDatabase();
-       checkResults(PQexec(m_conn, "BEGIN;"));
-}
-
-void Database_PostgreSQL::endSave()
-{
-       checkResults(PQexec(m_conn, "COMMIT;"));
-}
-
-MapDatabasePostgreSQL::MapDatabasePostgreSQL(const std::string &connect_string):
-       Database_PostgreSQL(connect_string),
-       MapDatabase()
-{
-       connectToDatabase();
-}
-
-
-void MapDatabasePostgreSQL::createDatabase()
-{
-       createTableIfNotExists("blocks",
-               "CREATE TABLE blocks ("
-                       "posX INT NOT NULL,"
-                       "posY INT NOT NULL,"
-                       "posZ INT NOT NULL,"
-                       "data BYTEA,"
-                       "PRIMARY KEY (posX,posY,posZ)"
-                       ");"
-       );
-
-       infostream << "PostgreSQL: Map Database was initialized." << std::endl;
-}
-
-void MapDatabasePostgreSQL::initStatements()
-{
-       prepareStatement("read_block",
-               "SELECT data FROM blocks "
-                       "WHERE posX = $1::int4 AND posY = $2::int4 AND "
-                       "posZ = $3::int4");
-
-       if (getPGVersion() < 90500) {
-               prepareStatement("write_block_insert",
-                       "INSERT INTO blocks (posX, posY, posZ, data) SELECT "
-                               "$1::int4, $2::int4, $3::int4, $4::bytea "
-                               "WHERE NOT EXISTS (SELECT true FROM blocks "
-                               "WHERE posX = $1::int4 AND posY = $2::int4 AND "
-                               "posZ = $3::int4)");
-
-               prepareStatement("write_block_update",
-                       "UPDATE blocks SET data = $4::bytea "
-                               "WHERE posX = $1::int4 AND posY = $2::int4 AND "
-                               "posZ = $3::int4");
-       } else {
-               prepareStatement("write_block",
-                       "INSERT INTO blocks (posX, posY, posZ, data) VALUES "
-                               "($1::int4, $2::int4, $3::int4, $4::bytea) "
-                               "ON CONFLICT ON CONSTRAINT blocks_pkey DO "
-                               "UPDATE SET data = $4::bytea");
-       }
-
-       prepareStatement("delete_block", "DELETE FROM blocks WHERE "
-               "posX = $1::int4 AND posY = $2::int4 AND posZ = $3::int4");
-
-       prepareStatement("list_all_loadable_blocks",
-               "SELECT posX, posY, posZ FROM blocks");
-}
-
-bool MapDatabasePostgreSQL::saveBlock(const v3s16 &pos, const std::string &data)
-{
-       // Verify if we don't overflow the platform integer with the mapblock size
-       if (data.size() > INT_MAX) {
-               errorstream << "Database_PostgreSQL::saveBlock: Data truncation! "
-                       << "data.size() over 0xFFFFFFFF (== " << data.size()
-                       << ")" << std::endl;
-               return false;
-       }
-
-       verifyDatabase();
-
-       s32 x, y, z;
-       x = htonl(pos.X);
-       y = htonl(pos.Y);
-       z = htonl(pos.Z);
-
-       const void *args[] = { &x, &y, &z, data.c_str() };
-       const int argLen[] = {
-               sizeof(x), sizeof(y), sizeof(z), (int)data.size()
-       };
-       const int argFmt[] = { 1, 1, 1, 1 };
-
-       if (getPGVersion() < 90500) {
-               execPrepared("write_block_update", ARRLEN(args), args, argLen, argFmt);
-               execPrepared("write_block_insert", ARRLEN(args), args, argLen, argFmt);
-       } else {
-               execPrepared("write_block", ARRLEN(args), args, argLen, argFmt);
-       }
-       return true;
-}
-
-void MapDatabasePostgreSQL::loadBlock(const v3s16 &pos, std::string *block)
-{
-       verifyDatabase();
-
-       s32 x, y, z;
-       x = htonl(pos.X);
-       y = htonl(pos.Y);
-       z = htonl(pos.Z);
-
-       const void *args[] = { &x, &y, &z };
-       const int argLen[] = { sizeof(x), sizeof(y), sizeof(z) };
-       const int argFmt[] = { 1, 1, 1 };
-
-       PGresult *results = execPrepared("read_block", ARRLEN(args), args,
-               argLen, argFmt, false);
-
-       *block = "";
-
-       if (PQntuples(results))
-               *block = std::string(PQgetvalue(results, 0, 0), PQgetlength(results, 0, 0));
-
-       PQclear(results);
-}
-
-bool MapDatabasePostgreSQL::deleteBlock(const v3s16 &pos)
-{
-       verifyDatabase();
-
-       s32 x, y, z;
-       x = htonl(pos.X);
-       y = htonl(pos.Y);
-       z = htonl(pos.Z);
-
-       const void *args[] = { &x, &y, &z };
-       const int argLen[] = { sizeof(x), sizeof(y), sizeof(z) };
-       const int argFmt[] = { 1, 1, 1 };
-
-       execPrepared("delete_block", ARRLEN(args), args, argLen, argFmt);
-
-       return true;
-}
-
-void MapDatabasePostgreSQL::listAllLoadableBlocks(std::vector<v3s16> &dst)
-{
-       verifyDatabase();
-
-       PGresult *results = execPrepared("list_all_loadable_blocks", 0,
-               NULL, NULL, NULL, false, false);
-
-       int numrows = PQntuples(results);
-
-       for (int row = 0; row < numrows; ++row)
-               dst.push_back(pg_to_v3s16(results, 0, 0));
-
-       PQclear(results);
-}
-
-/*
- * Player Database
- */
-PlayerDatabasePostgreSQL::PlayerDatabasePostgreSQL(const std::string &connect_string):
-       Database_PostgreSQL(connect_string),
-       PlayerDatabase()
-{
-       connectToDatabase();
-}
-
-
-void PlayerDatabasePostgreSQL::createDatabase()
-{
-       createTableIfNotExists("player",
-               "CREATE TABLE player ("
-                       "name VARCHAR(60) NOT NULL,"
-                       "pitch NUMERIC(15, 7) NOT NULL,"
-                       "yaw NUMERIC(15, 7) NOT NULL,"
-                       "posX NUMERIC(15, 7) NOT NULL,"
-                       "posY NUMERIC(15, 7) NOT NULL,"
-                       "posZ NUMERIC(15, 7) NOT NULL,"
-                       "hp INT NOT NULL,"
-                       "breath INT NOT NULL,"
-                       "creation_date TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),"
-                       "modification_date TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),"
-                       "PRIMARY KEY (name)"
-                       ");"
-       );
-
-       createTableIfNotExists("player_inventories",
-               "CREATE TABLE player_inventories ("
-                       "player VARCHAR(60) NOT NULL,"
-                       "inv_id INT NOT NULL,"
-                       "inv_width INT NOT NULL,"
-                       "inv_name TEXT NOT NULL DEFAULT '',"
-                       "inv_size INT NOT NULL,"
-                       "PRIMARY KEY(player, inv_id),"
-                       "CONSTRAINT player_inventories_fkey FOREIGN KEY (player) REFERENCES "
-                       "player (name) ON DELETE CASCADE"
-                       ");"
-       );
-
-       createTableIfNotExists("player_inventory_items",
-               "CREATE TABLE player_inventory_items ("
-                       "player VARCHAR(60) NOT NULL,"
-                       "inv_id INT NOT NULL,"
-                       "slot_id INT NOT NULL,"
-                       "item TEXT NOT NULL DEFAULT '',"
-                       "PRIMARY KEY(player, inv_id, slot_id),"
-                       "CONSTRAINT player_inventory_items_fkey FOREIGN KEY (player) REFERENCES "
-                       "player (name) ON DELETE CASCADE"
-                       ");"
-       );
-
-       createTableIfNotExists("player_metadata",
-               "CREATE TABLE player_metadata ("
-                       "player VARCHAR(60) NOT NULL,"
-                       "attr VARCHAR(256) NOT NULL,"
-                       "value TEXT,"
-                       "PRIMARY KEY(player, attr),"
-                       "CONSTRAINT player_metadata_fkey FOREIGN KEY (player) REFERENCES "
-                       "player (name) ON DELETE CASCADE"
-                       ");"
-       );
-
-       infostream << "PostgreSQL: Player Database was inited." << std::endl;
-}
-
-void PlayerDatabasePostgreSQL::initStatements()
-{
-       if (getPGVersion() < 90500) {
-               prepareStatement("create_player",
-                       "INSERT INTO player(name, pitch, yaw, posX, posY, posZ, hp, breath) VALUES "
-                               "($1, $2, $3, $4, $5, $6, $7::int, $8::int)");
-
-               prepareStatement("update_player",
-                       "UPDATE SET pitch = $2, yaw = $3, posX = $4, posY = $5, posZ = $6, hp = $7::int, "
-                               "breath = $8::int, modification_date = NOW() WHERE name = $1");
-       } else {
-               prepareStatement("save_player",
-                       "INSERT INTO player(name, pitch, yaw, posX, posY, posZ, hp, breath) VALUES "
-                               "($1, $2, $3, $4, $5, $6, $7::int, $8::int)"
-                               "ON CONFLICT ON CONSTRAINT player_pkey DO UPDATE SET pitch = $2, yaw = $3, "
-                               "posX = $4, posY = $5, posZ = $6, hp = $7::int, breath = $8::int, "
-                               "modification_date = NOW()");
-       }
-
-       prepareStatement("remove_player", "DELETE FROM player WHERE name = $1");
-
-       prepareStatement("load_player_list", "SELECT name FROM player");
-
-       prepareStatement("remove_player_inventories",
-               "DELETE FROM player_inventories WHERE player = $1");
-
-       prepareStatement("remove_player_inventory_items",
-               "DELETE FROM player_inventory_items WHERE player = $1");
-
-       prepareStatement("add_player_inventory",
-               "INSERT INTO player_inventories (player, inv_id, inv_width, inv_name, inv_size) VALUES "
-                       "($1, $2::int, $3::int, $4, $5::int)");
-
-       prepareStatement("add_player_inventory_item",
-               "INSERT INTO player_inventory_items (player, inv_id, slot_id, item) VALUES "
-                       "($1, $2::int, $3::int, $4)");
-
-       prepareStatement("load_player_inventories",
-               "SELECT inv_id, inv_width, inv_name, inv_size FROM player_inventories "
-                       "WHERE player = $1 ORDER BY inv_id");
-
-       prepareStatement("load_player_inventory_items",
-               "SELECT slot_id, item FROM player_inventory_items WHERE "
-                       "player = $1 AND inv_id = $2::int");
-
-       prepareStatement("load_player",
-               "SELECT pitch, yaw, posX, posY, posZ, hp, breath FROM player WHERE name = $1");
-
-       prepareStatement("remove_player_metadata",
-               "DELETE FROM player_metadata WHERE player = $1");
-
-       prepareStatement("save_player_metadata",
-               "INSERT INTO player_metadata (player, attr, value) VALUES ($1, $2, $3)");
-
-       prepareStatement("load_player_metadata",
-               "SELECT attr, value FROM player_metadata WHERE player = $1");
-
-}
-
-bool PlayerDatabasePostgreSQL::playerDataExists(const std::string &playername)
-{
-       verifyDatabase();
-
-       const char *values[] = { playername.c_str() };
-       PGresult *results = execPrepared("load_player", 1, values, false);
-
-       bool res = (PQntuples(results) > 0);
-       PQclear(results);
-       return res;
-}
-
-void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player)
-{
-       PlayerSAO* sao = player->getPlayerSAO();
-       if (!sao)
-               return;
-
-       verifyDatabase();
-
-       v3f pos = sao->getBasePosition();
-       std::string pitch = ftos(sao->getPitch());
-       std::string yaw = ftos(sao->getYaw());
-       std::string posx = ftos(pos.X);
-       std::string posy = ftos(pos.Y);
-       std::string posz = ftos(pos.Z);
-       std::string hp = itos(sao->getHP());
-       std::string breath = itos(sao->getBreath());
-       const char *values[] = {
-               player->getName(),
-               pitch.c_str(),
-               yaw.c_str(),
-               posx.c_str(), posy.c_str(), posz.c_str(),
-               hp.c_str(),
-               breath.c_str()
-       };
-
-       const char* rmvalues[] = { player->getName() };
-       beginSave();
-
-       if (getPGVersion() < 90500) {
-               if (!playerDataExists(player->getName()))
-                       execPrepared("create_player", 8, values, true, false);
-               else
-                       execPrepared("update_player", 8, values, true, false);
-       }
-       else
-               execPrepared("save_player", 8, values, true, false);
-
-       // Write player inventories
-       execPrepared("remove_player_inventories", 1, rmvalues);
-       execPrepared("remove_player_inventory_items", 1, rmvalues);
-
-       std::vector<const InventoryList*> inventory_lists = sao->getInventory()->getLists();
-       for (u16 i = 0; i < inventory_lists.size(); i++) {
-               const InventoryList* list = inventory_lists[i];
-               const std::string &name = list->getName();
-               std::string width = itos(list->getWidth()),
-                       inv_id = itos(i), lsize = itos(list->getSize());
-
-               const char* inv_values[] = {
-                       player->getName(),
-                       inv_id.c_str(),
-                       width.c_str(),
-                       name.c_str(),
-                       lsize.c_str()
-               };
-               execPrepared("add_player_inventory", 5, inv_values);
-
-               for (u32 j = 0; j < list->getSize(); j++) {
-                       std::ostringstream os;
-                       list->getItem(j).serialize(os);
-                       std::string itemStr = os.str(), slotId = itos(j);
-
-                       const char* invitem_values[] = {
-                               player->getName(),
-                               inv_id.c_str(),
-                               slotId.c_str(),
-                               itemStr.c_str()
-                       };
-                       execPrepared("add_player_inventory_item", 4, invitem_values);
-               }
-       }
-
-       execPrepared("remove_player_metadata", 1, rmvalues);
-       const PlayerAttributes &attrs = sao->getExtendedAttributes();
-       for (const auto &attr : attrs) {
-               const char *meta_values[] = {
-                       player->getName(),
-                       attr.first.c_str(),
-                       attr.second.c_str()
-               };
-               execPrepared("save_player_metadata", 3, meta_values);
-       }
-       endSave();
-}
-
-bool PlayerDatabasePostgreSQL::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
-{
-       sanity_check(sao);
-       verifyDatabase();
-
-       const char *values[] = { player->getName() };
-       PGresult *results = execPrepared("load_player", 1, values, false, false);
-
-       // Player not found, return not found
-       if (!PQntuples(results)) {
-               PQclear(results);
-               return false;
-       }
-
-       sao->setPitch(pg_to_float(results, 0, 0));
-       sao->setYaw(pg_to_float(results, 0, 1));
-       sao->setBasePosition(v3f(
-               pg_to_float(results, 0, 2),
-               pg_to_float(results, 0, 3),
-               pg_to_float(results, 0, 4))
-       );
-       sao->setHPRaw((s16) pg_to_int(results, 0, 5));
-       sao->setBreath((u16) pg_to_int(results, 0, 6), false);
-
-       PQclear(results);
-
-       // Load inventory
-       results = execPrepared("load_player_inventories", 1, values, false, false);
-
-       int resultCount = PQntuples(results);
-
-       for (int row = 0; row < resultCount; ++row) {
-               InventoryList* invList = player->inventory.
-                       addList(PQgetvalue(results, row, 2), pg_to_uint(results, row, 3));
-               invList->setWidth(pg_to_uint(results, row, 1));
-
-               u32 invId = pg_to_uint(results, row, 0);
-               std::string invIdStr = itos(invId);
-
-               const char* values2[] = {
-                       player->getName(),
-                       invIdStr.c_str()
-               };
-               PGresult *results2 = execPrepared("load_player_inventory_items", 2,
-                       values2, false, false);
-
-               int resultCount2 = PQntuples(results2);
-               for (int row2 = 0; row2 < resultCount2; row2++) {
-                       const std::string itemStr = PQgetvalue(results2, row2, 1);
-                       if (itemStr.length() > 0) {
-                               ItemStack stack;
-                               stack.deSerialize(itemStr);
-                               invList->changeItem(pg_to_uint(results2, row2, 0), stack);
-                       }
-               }
-               PQclear(results2);
-       }
-
-       PQclear(results);
-
-       results = execPrepared("load_player_metadata", 1, values, false);
-
-       int numrows = PQntuples(results);
-       for (int row = 0; row < numrows; row++) {
-               sao->setExtendedAttribute(PQgetvalue(results, row, 0),PQgetvalue(results, row, 1));
-       }
-
-       PQclear(results);
-
-       return true;
-}
-
-bool PlayerDatabasePostgreSQL::removePlayer(const std::string &name)
-{
-       if (!playerDataExists(name))
-               return false;
-
-       verifyDatabase();
-
-       const char *values[] = { name.c_str() };
-       execPrepared("remove_player", 1, values);
-
-       return true;
-}
-
-void PlayerDatabasePostgreSQL::listPlayers(std::vector<std::string> &res)
-{
-       verifyDatabase();
-
-       PGresult *results = execPrepared("load_player_list", 0, NULL, false);
-
-       int numrows = PQntuples(results);
-       for (int row = 0; row < numrows; row++)
-               res.emplace_back(PQgetvalue(results, row, 0));
-
-       PQclear(results);
-}
-
-#endif // USE_POSTGRESQL
diff --git a/src/database-postgresql.h b/src/database-postgresql.h
deleted file mode 100644 (file)
index db0b505..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include <string>
-#include <libpq-fe.h>
-#include "database.h"
-#include "util/basic_macros.h"
-
-class Settings;
-
-class Database_PostgreSQL: public Database
-{
-public:
-       Database_PostgreSQL(const std::string &connect_string);
-       ~Database_PostgreSQL();
-
-       void beginSave();
-       void endSave();
-
-       bool initialized() const;
-
-
-protected:
-       // Conversion helpers
-       inline int pg_to_int(PGresult *res, int row, int col)
-       {
-               return atoi(PQgetvalue(res, row, col));
-       }
-
-       inline u32 pg_to_uint(PGresult *res, int row, int col)
-       {
-               return (u32) atoi(PQgetvalue(res, row, col));
-       }
-
-       inline float pg_to_float(PGresult *res, int row, int col)
-       {
-               return (float) atof(PQgetvalue(res, row, col));
-       }
-
-       inline v3s16 pg_to_v3s16(PGresult *res, int row, int col)
-       {
-               return v3s16(
-                       pg_to_int(res, row, col),
-                       pg_to_int(res, row, col + 1),
-                       pg_to_int(res, row, col + 2)
-               );
-       }
-
-       inline PGresult *execPrepared(const char *stmtName, const int paramsNumber,
-               const void **params,
-               const int *paramsLengths = NULL, const int *paramsFormats = NULL,
-               bool clear = true, bool nobinary = true)
-       {
-               return checkResults(PQexecPrepared(m_conn, stmtName, paramsNumber,
-                       (const char* const*) params, paramsLengths, paramsFormats,
-                       nobinary ? 1 : 0), clear);
-       }
-
-       inline PGresult *execPrepared(const char *stmtName, const int paramsNumber,
-               const char **params, bool clear = true, bool nobinary = true)
-       {
-               return execPrepared(stmtName, paramsNumber,
-                       (const void **)params, NULL, NULL, clear, nobinary);
-       }
-
-       void createTableIfNotExists(const std::string &table_name, const std::string &definition);
-       void verifyDatabase();
-
-       // Database initialization
-       void connectToDatabase();
-       virtual void createDatabase() = 0;
-       virtual void initStatements() = 0;
-       inline void prepareStatement(const std::string &name, const std::string &sql)
-       {
-               checkResults(PQprepare(m_conn, name.c_str(), sql.c_str(), 0, NULL));
-       }
-
-       const int getPGVersion() const { return m_pgversion; }
-private:
-       // Database connectivity checks
-       void ping();
-
-       // Database usage
-       PGresult *checkResults(PGresult *res, bool clear = true);
-
-       // Attributes
-       std::string m_connect_string;
-       PGconn *m_conn = nullptr;
-       int m_pgversion = 0;
-};
-
-class MapDatabasePostgreSQL : private Database_PostgreSQL, public MapDatabase
-{
-public:
-       MapDatabasePostgreSQL(const std::string &connect_string);
-       virtual ~MapDatabasePostgreSQL() = default;
-
-       bool saveBlock(const v3s16 &pos, const std::string &data);
-       void loadBlock(const v3s16 &pos, std::string *block);
-       bool deleteBlock(const v3s16 &pos);
-       void listAllLoadableBlocks(std::vector<v3s16> &dst);
-
-       void beginSave() { Database_PostgreSQL::beginSave(); }
-       void endSave() { Database_PostgreSQL::endSave(); }
-
-protected:
-       virtual void createDatabase();
-       virtual void initStatements();
-};
-
-class PlayerDatabasePostgreSQL : private Database_PostgreSQL, public PlayerDatabase
-{
-public:
-       PlayerDatabasePostgreSQL(const std::string &connect_string);
-       virtual ~PlayerDatabasePostgreSQL() = default;
-
-       void savePlayer(RemotePlayer *player);
-       bool loadPlayer(RemotePlayer *player, PlayerSAO *sao);
-       bool removePlayer(const std::string &name);
-       void listPlayers(std::vector<std::string> &res);
-
-protected:
-       virtual void createDatabase();
-       virtual void initStatements();
-
-private:
-       bool playerDataExists(const std::string &playername);
-};
diff --git a/src/database-redis.cpp b/src/database-redis.cpp
deleted file mode 100644 (file)
index 096ea50..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "config.h"
-
-#if USE_REDIS
-
-#include "database-redis.h"
-
-#include "settings.h"
-#include "log.h"
-#include "exceptions.h"
-#include "util/string.h"
-
-#include <hiredis.h>
-#include <cassert>
-
-
-Database_Redis::Database_Redis(Settings &conf)
-{
-       std::string tmp;
-       try {
-               tmp = conf.get("redis_address");
-               hash = conf.get("redis_hash");
-       } catch (SettingNotFoundException &) {
-               throw SettingNotFoundException("Set redis_address and "
-                       "redis_hash in world.mt to use the redis backend");
-       }
-       const char *addr = tmp.c_str();
-       int port = conf.exists("redis_port") ? conf.getU16("redis_port") : 6379;
-       // if redis_address contains '/' assume unix socket, else hostname/ip
-       ctx = tmp.find('/') != std::string::npos ? redisConnectUnix(addr) : redisConnect(addr, port);
-       if (!ctx) {
-               throw DatabaseException("Cannot allocate redis context");
-       } else if (ctx->err) {
-               std::string err = std::string("Connection error: ") + ctx->errstr;
-               redisFree(ctx);
-               throw DatabaseException(err);
-       }
-       if (conf.exists("redis_password")) {
-               tmp = conf.get("redis_password");
-               redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "AUTH %s", tmp.c_str()));
-               if (!reply)
-                       throw DatabaseException("Redis authentication failed");
-               if (reply->type == REDIS_REPLY_ERROR) {
-                       std::string err = "Redis authentication failed: " + std::string(reply->str, reply->len);
-                       freeReplyObject(reply);
-                       throw DatabaseException(err);
-               }
-               freeReplyObject(reply);
-       }
-}
-
-Database_Redis::~Database_Redis()
-{
-       redisFree(ctx);
-}
-
-void Database_Redis::beginSave() {
-       redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "MULTI"));
-       if (!reply) {
-               throw DatabaseException(std::string(
-                       "Redis command 'MULTI' failed: ") + ctx->errstr);
-       }
-       freeReplyObject(reply);
-}
-
-void Database_Redis::endSave() {
-       redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "EXEC"));
-       if (!reply) {
-               throw DatabaseException(std::string(
-                       "Redis command 'EXEC' failed: ") + ctx->errstr);
-       }
-       freeReplyObject(reply);
-}
-
-bool Database_Redis::saveBlock(const v3s16 &pos, const std::string &data)
-{
-       std::string tmp = i64tos(getBlockAsInteger(pos));
-
-       redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "HSET %s %s %b",
-                       hash.c_str(), tmp.c_str(), data.c_str(), data.size()));
-       if (!reply) {
-               warningstream << "saveBlock: redis command 'HSET' failed on "
-                       "block " << PP(pos) << ": " << ctx->errstr << std::endl;
-               freeReplyObject(reply);
-               return false;
-       }
-
-       if (reply->type == REDIS_REPLY_ERROR) {
-               warningstream << "saveBlock: saving block " << PP(pos)
-                       << " failed: " << std::string(reply->str, reply->len) << std::endl;
-               freeReplyObject(reply);
-               return false;
-       }
-
-       freeReplyObject(reply);
-       return true;
-}
-
-void Database_Redis::loadBlock(const v3s16 &pos, std::string *block)
-{
-       std::string tmp = i64tos(getBlockAsInteger(pos));
-       redisReply *reply = static_cast<redisReply *>(redisCommand(ctx,
-                       "HGET %s %s", hash.c_str(), tmp.c_str()));
-
-       if (!reply) {
-               throw DatabaseException(std::string(
-                       "Redis command 'HGET %s %s' failed: ") + ctx->errstr);
-       }
-
-       switch (reply->type) {
-       case REDIS_REPLY_STRING: {
-               *block = std::string(reply->str, reply->len);
-               // std::string copies the memory so this won't cause any problems
-               freeReplyObject(reply);
-               return;
-       }
-       case REDIS_REPLY_ERROR: {
-               std::string errstr(reply->str, reply->len);
-               freeReplyObject(reply);
-               errorstream << "loadBlock: loading block " << PP(pos)
-                       << " failed: " << errstr << std::endl;
-               throw DatabaseException(std::string(
-                       "Redis command 'HGET %s %s' errored: ") + errstr);
-       }
-       case REDIS_REPLY_NIL: {
-               *block = "";
-               // block not found in database
-               freeReplyObject(reply);
-               return;
-       }
-       }
-
-       errorstream << "loadBlock: loading block " << PP(pos)
-               << " returned invalid reply type " << reply->type
-               << ": " << std::string(reply->str, reply->len) << std::endl;
-       freeReplyObject(reply);
-       throw DatabaseException(std::string(
-               "Redis command 'HGET %s %s' gave invalid reply."));
-}
-
-bool Database_Redis::deleteBlock(const v3s16 &pos)
-{
-       std::string tmp = i64tos(getBlockAsInteger(pos));
-
-       redisReply *reply = static_cast<redisReply *>(redisCommand(ctx,
-               "HDEL %s %s", hash.c_str(), tmp.c_str()));
-       if (!reply) {
-               throw DatabaseException(std::string(
-                       "Redis command 'HDEL %s %s' failed: ") + ctx->errstr);
-       } else if (reply->type == REDIS_REPLY_ERROR) {
-               warningstream << "deleteBlock: deleting block " << PP(pos)
-                       << " failed: " << std::string(reply->str, reply->len) << std::endl;
-               freeReplyObject(reply);
-               return false;
-       }
-
-       freeReplyObject(reply);
-       return true;
-}
-
-void Database_Redis::listAllLoadableBlocks(std::vector<v3s16> &dst)
-{
-       redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "HKEYS %s", hash.c_str()));
-       if (!reply) {
-               throw DatabaseException(std::string(
-                       "Redis command 'HKEYS %s' failed: ") + ctx->errstr);
-       }
-       switch (reply->type) {
-       case REDIS_REPLY_ARRAY:
-               dst.reserve(reply->elements);
-               for (size_t i = 0; i < reply->elements; i++) {
-                       assert(reply->element[i]->type == REDIS_REPLY_STRING);
-                       dst.push_back(getIntegerAsBlock(stoi64(reply->element[i]->str)));
-               }
-               break;
-       case REDIS_REPLY_ERROR:
-               throw DatabaseException(std::string(
-                       "Failed to get keys from database: ") +
-                       std::string(reply->str, reply->len));
-       }
-       freeReplyObject(reply);
-}
-
-#endif // USE_REDIS
-
diff --git a/src/database-redis.h b/src/database-redis.h
deleted file mode 100644 (file)
index 6bea563..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "config.h"
-
-#if USE_REDIS
-
-#include <hiredis.h>
-#include <string>
-#include "database.h"
-
-class Settings;
-
-class Database_Redis : public MapDatabase
-{
-public:
-       Database_Redis(Settings &conf);
-       ~Database_Redis();
-
-       void beginSave();
-       void endSave();
-
-       bool saveBlock(const v3s16 &pos, const std::string &data);
-       void loadBlock(const v3s16 &pos, std::string *block);
-       bool deleteBlock(const v3s16 &pos);
-       void listAllLoadableBlocks(std::vector<v3s16> &dst);
-
-private:
-       redisContext *ctx = nullptr;
-       std::string hash = "";
-};
-
-#endif // USE_REDIS
diff --git a/src/database-sqlite3.cpp b/src/database-sqlite3.cpp
deleted file mode 100644 (file)
index 78c182f..0000000
+++ /dev/null
@@ -1,606 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-/*
-SQLite format specification:
-       blocks:
-               (PK) INT id
-               BLOB data
-*/
-
-
-#include "database-sqlite3.h"
-
-#include "log.h"
-#include "filesys.h"
-#include "exceptions.h"
-#include "settings.h"
-#include "porting.h"
-#include "util/string.h"
-#include "content_sao.h"
-#include "remoteplayer.h"
-
-#include <cassert>
-
-// When to print messages when the database is being held locked by another process
-// Note: I've seen occasional delays of over 250ms while running minetestmapper.
-#define BUSY_INFO_TRESHOLD     100     // Print first informational message after 100ms.
-#define BUSY_WARNING_TRESHOLD  250     // Print warning message after 250ms. Lag is increased.
-#define BUSY_ERROR_TRESHOLD    1000    // Print error message after 1000ms. Significant lag.
-#define BUSY_FATAL_TRESHOLD    3000    // Allow SQLITE_BUSY to be returned, which will cause a minetest crash.
-#define BUSY_ERROR_INTERVAL    10000   // Safety net: report again every 10 seconds
-
-
-#define SQLRES(s, r, m) \
-       if ((s) != (r)) { \
-               throw DatabaseException(std::string(m) + ": " +\
-                               sqlite3_errmsg(m_database)); \
-       }
-#define SQLOK(s, m) SQLRES(s, SQLITE_OK, m)
-
-#define PREPARE_STATEMENT(name, query) \
-       SQLOK(sqlite3_prepare_v2(m_database, query, -1, &m_stmt_##name, NULL),\
-               "Failed to prepare query '" query "'")
-
-#define SQLOK_ERRSTREAM(s, m)                           \
-       if ((s) != SQLITE_OK) {                             \
-               errorstream << (m) << ": "                      \
-                       << sqlite3_errmsg(m_database) << std::endl; \
-       }
-
-#define FINALIZE_STATEMENT(statement) SQLOK_ERRSTREAM(sqlite3_finalize(statement), \
-       "Failed to finalize " #statement)
-
-int Database_SQLite3::busyHandler(void *data, int count)
-{
-       s64 &first_time = reinterpret_cast<s64 *>(data)[0];
-       s64 &prev_time = reinterpret_cast<s64 *>(data)[1];
-       s64 cur_time = porting::getTimeMs();
-
-       if (count == 0) {
-               first_time = cur_time;
-               prev_time = first_time;
-       } else {
-               while (cur_time < prev_time)
-                       cur_time += s64(1)<<32;
-       }
-
-       if (cur_time - first_time < BUSY_INFO_TRESHOLD) {
-               ; // do nothing
-       } else if (cur_time - first_time >= BUSY_INFO_TRESHOLD &&
-                       prev_time - first_time < BUSY_INFO_TRESHOLD) {
-               infostream << "SQLite3 database has been locked for "
-                       << cur_time - first_time << " ms." << std::endl;
-       } else if (cur_time - first_time >= BUSY_WARNING_TRESHOLD &&
-                       prev_time - first_time < BUSY_WARNING_TRESHOLD) {
-               warningstream << "SQLite3 database has been locked for "
-                       << cur_time - first_time << " ms." << std::endl;
-       } else if (cur_time - first_time >= BUSY_ERROR_TRESHOLD &&
-                       prev_time - first_time < BUSY_ERROR_TRESHOLD) {
-               errorstream << "SQLite3 database has been locked for "
-                       << cur_time - first_time << " ms; this causes lag." << std::endl;
-       } else if (cur_time - first_time >= BUSY_FATAL_TRESHOLD &&
-                       prev_time - first_time < BUSY_FATAL_TRESHOLD) {
-               errorstream << "SQLite3 database has been locked for "
-                       << cur_time - first_time << " ms - giving up!" << std::endl;
-       } else if ((cur_time - first_time) / BUSY_ERROR_INTERVAL !=
-                       (prev_time - first_time) / BUSY_ERROR_INTERVAL) {
-               // Safety net: keep reporting every BUSY_ERROR_INTERVAL
-               errorstream << "SQLite3 database has been locked for "
-                       << (cur_time - first_time) / 1000 << " seconds!" << std::endl;
-       }
-
-       prev_time = cur_time;
-
-       // Make sqlite transaction fail if delay exceeds BUSY_FATAL_TRESHOLD
-       return cur_time - first_time < BUSY_FATAL_TRESHOLD;
-}
-
-
-Database_SQLite3::Database_SQLite3(const std::string &savedir, const std::string &dbname) :
-       m_savedir(savedir),
-       m_dbname(dbname)
-{
-}
-
-void Database_SQLite3::beginSave()
-{
-       verifyDatabase();
-       SQLRES(sqlite3_step(m_stmt_begin), SQLITE_DONE,
-               "Failed to start SQLite3 transaction");
-       sqlite3_reset(m_stmt_begin);
-}
-
-void Database_SQLite3::endSave()
-{
-       verifyDatabase();
-       SQLRES(sqlite3_step(m_stmt_end), SQLITE_DONE,
-               "Failed to commit SQLite3 transaction");
-       sqlite3_reset(m_stmt_end);
-}
-
-void Database_SQLite3::openDatabase()
-{
-       if (m_database) return;
-
-       std::string dbp = m_savedir + DIR_DELIM + m_dbname + ".sqlite";
-
-       // Open the database connection
-
-       if (!fs::CreateAllDirs(m_savedir)) {
-               infostream << "Database_SQLite3: Failed to create directory \""
-                       << m_savedir << "\"" << std::endl;
-               throw FileNotGoodException("Failed to create database "
-                               "save directory");
-       }
-
-       bool needs_create = !fs::PathExists(dbp);
-
-       SQLOK(sqlite3_open_v2(dbp.c_str(), &m_database,
-                       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL),
-               std::string("Failed to open SQLite3 database file ") + dbp);
-
-       SQLOK(sqlite3_busy_handler(m_database, Database_SQLite3::busyHandler,
-               m_busy_handler_data), "Failed to set SQLite3 busy handler");
-
-       if (needs_create) {
-               createDatabase();
-       }
-
-       std::string query_str = std::string("PRAGMA synchronous = ")
-                        + itos(g_settings->getU16("sqlite_synchronous"));
-       SQLOK(sqlite3_exec(m_database, query_str.c_str(), NULL, NULL, NULL),
-               "Failed to modify sqlite3 synchronous mode");
-       SQLOK(sqlite3_exec(m_database, "PRAGMA foreign_keys = ON", NULL, NULL, NULL),
-               "Failed to enable sqlite3 foreign key support");
-}
-
-void Database_SQLite3::verifyDatabase()
-{
-       if (m_initialized) return;
-
-       openDatabase();
-
-       PREPARE_STATEMENT(begin, "BEGIN;");
-       PREPARE_STATEMENT(end, "COMMIT;");
-
-       initStatements();
-
-       m_initialized = true;
-}
-
-Database_SQLite3::~Database_SQLite3()
-{
-       FINALIZE_STATEMENT(m_stmt_begin)
-       FINALIZE_STATEMENT(m_stmt_end)
-
-       SQLOK_ERRSTREAM(sqlite3_close(m_database), "Failed to close database");
-}
-
-/*
- * Map database
- */
-
-MapDatabaseSQLite3::MapDatabaseSQLite3(const std::string &savedir):
-       Database_SQLite3(savedir, "map"),
-       MapDatabase()
-{
-}
-
-MapDatabaseSQLite3::~MapDatabaseSQLite3()
-{
-       FINALIZE_STATEMENT(m_stmt_read)
-       FINALIZE_STATEMENT(m_stmt_write)
-       FINALIZE_STATEMENT(m_stmt_list)
-       FINALIZE_STATEMENT(m_stmt_delete)
-}
-
-
-void MapDatabaseSQLite3::createDatabase()
-{
-       assert(m_database); // Pre-condition
-
-       SQLOK(sqlite3_exec(m_database,
-               "CREATE TABLE IF NOT EXISTS `blocks` (\n"
-                       "       `pos` INT PRIMARY KEY,\n"
-                       "       `data` BLOB\n"
-                       ");\n",
-               NULL, NULL, NULL),
-               "Failed to create database table");
-}
-
-void MapDatabaseSQLite3::initStatements()
-{
-       PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `pos` = ? LIMIT 1");
-#ifdef __ANDROID__
-       PREPARE_STATEMENT(write,  "INSERT INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
-#else
-       PREPARE_STATEMENT(write, "REPLACE INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
-#endif
-       PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?");
-       PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`");
-
-       verbosestream << "ServerMap: SQLite3 database opened." << std::endl;
-}
-
-inline void MapDatabaseSQLite3::bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index)
-{
-       SQLOK(sqlite3_bind_int64(stmt, index, getBlockAsInteger(pos)),
-               "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
-}
-
-bool MapDatabaseSQLite3::deleteBlock(const v3s16 &pos)
-{
-       verifyDatabase();
-
-       bindPos(m_stmt_delete, pos);
-
-       bool good = sqlite3_step(m_stmt_delete) == SQLITE_DONE;
-       sqlite3_reset(m_stmt_delete);
-
-       if (!good) {
-               warningstream << "deleteBlock: Block failed to delete "
-                       << PP(pos) << ": " << sqlite3_errmsg(m_database) << std::endl;
-       }
-       return good;
-}
-
-bool MapDatabaseSQLite3::saveBlock(const v3s16 &pos, const std::string &data)
-{
-       verifyDatabase();
-
-#ifdef __ANDROID__
-       /**
-        * Note: For some unknown reason SQLite3 fails to REPLACE blocks on Android,
-        * deleting them and then inserting works.
-        */
-       bindPos(m_stmt_read, pos);
-
-       if (sqlite3_step(m_stmt_read) == SQLITE_ROW) {
-               deleteBlock(pos);
-       }
-       sqlite3_reset(m_stmt_read);
-#endif
-
-       bindPos(m_stmt_write, pos);
-       SQLOK(sqlite3_bind_blob(m_stmt_write, 2, data.data(), data.size(), NULL),
-               "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
-
-       SQLRES(sqlite3_step(m_stmt_write), SQLITE_DONE, "Failed to save block")
-       sqlite3_reset(m_stmt_write);
-
-       return true;
-}
-
-void MapDatabaseSQLite3::loadBlock(const v3s16 &pos, std::string *block)
-{
-       verifyDatabase();
-
-       bindPos(m_stmt_read, pos);
-
-       if (sqlite3_step(m_stmt_read) != SQLITE_ROW) {
-               sqlite3_reset(m_stmt_read);
-               return;
-       }
-
-       const char *data = (const char *) sqlite3_column_blob(m_stmt_read, 0);
-       size_t len = sqlite3_column_bytes(m_stmt_read, 0);
-
-       *block = (data) ? std::string(data, len) : "";
-
-       sqlite3_step(m_stmt_read);
-       // We should never get more than 1 row, so ok to reset
-       sqlite3_reset(m_stmt_read);
-}
-
-void MapDatabaseSQLite3::listAllLoadableBlocks(std::vector<v3s16> &dst)
-{
-       verifyDatabase();
-
-       while (sqlite3_step(m_stmt_list) == SQLITE_ROW)
-               dst.push_back(getIntegerAsBlock(sqlite3_column_int64(m_stmt_list, 0)));
-
-       sqlite3_reset(m_stmt_list);
-}
-
-/*
- * Player Database
- */
-
-PlayerDatabaseSQLite3::PlayerDatabaseSQLite3(const std::string &savedir):
-       Database_SQLite3(savedir, "players"),
-       PlayerDatabase()
-{
-}
-
-PlayerDatabaseSQLite3::~PlayerDatabaseSQLite3()
-{
-       FINALIZE_STATEMENT(m_stmt_player_load)
-       FINALIZE_STATEMENT(m_stmt_player_add)
-       FINALIZE_STATEMENT(m_stmt_player_update)
-       FINALIZE_STATEMENT(m_stmt_player_remove)
-       FINALIZE_STATEMENT(m_stmt_player_list)
-       FINALIZE_STATEMENT(m_stmt_player_add_inventory)
-       FINALIZE_STATEMENT(m_stmt_player_add_inventory_items)
-       FINALIZE_STATEMENT(m_stmt_player_remove_inventory)
-       FINALIZE_STATEMENT(m_stmt_player_remove_inventory_items)
-       FINALIZE_STATEMENT(m_stmt_player_load_inventory)
-       FINALIZE_STATEMENT(m_stmt_player_load_inventory_items)
-       FINALIZE_STATEMENT(m_stmt_player_metadata_load)
-       FINALIZE_STATEMENT(m_stmt_player_metadata_add)
-       FINALIZE_STATEMENT(m_stmt_player_metadata_remove)
-};
-
-
-void PlayerDatabaseSQLite3::createDatabase()
-{
-       assert(m_database); // Pre-condition
-
-       SQLOK(sqlite3_exec(m_database,
-               "CREATE TABLE IF NOT EXISTS `player` ("
-                       "`name` VARCHAR(50) NOT NULL,"
-                       "`pitch` NUMERIC(11, 4) NOT NULL,"
-                       "`yaw` NUMERIC(11, 4) NOT NULL,"
-                       "`posX` NUMERIC(11, 4) NOT NULL,"
-                       "`posY` NUMERIC(11, 4) NOT NULL,"
-                       "`posZ` NUMERIC(11, 4) NOT NULL,"
-                       "`hp` INT NOT NULL,"
-                       "`breath` INT NOT NULL,"
-                       "`creation_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,"
-                       "`modification_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,"
-                       "PRIMARY KEY (`name`));",
-               NULL, NULL, NULL),
-               "Failed to create player table");
-
-       SQLOK(sqlite3_exec(m_database,
-               "CREATE TABLE IF NOT EXISTS `player_metadata` ("
-                       "    `player` VARCHAR(50) NOT NULL,"
-                       "    `metadata` VARCHAR(256) NOT NULL,"
-                       "    `value` TEXT,"
-                       "    PRIMARY KEY(`player`, `metadata`),"
-                       "    FOREIGN KEY (`player`) REFERENCES player (`name`) ON DELETE CASCADE );",
-               NULL, NULL, NULL),
-               "Failed to create player metadata table");
-
-       SQLOK(sqlite3_exec(m_database,
-               "CREATE TABLE IF NOT EXISTS `player_inventories` ("
-                       "   `player` VARCHAR(50) NOT NULL,"
-                       "       `inv_id` INT NOT NULL,"
-                       "       `inv_width` INT NOT NULL,"
-                       "       `inv_name` TEXT NOT NULL DEFAULT '',"
-                       "       `inv_size` INT NOT NULL,"
-                       "       PRIMARY KEY(player, inv_id),"
-                       "   FOREIGN KEY (`player`) REFERENCES player (`name`) ON DELETE CASCADE );",
-               NULL, NULL, NULL),
-               "Failed to create player inventory table");
-
-       SQLOK(sqlite3_exec(m_database,
-               "CREATE TABLE `player_inventory_items` ("
-                       "   `player` VARCHAR(50) NOT NULL,"
-                       "       `inv_id` INT NOT NULL,"
-                       "       `slot_id` INT NOT NULL,"
-                       "       `item` TEXT NOT NULL DEFAULT '',"
-                       "       PRIMARY KEY(player, inv_id, slot_id),"
-                       "   FOREIGN KEY (`player`) REFERENCES player (`name`) ON DELETE CASCADE );",
-               NULL, NULL, NULL),
-               "Failed to create player inventory items table");
-}
-
-void PlayerDatabaseSQLite3::initStatements()
-{
-       PREPARE_STATEMENT(player_load, "SELECT `pitch`, `yaw`, `posX`, `posY`, `posZ`, `hp`, "
-               "`breath`"
-               "FROM `player` WHERE `name` = ?")
-       PREPARE_STATEMENT(player_add, "INSERT INTO `player` (`name`, `pitch`, `yaw`, `posX`, "
-               "`posY`, `posZ`, `hp`, `breath`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
-       PREPARE_STATEMENT(player_update, "UPDATE `player` SET `pitch` = ?, `yaw` = ?, "
-                       "`posX` = ?, `posY` = ?, `posZ` = ?, `hp` = ?, `breath` = ?, "
-                       "`modification_date` = CURRENT_TIMESTAMP WHERE `name` = ?")
-       PREPARE_STATEMENT(player_remove, "DELETE FROM `player` WHERE `name` = ?")
-       PREPARE_STATEMENT(player_list, "SELECT `name` FROM `player`")
-
-       PREPARE_STATEMENT(player_add_inventory, "INSERT INTO `player_inventories` "
-               "(`player`, `inv_id`, `inv_width`, `inv_name`, `inv_size`) VALUES (?, ?, ?, ?, ?)")
-       PREPARE_STATEMENT(player_add_inventory_items, "INSERT INTO `player_inventory_items` "
-               "(`player`, `inv_id`, `slot_id`, `item`) VALUES (?, ?, ?, ?)")
-       PREPARE_STATEMENT(player_remove_inventory, "DELETE FROM `player_inventories` "
-               "WHERE `player` = ?")
-       PREPARE_STATEMENT(player_remove_inventory_items, "DELETE FROM `player_inventory_items` "
-               "WHERE `player` = ?")
-       PREPARE_STATEMENT(player_load_inventory, "SELECT `inv_id`, `inv_width`, `inv_name`, "
-               "`inv_size` FROM `player_inventories` WHERE `player` = ? ORDER BY inv_id")
-       PREPARE_STATEMENT(player_load_inventory_items, "SELECT `slot_id`, `item` "
-               "FROM `player_inventory_items` WHERE `player` = ? AND `inv_id` = ?")
-
-       PREPARE_STATEMENT(player_metadata_load, "SELECT `metadata`, `value` FROM "
-               "`player_metadata` WHERE `player` = ?")
-       PREPARE_STATEMENT(player_metadata_add, "INSERT INTO `player_metadata` "
-               "(`player`, `metadata`, `value`) VALUES (?, ?, ?)")
-       PREPARE_STATEMENT(player_metadata_remove, "DELETE FROM `player_metadata` "
-               "WHERE `player` = ?")
-       verbosestream << "ServerEnvironment: SQLite3 database opened (players)." << std::endl;
-}
-
-bool PlayerDatabaseSQLite3::playerDataExists(const std::string &name)
-{
-       verifyDatabase();
-       str_to_sqlite(m_stmt_player_load, 1, name);
-       bool res = (sqlite3_step(m_stmt_player_load) == SQLITE_ROW);
-       sqlite3_reset(m_stmt_player_load);
-       return res;
-}
-
-void PlayerDatabaseSQLite3::savePlayer(RemotePlayer *player)
-{
-       PlayerSAO* sao = player->getPlayerSAO();
-       sanity_check(sao);
-
-       const v3f &pos = sao->getBasePosition();
-       // Begin save in brace is mandatory
-       if (!playerDataExists(player->getName())) {
-               beginSave();
-               str_to_sqlite(m_stmt_player_add, 1, player->getName());
-               double_to_sqlite(m_stmt_player_add, 2, sao->getPitch());
-               double_to_sqlite(m_stmt_player_add, 3, sao->getYaw());
-               double_to_sqlite(m_stmt_player_add, 4, pos.X);
-               double_to_sqlite(m_stmt_player_add, 5, pos.Y);
-               double_to_sqlite(m_stmt_player_add, 6, pos.Z);
-               int64_to_sqlite(m_stmt_player_add, 7, sao->getHP());
-               int64_to_sqlite(m_stmt_player_add, 8, sao->getBreath());
-
-               sqlite3_vrfy(sqlite3_step(m_stmt_player_add), SQLITE_DONE);
-               sqlite3_reset(m_stmt_player_add);
-       } else {
-               beginSave();
-               double_to_sqlite(m_stmt_player_update, 1, sao->getPitch());
-               double_to_sqlite(m_stmt_player_update, 2, sao->getYaw());
-               double_to_sqlite(m_stmt_player_update, 3, pos.X);
-               double_to_sqlite(m_stmt_player_update, 4, pos.Y);
-               double_to_sqlite(m_stmt_player_update, 5, pos.Z);
-               int64_to_sqlite(m_stmt_player_update, 6, sao->getHP());
-               int64_to_sqlite(m_stmt_player_update, 7, sao->getBreath());
-               str_to_sqlite(m_stmt_player_update, 8, player->getName());
-
-               sqlite3_vrfy(sqlite3_step(m_stmt_player_update), SQLITE_DONE);
-               sqlite3_reset(m_stmt_player_update);
-       }
-
-       // Write player inventories
-       str_to_sqlite(m_stmt_player_remove_inventory, 1, player->getName());
-       sqlite3_vrfy(sqlite3_step(m_stmt_player_remove_inventory), SQLITE_DONE);
-       sqlite3_reset(m_stmt_player_remove_inventory);
-
-       str_to_sqlite(m_stmt_player_remove_inventory_items, 1, player->getName());
-       sqlite3_vrfy(sqlite3_step(m_stmt_player_remove_inventory_items), SQLITE_DONE);
-       sqlite3_reset(m_stmt_player_remove_inventory_items);
-
-       std::vector<const InventoryList*> inventory_lists = sao->getInventory()->getLists();
-       for (u16 i = 0; i < inventory_lists.size(); i++) {
-               const InventoryList* list = inventory_lists[i];
-
-               str_to_sqlite(m_stmt_player_add_inventory, 1, player->getName());
-               int_to_sqlite(m_stmt_player_add_inventory, 2, i);
-               int_to_sqlite(m_stmt_player_add_inventory, 3, list->getWidth());
-               str_to_sqlite(m_stmt_player_add_inventory, 4, list->getName());
-               int_to_sqlite(m_stmt_player_add_inventory, 5, list->getSize());
-               sqlite3_vrfy(sqlite3_step(m_stmt_player_add_inventory), SQLITE_DONE);
-               sqlite3_reset(m_stmt_player_add_inventory);
-
-               for (u32 j = 0; j < list->getSize(); j++) {
-                       std::ostringstream os;
-                       list->getItem(j).serialize(os);
-                       std::string itemStr = os.str();
-
-                       str_to_sqlite(m_stmt_player_add_inventory_items, 1, player->getName());
-                       int_to_sqlite(m_stmt_player_add_inventory_items, 2, i);
-                       int_to_sqlite(m_stmt_player_add_inventory_items, 3, j);
-                       str_to_sqlite(m_stmt_player_add_inventory_items, 4, itemStr);
-                       sqlite3_vrfy(sqlite3_step(m_stmt_player_add_inventory_items), SQLITE_DONE);
-                       sqlite3_reset(m_stmt_player_add_inventory_items);
-               }
-       }
-
-       str_to_sqlite(m_stmt_player_metadata_remove, 1, player->getName());
-       sqlite3_vrfy(sqlite3_step(m_stmt_player_metadata_remove), SQLITE_DONE);
-       sqlite3_reset(m_stmt_player_metadata_remove);
-
-       const PlayerAttributes &attrs = sao->getExtendedAttributes();
-       for (const auto &attr : attrs) {
-               str_to_sqlite(m_stmt_player_metadata_add, 1, player->getName());
-               str_to_sqlite(m_stmt_player_metadata_add, 2, attr.first);
-               str_to_sqlite(m_stmt_player_metadata_add, 3, attr.second);
-               sqlite3_vrfy(sqlite3_step(m_stmt_player_metadata_add), SQLITE_DONE);
-               sqlite3_reset(m_stmt_player_metadata_add);
-       }
-
-       endSave();
-}
-
-bool PlayerDatabaseSQLite3::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
-{
-       verifyDatabase();
-
-       str_to_sqlite(m_stmt_player_load, 1, player->getName());
-       if (sqlite3_step(m_stmt_player_load) != SQLITE_ROW) {
-               sqlite3_reset(m_stmt_player_load);
-               return false;
-       }
-       sao->setPitch(sqlite_to_float(m_stmt_player_load, 0));
-       sao->setYaw(sqlite_to_float(m_stmt_player_load, 1));
-       sao->setBasePosition(sqlite_to_v3f(m_stmt_player_load, 2));
-       sao->setHPRaw((s16) MYMIN(sqlite_to_int(m_stmt_player_load, 5), S16_MAX));
-       sao->setBreath((u16) MYMIN(sqlite_to_int(m_stmt_player_load, 6), U16_MAX), false);
-       sqlite3_reset(m_stmt_player_load);
-
-       // Load inventory
-       str_to_sqlite(m_stmt_player_load_inventory, 1, player->getName());
-       while (sqlite3_step(m_stmt_player_load_inventory) == SQLITE_ROW) {
-               InventoryList *invList = player->inventory.addList(
-                       sqlite_to_string(m_stmt_player_load_inventory, 2),
-                       sqlite_to_uint(m_stmt_player_load_inventory, 3));
-               invList->setWidth(sqlite_to_uint(m_stmt_player_load_inventory, 1));
-
-               u32 invId = sqlite_to_uint(m_stmt_player_load_inventory, 0);
-
-               str_to_sqlite(m_stmt_player_load_inventory_items, 1, player->getName());
-               int_to_sqlite(m_stmt_player_load_inventory_items, 2, invId);
-               while (sqlite3_step(m_stmt_player_load_inventory_items) == SQLITE_ROW) {
-                       const std::string itemStr = sqlite_to_string(m_stmt_player_load_inventory_items, 1);
-                       if (itemStr.length() > 0) {
-                               ItemStack stack;
-                               stack.deSerialize(itemStr);
-                               invList->changeItem(sqlite_to_uint(m_stmt_player_load_inventory_items, 0), stack);
-                       }
-               }
-               sqlite3_reset(m_stmt_player_load_inventory_items);
-       }
-
-       sqlite3_reset(m_stmt_player_load_inventory);
-
-       str_to_sqlite(m_stmt_player_metadata_load, 1, sao->getPlayer()->getName());
-       while (sqlite3_step(m_stmt_player_metadata_load) == SQLITE_ROW) {
-               std::string attr = sqlite_to_string(m_stmt_player_metadata_load, 0);
-               std::string value = sqlite_to_string(m_stmt_player_metadata_load, 1);
-
-               sao->setExtendedAttribute(attr, value);
-       }
-       sqlite3_reset(m_stmt_player_metadata_load);
-       return true;
-}
-
-bool PlayerDatabaseSQLite3::removePlayer(const std::string &name)
-{
-       if (!playerDataExists(name))
-               return false;
-
-       str_to_sqlite(m_stmt_player_remove, 1, name);
-       sqlite3_vrfy(sqlite3_step(m_stmt_player_remove), SQLITE_DONE);
-       sqlite3_reset(m_stmt_player_remove);
-       return true;
-}
-
-void PlayerDatabaseSQLite3::listPlayers(std::vector<std::string> &res)
-{
-       verifyDatabase();
-
-       while (sqlite3_step(m_stmt_player_list) == SQLITE_ROW)
-               res.push_back(sqlite_to_string(m_stmt_player_list, 0));
-
-       sqlite3_reset(m_stmt_player_list);
-}
diff --git a/src/database-sqlite3.h b/src/database-sqlite3.h
deleted file mode 100644 (file)
index 8d9f91f..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include <cstring>
-#include <string>
-#include "database.h"
-#include "exceptions.h"
-
-extern "C" {
-#include "sqlite3.h"
-}
-
-class Database_SQLite3 : public Database
-{
-public:
-       virtual ~Database_SQLite3();
-
-       void beginSave();
-       void endSave();
-
-       bool initialized() const { return m_initialized; }
-protected:
-       Database_SQLite3(const std::string &savedir, const std::string &dbname);
-
-       // Open and initialize the database if needed
-       void verifyDatabase();
-
-       // Convertors
-       inline void str_to_sqlite(sqlite3_stmt *s, int iCol, const std::string &str) const
-       {
-               sqlite3_vrfy(sqlite3_bind_text(s, iCol, str.c_str(), str.size(), NULL));
-       }
-
-       inline void str_to_sqlite(sqlite3_stmt *s, int iCol, const char *str) const
-       {
-               sqlite3_vrfy(sqlite3_bind_text(s, iCol, str, strlen(str), NULL));
-       }
-
-       inline void int_to_sqlite(sqlite3_stmt *s, int iCol, int val) const
-       {
-               sqlite3_vrfy(sqlite3_bind_int(s, iCol, val));
-       }
-
-       inline void int64_to_sqlite(sqlite3_stmt *s, int iCol, s64 val) const
-       {
-               sqlite3_vrfy(sqlite3_bind_int64(s, iCol, (sqlite3_int64) val));
-       }
-
-       inline void double_to_sqlite(sqlite3_stmt *s, int iCol, double val) const
-       {
-               sqlite3_vrfy(sqlite3_bind_double(s, iCol, val));
-       }
-
-       inline std::string sqlite_to_string(sqlite3_stmt *s, int iCol)
-       {
-               const char* text = reinterpret_cast<const char*>(sqlite3_column_text(s, iCol));
-               return std::string(text ? text : "");
-       }
-
-       inline s32 sqlite_to_int(sqlite3_stmt *s, int iCol)
-       {
-               return sqlite3_column_int(s, iCol);
-       }
-
-       inline u32 sqlite_to_uint(sqlite3_stmt *s, int iCol)
-       {
-               return (u32) sqlite3_column_int(s, iCol);
-       }
-
-       inline float sqlite_to_float(sqlite3_stmt *s, int iCol)
-       {
-               return (float) sqlite3_column_double(s, iCol);
-       }
-
-       inline const v3f sqlite_to_v3f(sqlite3_stmt *s, int iCol)
-       {
-               return v3f(sqlite_to_float(s, iCol), sqlite_to_float(s, iCol + 1),
-                               sqlite_to_float(s, iCol + 2));
-       }
-
-       // Query verifiers helpers
-       inline void sqlite3_vrfy(int s, const std::string &m = "", int r = SQLITE_OK) const
-       {
-               if (s != r)
-                       throw DatabaseException(m + ": " + sqlite3_errmsg(m_database));
-       }
-
-       inline void sqlite3_vrfy(const int s, const int r, const std::string &m = "") const
-       {
-               sqlite3_vrfy(s, m, r);
-       }
-
-       // Create the database structure
-       virtual void createDatabase() = 0;
-       virtual void initStatements() = 0;
-
-       sqlite3 *m_database = nullptr;
-private:
-       // Open the database
-       void openDatabase();
-
-       bool m_initialized = false;
-
-       std::string m_savedir = "";
-       std::string m_dbname = "";
-
-       sqlite3_stmt *m_stmt_begin = nullptr;
-       sqlite3_stmt *m_stmt_end = nullptr;
-
-       s64 m_busy_handler_data[2];
-
-       static int busyHandler(void *data, int count);
-};
-
-class MapDatabaseSQLite3 : private Database_SQLite3, public MapDatabase
-{
-public:
-       MapDatabaseSQLite3(const std::string &savedir);
-       virtual ~MapDatabaseSQLite3();
-
-       bool saveBlock(const v3s16 &pos, const std::string &data);
-       void loadBlock(const v3s16 &pos, std::string *block);
-       bool deleteBlock(const v3s16 &pos);
-       void listAllLoadableBlocks(std::vector<v3s16> &dst);
-
-       void beginSave() { Database_SQLite3::beginSave(); }
-       void endSave() { Database_SQLite3::endSave(); }
-protected:
-       virtual void createDatabase();
-       virtual void initStatements();
-
-private:
-       void bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index = 1);
-
-       // Map
-       sqlite3_stmt *m_stmt_read = nullptr;
-       sqlite3_stmt *m_stmt_write = nullptr;
-       sqlite3_stmt *m_stmt_list = nullptr;
-       sqlite3_stmt *m_stmt_delete = nullptr;
-};
-
-class PlayerDatabaseSQLite3 : private Database_SQLite3, public PlayerDatabase
-{
-public:
-       PlayerDatabaseSQLite3(const std::string &savedir);
-       virtual ~PlayerDatabaseSQLite3();
-
-       void savePlayer(RemotePlayer *player);
-       bool loadPlayer(RemotePlayer *player, PlayerSAO *sao);
-       bool removePlayer(const std::string &name);
-       void listPlayers(std::vector<std::string> &res);
-
-protected:
-       virtual void createDatabase();
-       virtual void initStatements();
-
-private:
-       bool playerDataExists(const std::string &name);
-
-       // Players
-       sqlite3_stmt *m_stmt_player_load = nullptr;
-       sqlite3_stmt *m_stmt_player_add = nullptr;
-       sqlite3_stmt *m_stmt_player_update = nullptr;
-       sqlite3_stmt *m_stmt_player_remove = nullptr;
-       sqlite3_stmt *m_stmt_player_list = nullptr;
-       sqlite3_stmt *m_stmt_player_load_inventory = nullptr;
-       sqlite3_stmt *m_stmt_player_load_inventory_items = nullptr;
-       sqlite3_stmt *m_stmt_player_add_inventory = nullptr;
-       sqlite3_stmt *m_stmt_player_add_inventory_items = nullptr;
-       sqlite3_stmt *m_stmt_player_remove_inventory = nullptr;
-       sqlite3_stmt *m_stmt_player_remove_inventory_items = nullptr;
-       sqlite3_stmt *m_stmt_player_metadata_load = nullptr;
-       sqlite3_stmt *m_stmt_player_metadata_remove = nullptr;
-       sqlite3_stmt *m_stmt_player_metadata_add = nullptr;
-};
diff --git a/src/database.cpp b/src/database.cpp
deleted file mode 100644 (file)
index 12e0e1a..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "database.h"
-#include "irrlichttypes.h"
-
-
-/****************
- * Black magic! *
- ****************
- * The position hashing is very messed up.
- * It's a lot more complicated than it looks.
- */
-
-static inline s16 unsigned_to_signed(u16 i, u16 max_positive)
-{
-       if (i < max_positive) {
-               return i;
-       }
-
-       return i - (max_positive * 2);
-}
-
-
-// Modulo of a negative number does not work consistently in C
-static inline s64 pythonmodulo(s64 i, s16 mod)
-{
-       if (i >= 0) {
-               return i % mod;
-       }
-       return mod - ((-i) % mod);
-}
-
-
-s64 MapDatabase::getBlockAsInteger(const v3s16 &pos)
-{
-       return (u64) pos.Z * 0x1000000 +
-               (u64) pos.Y * 0x1000 +
-               (u64) pos.X;
-}
-
-
-v3s16 MapDatabase::getIntegerAsBlock(s64 i)
-{
-       v3s16 pos;
-       pos.X = unsigned_to_signed(pythonmodulo(i, 4096), 2048);
-       i = (i - pos.X) / 4096;
-       pos.Y = unsigned_to_signed(pythonmodulo(i, 4096), 2048);
-       i = (i - pos.Y) / 4096;
-       pos.Z = unsigned_to_signed(pythonmodulo(i, 4096), 2048);
-       return pos;
-}
-
diff --git a/src/database.h b/src/database.h
deleted file mode 100644 (file)
index 9926c7b..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include <string>
-#include <vector>
-#include "irr_v3d.h"
-#include "irrlichttypes.h"
-#include "util/basic_macros.h"
-
-class Database
-{
-public:
-       virtual void beginSave() = 0;
-       virtual void endSave() = 0;
-       virtual bool initialized() const { return true; }
-};
-
-class MapDatabase : public Database
-{
-public:
-       virtual ~MapDatabase() = default;
-
-       virtual bool saveBlock(const v3s16 &pos, const std::string &data) = 0;
-       virtual void loadBlock(const v3s16 &pos, std::string *block) = 0;
-       virtual bool deleteBlock(const v3s16 &pos) = 0;
-
-       static s64 getBlockAsInteger(const v3s16 &pos);
-       static v3s16 getIntegerAsBlock(s64 i);
-
-       virtual void listAllLoadableBlocks(std::vector<v3s16> &dst) = 0;
-};
-
-class PlayerSAO;
-class RemotePlayer;
-
-class PlayerDatabase
-{
-public:
-       virtual ~PlayerDatabase() = default;
-
-       virtual void savePlayer(RemotePlayer *player) = 0;
-       virtual bool loadPlayer(RemotePlayer *player, PlayerSAO *sao) = 0;
-       virtual bool removePlayer(const std::string &name) = 0;
-       virtual void listPlayers(std::vector<std::string> &res) = 0;
-};
diff --git a/src/database/CMakeLists.txt b/src/database/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e9d157c
--- /dev/null
@@ -0,0 +1,10 @@
+set(database_SRCS
+       ${CMAKE_CURRENT_SOURCE_DIR}/database.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/database-dummy.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/database-files.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/database-leveldb.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/database-postgresql.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/database-redis.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/database-sqlite3.cpp
+       PARENT_SCOPE
+)
diff --git a/src/database/database-dummy.cpp b/src/database/database-dummy.cpp
new file mode 100644 (file)
index 0000000..a3d8cd5
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+/*
+Dummy database class
+*/
+
+#include "database-dummy.h"
+
+
+bool Database_Dummy::saveBlock(const v3s16 &pos, const std::string &data)
+{
+       m_database[getBlockAsInteger(pos)] = data;
+       return true;
+}
+
+void Database_Dummy::loadBlock(const v3s16 &pos, std::string *block)
+{
+       s64 i = getBlockAsInteger(pos);
+       auto it = m_database.find(i);
+       if (it == m_database.end()) {
+               *block = "";
+               return;
+       }
+
+       *block = it->second;
+}
+
+bool Database_Dummy::deleteBlock(const v3s16 &pos)
+{
+       m_database.erase(getBlockAsInteger(pos));
+       return true;
+}
+
+void Database_Dummy::listAllLoadableBlocks(std::vector<v3s16> &dst)
+{
+       dst.reserve(m_database.size());
+       for (std::map<s64, std::string>::const_iterator x = m_database.begin();
+                       x != m_database.end(); ++x) {
+               dst.push_back(getIntegerAsBlock(x->first));
+       }
+}
+
diff --git a/src/database/database-dummy.h b/src/database/database-dummy.h
new file mode 100644 (file)
index 0000000..2d87d58
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include <map>
+#include <string>
+#include "database.h"
+#include "irrlichttypes.h"
+
+class Database_Dummy : public MapDatabase, public PlayerDatabase
+{
+public:
+       bool saveBlock(const v3s16 &pos, const std::string &data);
+       void loadBlock(const v3s16 &pos, std::string *block);
+       bool deleteBlock(const v3s16 &pos);
+       void listAllLoadableBlocks(std::vector<v3s16> &dst);
+
+       void savePlayer(RemotePlayer *player) {}
+       bool loadPlayer(RemotePlayer *player, PlayerSAO *sao) { return true; }
+       bool removePlayer(const std::string &name) { return true; }
+       void listPlayers(std::vector<std::string> &res) {}
+
+       void beginSave() {}
+       void endSave() {}
+
+private:
+       std::map<s64, std::string> m_database;
+};
diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp
new file mode 100644 (file)
index 0000000..70de8c8
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+Minetest
+Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 <cassert>
+#include <json/json.h>
+#include "database-files.h"
+#include "content_sao.h"
+#include "remoteplayer.h"
+#include "settings.h"
+#include "porting.h"
+#include "filesys.h"
+
+// !!! WARNING !!!
+// This backend is intended to be used on Minetest 0.4.16 only for the transition backend
+// for player files
+
+void PlayerDatabaseFiles::serialize(std::ostringstream &os, RemotePlayer *player)
+{
+       // Utilize a Settings object for storing values
+       Settings args;
+       args.setS32("version", 1);
+       args.set("name", player->getName());
+
+       sanity_check(player->getPlayerSAO());
+       args.setS32("hp", player->getPlayerSAO()->getHP());
+       args.setV3F("position", player->getPlayerSAO()->getBasePosition());
+       args.setFloat("pitch", player->getPlayerSAO()->getPitch());
+       args.setFloat("yaw", player->getPlayerSAO()->getYaw());
+       args.setS32("breath", player->getPlayerSAO()->getBreath());
+
+       std::string extended_attrs;
+       player->serializeExtraAttributes(extended_attrs);
+       args.set("extended_attributes", extended_attrs);
+
+       args.writeLines(os);
+
+       os << "PlayerArgsEnd\n";
+
+       player->inventory.serialize(os);
+}
+
+void PlayerDatabaseFiles::savePlayer(RemotePlayer *player)
+{
+       std::string savedir = m_savedir + DIR_DELIM;
+       std::string path = savedir + player->getName();
+       bool path_found = false;
+       RemotePlayer testplayer("", NULL);
+
+       for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES && !path_found; i++) {
+               if (!fs::PathExists(path)) {
+                       path_found = true;
+                       continue;
+               }
+
+               // Open and deserialize file to check player name
+               std::ifstream is(path.c_str(), std::ios_base::binary);
+               if (!is.good()) {
+                       errorstream << "Failed to open " << path << std::endl;
+                       return;
+               }
+
+               testplayer.deSerialize(is, path, NULL);
+               is.close();
+               if (strcmp(testplayer.getName(), player->getName()) == 0) {
+                       path_found = true;
+                       continue;
+               }
+
+               path = savedir + player->getName() + itos(i);
+       }
+
+       if (!path_found) {
+               errorstream << "Didn't find free file for player " << player->getName()
+                               << std::endl;
+               return;
+       }
+
+       // Open and serialize file
+       std::ostringstream ss(std::ios_base::binary);
+       serialize(ss, player);
+       if (!fs::safeWriteToFile(path, ss.str())) {
+               infostream << "Failed to write " << path << std::endl;
+       }
+       player->setModified(false);
+}
+
+bool PlayerDatabaseFiles::removePlayer(const std::string &name)
+{
+       std::string players_path = m_savedir + DIR_DELIM;
+       std::string path = players_path + name;
+
+       RemotePlayer temp_player("", NULL);
+       for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
+               // Open file and deserialize
+               std::ifstream is(path.c_str(), std::ios_base::binary);
+               if (!is.good())
+                       continue;
+
+               temp_player.deSerialize(is, path, NULL);
+               is.close();
+
+               if (temp_player.getName() == name) {
+                       fs::DeleteSingleFileOrEmptyDirectory(path);
+                       return true;
+               }
+
+               path = players_path + name + itos(i);
+       }
+
+       return false;
+}
+
+bool PlayerDatabaseFiles::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
+{
+       std::string players_path = m_savedir + DIR_DELIM;
+       std::string path = players_path + player->getName();
+
+       const std::string player_to_load = player->getName();
+       for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) {
+               // Open file and deserialize
+               std::ifstream is(path.c_str(), std::ios_base::binary);
+               if (!is.good())
+                       continue;
+
+               player->deSerialize(is, path, sao);
+               is.close();
+
+               if (player->getName() == player_to_load)
+                       return true;
+
+               path = players_path + player_to_load + itos(i);
+       }
+
+       infostream << "Player file for player " << player_to_load << " not found" << std::endl;
+       return false;
+}
+
+void PlayerDatabaseFiles::listPlayers(std::vector<std::string> &res)
+{
+       std::vector<fs::DirListNode> files = fs::GetDirListing(m_savedir);
+       // list files into players directory
+       for (std::vector<fs::DirListNode>::const_iterator it = files.begin(); it !=
+               files.end(); ++it) {
+               // Ignore directories
+               if (it->dir)
+                       continue;
+
+               const std::string &filename = it->name;
+               std::string full_path = m_savedir + DIR_DELIM + filename;
+               std::ifstream is(full_path.c_str(), std::ios_base::binary);
+               if (!is.good())
+                       continue;
+
+               RemotePlayer player(filename.c_str(), NULL);
+               // Null env & dummy peer_id
+               PlayerSAO playerSAO(NULL, &player, 15789, false);
+
+               player.deSerialize(is, "", &playerSAO);
+               is.close();
+
+               res.emplace_back(player.getName());
+       }
+}
diff --git a/src/database/database-files.h b/src/database/database-files.h
new file mode 100644 (file)
index 0000000..f0824a3
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+Minetest
+Copyright (C) 2017 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+// !!! WARNING !!!
+// This backend is intended to be used on Minetest 0.4.16 only for the transition backend
+// for player files
+
+#include "database.h"
+
+class PlayerDatabaseFiles : public PlayerDatabase
+{
+public:
+       PlayerDatabaseFiles(const std::string &savedir) : m_savedir(savedir) {}
+       virtual ~PlayerDatabaseFiles() = default;
+
+       void savePlayer(RemotePlayer *player);
+       bool loadPlayer(RemotePlayer *player, PlayerSAO *sao);
+       bool removePlayer(const std::string &name);
+       void listPlayers(std::vector<std::string> &res);
+
+private:
+       void serialize(std::ostringstream &os, RemotePlayer *player);
+
+       std::string m_savedir;
+};
diff --git a/src/database/database-leveldb.cpp b/src/database/database-leveldb.cpp
new file mode 100644 (file)
index 0000000..4a4904c
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "config.h"
+
+#if USE_LEVELDB
+
+#include "database-leveldb.h"
+
+#include "log.h"
+#include "filesys.h"
+#include "exceptions.h"
+#include "util/string.h"
+
+#include "leveldb/db.h"
+
+
+#define ENSURE_STATUS_OK(s) \
+       if (!(s).ok()) { \
+               throw DatabaseException(std::string("LevelDB error: ") + \
+                               (s).ToString()); \
+       }
+
+
+Database_LevelDB::Database_LevelDB(const std::string &savedir)
+{
+       leveldb::Options options;
+       options.create_if_missing = true;
+       leveldb::Status status = leveldb::DB::Open(options,
+               savedir + DIR_DELIM + "map.db", &m_database);
+       ENSURE_STATUS_OK(status);
+}
+
+Database_LevelDB::~Database_LevelDB()
+{
+       delete m_database;
+}
+
+bool Database_LevelDB::saveBlock(const v3s16 &pos, const std::string &data)
+{
+       leveldb::Status status = m_database->Put(leveldb::WriteOptions(),
+                       i64tos(getBlockAsInteger(pos)), data);
+       if (!status.ok()) {
+               warningstream << "saveBlock: LevelDB error saving block "
+                       << PP(pos) << ": " << status.ToString() << std::endl;
+               return false;
+       }
+
+       return true;
+}
+
+void Database_LevelDB::loadBlock(const v3s16 &pos, std::string *block)
+{
+       std::string datastr;
+       leveldb::Status status = m_database->Get(leveldb::ReadOptions(),
+               i64tos(getBlockAsInteger(pos)), &datastr);
+
+       *block = (status.ok()) ? datastr : "";
+}
+
+bool Database_LevelDB::deleteBlock(const v3s16 &pos)
+{
+       leveldb::Status status = m_database->Delete(leveldb::WriteOptions(),
+                       i64tos(getBlockAsInteger(pos)));
+       if (!status.ok()) {
+               warningstream << "deleteBlock: LevelDB error deleting block "
+                       << PP(pos) << ": " << status.ToString() << std::endl;
+               return false;
+       }
+
+       return true;
+}
+
+void Database_LevelDB::listAllLoadableBlocks(std::vector<v3s16> &dst)
+{
+       leveldb::Iterator* it = m_database->NewIterator(leveldb::ReadOptions());
+       for (it->SeekToFirst(); it->Valid(); it->Next()) {
+               dst.push_back(getIntegerAsBlock(stoi64(it->key().ToString())));
+       }
+       ENSURE_STATUS_OK(it->status());  // Check for any errors found during the scan
+       delete it;
+}
+
+#endif // USE_LEVELDB
+
diff --git a/src/database/database-leveldb.h b/src/database/database-leveldb.h
new file mode 100644 (file)
index 0000000..d30f9f8
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "config.h"
+
+#if USE_LEVELDB
+
+#include <string>
+#include "database.h"
+#include "leveldb/db.h"
+
+class Database_LevelDB : public MapDatabase
+{
+public:
+       Database_LevelDB(const std::string &savedir);
+       ~Database_LevelDB();
+
+       bool saveBlock(const v3s16 &pos, const std::string &data);
+       void loadBlock(const v3s16 &pos, std::string *block);
+       bool deleteBlock(const v3s16 &pos);
+       void listAllLoadableBlocks(std::vector<v3s16> &dst);
+
+       void beginSave() {}
+       void endSave() {}
+
+private:
+       leveldb::DB *m_database;
+};
+
+#endif // USE_LEVELDB
diff --git a/src/database/database-postgresql.cpp b/src/database/database-postgresql.cpp
new file mode 100644 (file)
index 0000000..7465113
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+Copyright (C) 2016 Loic Blot <loic.blot@unix-experience.fr>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "config.h"
+
+#if USE_POSTGRESQL
+
+#include "database-postgresql.h"
+
+#ifdef _WIN32
+        // Without this some of the network functions are not found on mingw
+        #ifndef _WIN32_WINNT
+                #define _WIN32_WINNT 0x0501
+        #endif
+        #include <windows.h>
+        #include <winsock2.h>
+#else
+#include <netinet/in.h>
+#endif
+
+#include "debug.h"
+#include "exceptions.h"
+#include "settings.h"
+#include "content_sao.h"
+#include "remoteplayer.h"
+
+Database_PostgreSQL::Database_PostgreSQL(const std::string &connect_string) :
+       m_connect_string(connect_string)
+{
+       if (m_connect_string.empty()) {
+               throw SettingNotFoundException(
+                       "Set pgsql_connection string in world.mt to "
+                       "use the postgresql backend\n"
+                       "Notes:\n"
+                       "pgsql_connection has the following form: \n"
+                       "\tpgsql_connection = host=127.0.0.1 port=5432 user=mt_user "
+                       "password=mt_password dbname=minetest_world\n"
+                       "mt_user should have CREATE TABLE, INSERT, SELECT, UPDATE and "
+                       "DELETE rights on the database.\n"
+                       "Don't create mt_user as a SUPERUSER!");
+       }
+}
+
+Database_PostgreSQL::~Database_PostgreSQL()
+{
+       PQfinish(m_conn);
+}
+
+void Database_PostgreSQL::connectToDatabase()
+{
+       m_conn = PQconnectdb(m_connect_string.c_str());
+
+       if (PQstatus(m_conn) != CONNECTION_OK) {
+               throw DatabaseException(std::string(
+                       "PostgreSQL database error: ") +
+                       PQerrorMessage(m_conn));
+       }
+
+       m_pgversion = PQserverVersion(m_conn);
+
+       /*
+       * We are using UPSERT feature from PostgreSQL 9.5
+       * to have the better performance where possible.
+       */
+       if (m_pgversion < 90500) {
+               warningstream << "Your PostgreSQL server lacks UPSERT "
+                       << "support. Use version 9.5 or better if possible."
+                       << std::endl;
+       }
+
+       infostream << "PostgreSQL Database: Version " << m_pgversion
+                       << " Connection made." << std::endl;
+
+       createDatabase();
+       initStatements();
+}
+
+void Database_PostgreSQL::verifyDatabase()
+{
+       if (PQstatus(m_conn) == CONNECTION_OK)
+               return;
+
+       PQreset(m_conn);
+       ping();
+}
+
+void Database_PostgreSQL::ping()
+{
+       if (PQping(m_connect_string.c_str()) != PQPING_OK) {
+               throw DatabaseException(std::string(
+                       "PostgreSQL database error: ") +
+                       PQerrorMessage(m_conn));
+       }
+}
+
+bool Database_PostgreSQL::initialized() const
+{
+       return (PQstatus(m_conn) == CONNECTION_OK);
+}
+
+PGresult *Database_PostgreSQL::checkResults(PGresult *result, bool clear)
+{
+       ExecStatusType statusType = PQresultStatus(result);
+
+       switch (statusType) {
+       case PGRES_COMMAND_OK:
+       case PGRES_TUPLES_OK:
+               break;
+       case PGRES_FATAL_ERROR:
+       default:
+               throw DatabaseException(
+                       std::string("PostgreSQL database error: ") +
+                       PQresultErrorMessage(result));
+       }
+
+       if (clear)
+               PQclear(result);
+
+       return result;
+}
+
+void Database_PostgreSQL::createTableIfNotExists(const std::string &table_name,
+               const std::string &definition)
+{
+       std::string sql_check_table = "SELECT relname FROM pg_class WHERE relname='" +
+               table_name + "';";
+       PGresult *result = checkResults(PQexec(m_conn, sql_check_table.c_str()), false);
+
+       // If table doesn't exist, create it
+       if (!PQntuples(result)) {
+               checkResults(PQexec(m_conn, definition.c_str()));
+       }
+
+       PQclear(result);
+}
+
+void Database_PostgreSQL::beginSave()
+{
+       verifyDatabase();
+       checkResults(PQexec(m_conn, "BEGIN;"));
+}
+
+void Database_PostgreSQL::endSave()
+{
+       checkResults(PQexec(m_conn, "COMMIT;"));
+}
+
+MapDatabasePostgreSQL::MapDatabasePostgreSQL(const std::string &connect_string):
+       Database_PostgreSQL(connect_string),
+       MapDatabase()
+{
+       connectToDatabase();
+}
+
+
+void MapDatabasePostgreSQL::createDatabase()
+{
+       createTableIfNotExists("blocks",
+               "CREATE TABLE blocks ("
+                       "posX INT NOT NULL,"
+                       "posY INT NOT NULL,"
+                       "posZ INT NOT NULL,"
+                       "data BYTEA,"
+                       "PRIMARY KEY (posX,posY,posZ)"
+                       ");"
+       );
+
+       infostream << "PostgreSQL: Map Database was initialized." << std::endl;
+}
+
+void MapDatabasePostgreSQL::initStatements()
+{
+       prepareStatement("read_block",
+               "SELECT data FROM blocks "
+                       "WHERE posX = $1::int4 AND posY = $2::int4 AND "
+                       "posZ = $3::int4");
+
+       if (getPGVersion() < 90500) {
+               prepareStatement("write_block_insert",
+                       "INSERT INTO blocks (posX, posY, posZ, data) SELECT "
+                               "$1::int4, $2::int4, $3::int4, $4::bytea "
+                               "WHERE NOT EXISTS (SELECT true FROM blocks "
+                               "WHERE posX = $1::int4 AND posY = $2::int4 AND "
+                               "posZ = $3::int4)");
+
+               prepareStatement("write_block_update",
+                       "UPDATE blocks SET data = $4::bytea "
+                               "WHERE posX = $1::int4 AND posY = $2::int4 AND "
+                               "posZ = $3::int4");
+       } else {
+               prepareStatement("write_block",
+                       "INSERT INTO blocks (posX, posY, posZ, data) VALUES "
+                               "($1::int4, $2::int4, $3::int4, $4::bytea) "
+                               "ON CONFLICT ON CONSTRAINT blocks_pkey DO "
+                               "UPDATE SET data = $4::bytea");
+       }
+
+       prepareStatement("delete_block", "DELETE FROM blocks WHERE "
+               "posX = $1::int4 AND posY = $2::int4 AND posZ = $3::int4");
+
+       prepareStatement("list_all_loadable_blocks",
+               "SELECT posX, posY, posZ FROM blocks");
+}
+
+bool MapDatabasePostgreSQL::saveBlock(const v3s16 &pos, const std::string &data)
+{
+       // Verify if we don't overflow the platform integer with the mapblock size
+       if (data.size() > INT_MAX) {
+               errorstream << "Database_PostgreSQL::saveBlock: Data truncation! "
+                       << "data.size() over 0xFFFFFFFF (== " << data.size()
+                       << ")" << std::endl;
+               return false;
+       }
+
+       verifyDatabase();
+
+       s32 x, y, z;
+       x = htonl(pos.X);
+       y = htonl(pos.Y);
+       z = htonl(pos.Z);
+
+       const void *args[] = { &x, &y, &z, data.c_str() };
+       const int argLen[] = {
+               sizeof(x), sizeof(y), sizeof(z), (int)data.size()
+       };
+       const int argFmt[] = { 1, 1, 1, 1 };
+
+       if (getPGVersion() < 90500) {
+               execPrepared("write_block_update", ARRLEN(args), args, argLen, argFmt);
+               execPrepared("write_block_insert", ARRLEN(args), args, argLen, argFmt);
+       } else {
+               execPrepared("write_block", ARRLEN(args), args, argLen, argFmt);
+       }
+       return true;
+}
+
+void MapDatabasePostgreSQL::loadBlock(const v3s16 &pos, std::string *block)
+{
+       verifyDatabase();
+
+       s32 x, y, z;
+       x = htonl(pos.X);
+       y = htonl(pos.Y);
+       z = htonl(pos.Z);
+
+       const void *args[] = { &x, &y, &z };
+       const int argLen[] = { sizeof(x), sizeof(y), sizeof(z) };
+       const int argFmt[] = { 1, 1, 1 };
+
+       PGresult *results = execPrepared("read_block", ARRLEN(args), args,
+               argLen, argFmt, false);
+
+       *block = "";
+
+       if (PQntuples(results))
+               *block = std::string(PQgetvalue(results, 0, 0), PQgetlength(results, 0, 0));
+
+       PQclear(results);
+}
+
+bool MapDatabasePostgreSQL::deleteBlock(const v3s16 &pos)
+{
+       verifyDatabase();
+
+       s32 x, y, z;
+       x = htonl(pos.X);
+       y = htonl(pos.Y);
+       z = htonl(pos.Z);
+
+       const void *args[] = { &x, &y, &z };
+       const int argLen[] = { sizeof(x), sizeof(y), sizeof(z) };
+       const int argFmt[] = { 1, 1, 1 };
+
+       execPrepared("delete_block", ARRLEN(args), args, argLen, argFmt);
+
+       return true;
+}
+
+void MapDatabasePostgreSQL::listAllLoadableBlocks(std::vector<v3s16> &dst)
+{
+       verifyDatabase();
+
+       PGresult *results = execPrepared("list_all_loadable_blocks", 0,
+               NULL, NULL, NULL, false, false);
+
+       int numrows = PQntuples(results);
+
+       for (int row = 0; row < numrows; ++row)
+               dst.push_back(pg_to_v3s16(results, 0, 0));
+
+       PQclear(results);
+}
+
+/*
+ * Player Database
+ */
+PlayerDatabasePostgreSQL::PlayerDatabasePostgreSQL(const std::string &connect_string):
+       Database_PostgreSQL(connect_string),
+       PlayerDatabase()
+{
+       connectToDatabase();
+}
+
+
+void PlayerDatabasePostgreSQL::createDatabase()
+{
+       createTableIfNotExists("player",
+               "CREATE TABLE player ("
+                       "name VARCHAR(60) NOT NULL,"
+                       "pitch NUMERIC(15, 7) NOT NULL,"
+                       "yaw NUMERIC(15, 7) NOT NULL,"
+                       "posX NUMERIC(15, 7) NOT NULL,"
+                       "posY NUMERIC(15, 7) NOT NULL,"
+                       "posZ NUMERIC(15, 7) NOT NULL,"
+                       "hp INT NOT NULL,"
+                       "breath INT NOT NULL,"
+                       "creation_date TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),"
+                       "modification_date TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(),"
+                       "PRIMARY KEY (name)"
+                       ");"
+       );
+
+       createTableIfNotExists("player_inventories",
+               "CREATE TABLE player_inventories ("
+                       "player VARCHAR(60) NOT NULL,"
+                       "inv_id INT NOT NULL,"
+                       "inv_width INT NOT NULL,"
+                       "inv_name TEXT NOT NULL DEFAULT '',"
+                       "inv_size INT NOT NULL,"
+                       "PRIMARY KEY(player, inv_id),"
+                       "CONSTRAINT player_inventories_fkey FOREIGN KEY (player) REFERENCES "
+                       "player (name) ON DELETE CASCADE"
+                       ");"
+       );
+
+       createTableIfNotExists("player_inventory_items",
+               "CREATE TABLE player_inventory_items ("
+                       "player VARCHAR(60) NOT NULL,"
+                       "inv_id INT NOT NULL,"
+                       "slot_id INT NOT NULL,"
+                       "item TEXT NOT NULL DEFAULT '',"
+                       "PRIMARY KEY(player, inv_id, slot_id),"
+                       "CONSTRAINT player_inventory_items_fkey FOREIGN KEY (player) REFERENCES "
+                       "player (name) ON DELETE CASCADE"
+                       ");"
+       );
+
+       createTableIfNotExists("player_metadata",
+               "CREATE TABLE player_metadata ("
+                       "player VARCHAR(60) NOT NULL,"
+                       "attr VARCHAR(256) NOT NULL,"
+                       "value TEXT,"
+                       "PRIMARY KEY(player, attr),"
+                       "CONSTRAINT player_metadata_fkey FOREIGN KEY (player) REFERENCES "
+                       "player (name) ON DELETE CASCADE"
+                       ");"
+       );
+
+       infostream << "PostgreSQL: Player Database was inited." << std::endl;
+}
+
+void PlayerDatabasePostgreSQL::initStatements()
+{
+       if (getPGVersion() < 90500) {
+               prepareStatement("create_player",
+                       "INSERT INTO player(name, pitch, yaw, posX, posY, posZ, hp, breath) VALUES "
+                               "($1, $2, $3, $4, $5, $6, $7::int, $8::int)");
+
+               prepareStatement("update_player",
+                       "UPDATE SET pitch = $2, yaw = $3, posX = $4, posY = $5, posZ = $6, hp = $7::int, "
+                               "breath = $8::int, modification_date = NOW() WHERE name = $1");
+       } else {
+               prepareStatement("save_player",
+                       "INSERT INTO player(name, pitch, yaw, posX, posY, posZ, hp, breath) VALUES "
+                               "($1, $2, $3, $4, $5, $6, $7::int, $8::int)"
+                               "ON CONFLICT ON CONSTRAINT player_pkey DO UPDATE SET pitch = $2, yaw = $3, "
+                               "posX = $4, posY = $5, posZ = $6, hp = $7::int, breath = $8::int, "
+                               "modification_date = NOW()");
+       }
+
+       prepareStatement("remove_player", "DELETE FROM player WHERE name = $1");
+
+       prepareStatement("load_player_list", "SELECT name FROM player");
+
+       prepareStatement("remove_player_inventories",
+               "DELETE FROM player_inventories WHERE player = $1");
+
+       prepareStatement("remove_player_inventory_items",
+               "DELETE FROM player_inventory_items WHERE player = $1");
+
+       prepareStatement("add_player_inventory",
+               "INSERT INTO player_inventories (player, inv_id, inv_width, inv_name, inv_size) VALUES "
+                       "($1, $2::int, $3::int, $4, $5::int)");
+
+       prepareStatement("add_player_inventory_item",
+               "INSERT INTO player_inventory_items (player, inv_id, slot_id, item) VALUES "
+                       "($1, $2::int, $3::int, $4)");
+
+       prepareStatement("load_player_inventories",
+               "SELECT inv_id, inv_width, inv_name, inv_size FROM player_inventories "
+                       "WHERE player = $1 ORDER BY inv_id");
+
+       prepareStatement("load_player_inventory_items",
+               "SELECT slot_id, item FROM player_inventory_items WHERE "
+                       "player = $1 AND inv_id = $2::int");
+
+       prepareStatement("load_player",
+               "SELECT pitch, yaw, posX, posY, posZ, hp, breath FROM player WHERE name = $1");
+
+       prepareStatement("remove_player_metadata",
+               "DELETE FROM player_metadata WHERE player = $1");
+
+       prepareStatement("save_player_metadata",
+               "INSERT INTO player_metadata (player, attr, value) VALUES ($1, $2, $3)");
+
+       prepareStatement("load_player_metadata",
+               "SELECT attr, value FROM player_metadata WHERE player = $1");
+
+}
+
+bool PlayerDatabasePostgreSQL::playerDataExists(const std::string &playername)
+{
+       verifyDatabase();
+
+       const char *values[] = { playername.c_str() };
+       PGresult *results = execPrepared("load_player", 1, values, false);
+
+       bool res = (PQntuples(results) > 0);
+       PQclear(results);
+       return res;
+}
+
+void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player)
+{
+       PlayerSAO* sao = player->getPlayerSAO();
+       if (!sao)
+               return;
+
+       verifyDatabase();
+
+       v3f pos = sao->getBasePosition();
+       std::string pitch = ftos(sao->getPitch());
+       std::string yaw = ftos(sao->getYaw());
+       std::string posx = ftos(pos.X);
+       std::string posy = ftos(pos.Y);
+       std::string posz = ftos(pos.Z);
+       std::string hp = itos(sao->getHP());
+       std::string breath = itos(sao->getBreath());
+       const char *values[] = {
+               player->getName(),
+               pitch.c_str(),
+               yaw.c_str(),
+               posx.c_str(), posy.c_str(), posz.c_str(),
+               hp.c_str(),
+               breath.c_str()
+       };
+
+       const char* rmvalues[] = { player->getName() };
+       beginSave();
+
+       if (getPGVersion() < 90500) {
+               if (!playerDataExists(player->getName()))
+                       execPrepared("create_player", 8, values, true, false);
+               else
+                       execPrepared("update_player", 8, values, true, false);
+       }
+       else
+               execPrepared("save_player", 8, values, true, false);
+
+       // Write player inventories
+       execPrepared("remove_player_inventories", 1, rmvalues);
+       execPrepared("remove_player_inventory_items", 1, rmvalues);
+
+       std::vector<const InventoryList*> inventory_lists = sao->getInventory()->getLists();
+       for (u16 i = 0; i < inventory_lists.size(); i++) {
+               const InventoryList* list = inventory_lists[i];
+               const std::string &name = list->getName();
+               std::string width = itos(list->getWidth()),
+                       inv_id = itos(i), lsize = itos(list->getSize());
+
+               const char* inv_values[] = {
+                       player->getName(),
+                       inv_id.c_str(),
+                       width.c_str(),
+                       name.c_str(),
+                       lsize.c_str()
+               };
+               execPrepared("add_player_inventory", 5, inv_values);
+
+               for (u32 j = 0; j < list->getSize(); j++) {
+                       std::ostringstream os;
+                       list->getItem(j).serialize(os);
+                       std::string itemStr = os.str(), slotId = itos(j);
+
+                       const char* invitem_values[] = {
+                               player->getName(),
+                               inv_id.c_str(),
+                               slotId.c_str(),
+                               itemStr.c_str()
+                       };
+                       execPrepared("add_player_inventory_item", 4, invitem_values);
+               }
+       }
+
+       execPrepared("remove_player_metadata", 1, rmvalues);
+       const PlayerAttributes &attrs = sao->getExtendedAttributes();
+       for (const auto &attr : attrs) {
+               const char *meta_values[] = {
+                       player->getName(),
+                       attr.first.c_str(),
+                       attr.second.c_str()
+               };
+               execPrepared("save_player_metadata", 3, meta_values);
+       }
+       endSave();
+}
+
+bool PlayerDatabasePostgreSQL::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
+{
+       sanity_check(sao);
+       verifyDatabase();
+
+       const char *values[] = { player->getName() };
+       PGresult *results = execPrepared("load_player", 1, values, false, false);
+
+       // Player not found, return not found
+       if (!PQntuples(results)) {
+               PQclear(results);
+               return false;
+       }
+
+       sao->setPitch(pg_to_float(results, 0, 0));
+       sao->setYaw(pg_to_float(results, 0, 1));
+       sao->setBasePosition(v3f(
+               pg_to_float(results, 0, 2),
+               pg_to_float(results, 0, 3),
+               pg_to_float(results, 0, 4))
+       );
+       sao->setHPRaw((s16) pg_to_int(results, 0, 5));
+       sao->setBreath((u16) pg_to_int(results, 0, 6), false);
+
+       PQclear(results);
+
+       // Load inventory
+       results = execPrepared("load_player_inventories", 1, values, false, false);
+
+       int resultCount = PQntuples(results);
+
+       for (int row = 0; row < resultCount; ++row) {
+               InventoryList* invList = player->inventory.
+                       addList(PQgetvalue(results, row, 2), pg_to_uint(results, row, 3));
+               invList->setWidth(pg_to_uint(results, row, 1));
+
+               u32 invId = pg_to_uint(results, row, 0);
+               std::string invIdStr = itos(invId);
+
+               const char* values2[] = {
+                       player->getName(),
+                       invIdStr.c_str()
+               };
+               PGresult *results2 = execPrepared("load_player_inventory_items", 2,
+                       values2, false, false);
+
+               int resultCount2 = PQntuples(results2);
+               for (int row2 = 0; row2 < resultCount2; row2++) {
+                       const std::string itemStr = PQgetvalue(results2, row2, 1);
+                       if (itemStr.length() > 0) {
+                               ItemStack stack;
+                               stack.deSerialize(itemStr);
+                               invList->changeItem(pg_to_uint(results2, row2, 0), stack);
+                       }
+               }
+               PQclear(results2);
+       }
+
+       PQclear(results);
+
+       results = execPrepared("load_player_metadata", 1, values, false);
+
+       int numrows = PQntuples(results);
+       for (int row = 0; row < numrows; row++) {
+               sao->setExtendedAttribute(PQgetvalue(results, row, 0),PQgetvalue(results, row, 1));
+       }
+
+       PQclear(results);
+
+       return true;
+}
+
+bool PlayerDatabasePostgreSQL::removePlayer(const std::string &name)
+{
+       if (!playerDataExists(name))
+               return false;
+
+       verifyDatabase();
+
+       const char *values[] = { name.c_str() };
+       execPrepared("remove_player", 1, values);
+
+       return true;
+}
+
+void PlayerDatabasePostgreSQL::listPlayers(std::vector<std::string> &res)
+{
+       verifyDatabase();
+
+       PGresult *results = execPrepared("load_player_list", 0, NULL, false);
+
+       int numrows = PQntuples(results);
+       for (int row = 0; row < numrows; row++)
+               res.emplace_back(PQgetvalue(results, row, 0));
+
+       PQclear(results);
+}
+
+#endif // USE_POSTGRESQL
diff --git a/src/database/database-postgresql.h b/src/database/database-postgresql.h
new file mode 100644 (file)
index 0000000..db0b505
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include <string>
+#include <libpq-fe.h>
+#include "database.h"
+#include "util/basic_macros.h"
+
+class Settings;
+
+class Database_PostgreSQL: public Database
+{
+public:
+       Database_PostgreSQL(const std::string &connect_string);
+       ~Database_PostgreSQL();
+
+       void beginSave();
+       void endSave();
+
+       bool initialized() const;
+
+
+protected:
+       // Conversion helpers
+       inline int pg_to_int(PGresult *res, int row, int col)
+       {
+               return atoi(PQgetvalue(res, row, col));
+       }
+
+       inline u32 pg_to_uint(PGresult *res, int row, int col)
+       {
+               return (u32) atoi(PQgetvalue(res, row, col));
+       }
+
+       inline float pg_to_float(PGresult *res, int row, int col)
+       {
+               return (float) atof(PQgetvalue(res, row, col));
+       }
+
+       inline v3s16 pg_to_v3s16(PGresult *res, int row, int col)
+       {
+               return v3s16(
+                       pg_to_int(res, row, col),
+                       pg_to_int(res, row, col + 1),
+                       pg_to_int(res, row, col + 2)
+               );
+       }
+
+       inline PGresult *execPrepared(const char *stmtName, const int paramsNumber,
+               const void **params,
+               const int *paramsLengths = NULL, const int *paramsFormats = NULL,
+               bool clear = true, bool nobinary = true)
+       {
+               return checkResults(PQexecPrepared(m_conn, stmtName, paramsNumber,
+                       (const char* const*) params, paramsLengths, paramsFormats,
+                       nobinary ? 1 : 0), clear);
+       }
+
+       inline PGresult *execPrepared(const char *stmtName, const int paramsNumber,
+               const char **params, bool clear = true, bool nobinary = true)
+       {
+               return execPrepared(stmtName, paramsNumber,
+                       (const void **)params, NULL, NULL, clear, nobinary);
+       }
+
+       void createTableIfNotExists(const std::string &table_name, const std::string &definition);
+       void verifyDatabase();
+
+       // Database initialization
+       void connectToDatabase();
+       virtual void createDatabase() = 0;
+       virtual void initStatements() = 0;
+       inline void prepareStatement(const std::string &name, const std::string &sql)
+       {
+               checkResults(PQprepare(m_conn, name.c_str(), sql.c_str(), 0, NULL));
+       }
+
+       const int getPGVersion() const { return m_pgversion; }
+private:
+       // Database connectivity checks
+       void ping();
+
+       // Database usage
+       PGresult *checkResults(PGresult *res, bool clear = true);
+
+       // Attributes
+       std::string m_connect_string;
+       PGconn *m_conn = nullptr;
+       int m_pgversion = 0;
+};
+
+class MapDatabasePostgreSQL : private Database_PostgreSQL, public MapDatabase
+{
+public:
+       MapDatabasePostgreSQL(const std::string &connect_string);
+       virtual ~MapDatabasePostgreSQL() = default;
+
+       bool saveBlock(const v3s16 &pos, const std::string &data);
+       void loadBlock(const v3s16 &pos, std::string *block);
+       bool deleteBlock(const v3s16 &pos);
+       void listAllLoadableBlocks(std::vector<v3s16> &dst);
+
+       void beginSave() { Database_PostgreSQL::beginSave(); }
+       void endSave() { Database_PostgreSQL::endSave(); }
+
+protected:
+       virtual void createDatabase();
+       virtual void initStatements();
+};
+
+class PlayerDatabasePostgreSQL : private Database_PostgreSQL, public PlayerDatabase
+{
+public:
+       PlayerDatabasePostgreSQL(const std::string &connect_string);
+       virtual ~PlayerDatabasePostgreSQL() = default;
+
+       void savePlayer(RemotePlayer *player);
+       bool loadPlayer(RemotePlayer *player, PlayerSAO *sao);
+       bool removePlayer(const std::string &name);
+       void listPlayers(std::vector<std::string> &res);
+
+protected:
+       virtual void createDatabase();
+       virtual void initStatements();
+
+private:
+       bool playerDataExists(const std::string &playername);
+};
diff --git a/src/database/database-redis.cpp b/src/database/database-redis.cpp
new file mode 100644 (file)
index 0000000..096ea50
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+Minetest
+Copyright (C) 2014 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "config.h"
+
+#if USE_REDIS
+
+#include "database-redis.h"
+
+#include "settings.h"
+#include "log.h"
+#include "exceptions.h"
+#include "util/string.h"
+
+#include <hiredis.h>
+#include <cassert>
+
+
+Database_Redis::Database_Redis(Settings &conf)
+{
+       std::string tmp;
+       try {
+               tmp = conf.get("redis_address");
+               hash = conf.get("redis_hash");
+       } catch (SettingNotFoundException &) {
+               throw SettingNotFoundException("Set redis_address and "
+                       "redis_hash in world.mt to use the redis backend");
+       }
+       const char *addr = tmp.c_str();
+       int port = conf.exists("redis_port") ? conf.getU16("redis_port") : 6379;
+       // if redis_address contains '/' assume unix socket, else hostname/ip
+       ctx = tmp.find('/') != std::string::npos ? redisConnectUnix(addr) : redisConnect(addr, port);
+       if (!ctx) {
+               throw DatabaseException("Cannot allocate redis context");
+       } else if (ctx->err) {
+               std::string err = std::string("Connection error: ") + ctx->errstr;
+               redisFree(ctx);
+               throw DatabaseException(err);
+       }
+       if (conf.exists("redis_password")) {
+               tmp = conf.get("redis_password");
+               redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "AUTH %s", tmp.c_str()));
+               if (!reply)
+                       throw DatabaseException("Redis authentication failed");
+               if (reply->type == REDIS_REPLY_ERROR) {
+                       std::string err = "Redis authentication failed: " + std::string(reply->str, reply->len);
+                       freeReplyObject(reply);
+                       throw DatabaseException(err);
+               }
+               freeReplyObject(reply);
+       }
+}
+
+Database_Redis::~Database_Redis()
+{
+       redisFree(ctx);
+}
+
+void Database_Redis::beginSave() {
+       redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "MULTI"));
+       if (!reply) {
+               throw DatabaseException(std::string(
+                       "Redis command 'MULTI' failed: ") + ctx->errstr);
+       }
+       freeReplyObject(reply);
+}
+
+void Database_Redis::endSave() {
+       redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "EXEC"));
+       if (!reply) {
+               throw DatabaseException(std::string(
+                       "Redis command 'EXEC' failed: ") + ctx->errstr);
+       }
+       freeReplyObject(reply);
+}
+
+bool Database_Redis::saveBlock(const v3s16 &pos, const std::string &data)
+{
+       std::string tmp = i64tos(getBlockAsInteger(pos));
+
+       redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "HSET %s %s %b",
+                       hash.c_str(), tmp.c_str(), data.c_str(), data.size()));
+       if (!reply) {
+               warningstream << "saveBlock: redis command 'HSET' failed on "
+                       "block " << PP(pos) << ": " << ctx->errstr << std::endl;
+               freeReplyObject(reply);
+               return false;
+       }
+
+       if (reply->type == REDIS_REPLY_ERROR) {
+               warningstream << "saveBlock: saving block " << PP(pos)
+                       << " failed: " << std::string(reply->str, reply->len) << std::endl;
+               freeReplyObject(reply);
+               return false;
+       }
+
+       freeReplyObject(reply);
+       return true;
+}
+
+void Database_Redis::loadBlock(const v3s16 &pos, std::string *block)
+{
+       std::string tmp = i64tos(getBlockAsInteger(pos));
+       redisReply *reply = static_cast<redisReply *>(redisCommand(ctx,
+                       "HGET %s %s", hash.c_str(), tmp.c_str()));
+
+       if (!reply) {
+               throw DatabaseException(std::string(
+                       "Redis command 'HGET %s %s' failed: ") + ctx->errstr);
+       }
+
+       switch (reply->type) {
+       case REDIS_REPLY_STRING: {
+               *block = std::string(reply->str, reply->len);
+               // std::string copies the memory so this won't cause any problems
+               freeReplyObject(reply);
+               return;
+       }
+       case REDIS_REPLY_ERROR: {
+               std::string errstr(reply->str, reply->len);
+               freeReplyObject(reply);
+               errorstream << "loadBlock: loading block " << PP(pos)
+                       << " failed: " << errstr << std::endl;
+               throw DatabaseException(std::string(
+                       "Redis command 'HGET %s %s' errored: ") + errstr);
+       }
+       case REDIS_REPLY_NIL: {
+               *block = "";
+               // block not found in database
+               freeReplyObject(reply);
+               return;
+       }
+       }
+
+       errorstream << "loadBlock: loading block " << PP(pos)
+               << " returned invalid reply type " << reply->type
+               << ": " << std::string(reply->str, reply->len) << std::endl;
+       freeReplyObject(reply);
+       throw DatabaseException(std::string(
+               "Redis command 'HGET %s %s' gave invalid reply."));
+}
+
+bool Database_Redis::deleteBlock(const v3s16 &pos)
+{
+       std::string tmp = i64tos(getBlockAsInteger(pos));
+
+       redisReply *reply = static_cast<redisReply *>(redisCommand(ctx,
+               "HDEL %s %s", hash.c_str(), tmp.c_str()));
+       if (!reply) {
+               throw DatabaseException(std::string(
+                       "Redis command 'HDEL %s %s' failed: ") + ctx->errstr);
+       } else if (reply->type == REDIS_REPLY_ERROR) {
+               warningstream << "deleteBlock: deleting block " << PP(pos)
+                       << " failed: " << std::string(reply->str, reply->len) << std::endl;
+               freeReplyObject(reply);
+               return false;
+       }
+
+       freeReplyObject(reply);
+       return true;
+}
+
+void Database_Redis::listAllLoadableBlocks(std::vector<v3s16> &dst)
+{
+       redisReply *reply = static_cast<redisReply *>(redisCommand(ctx, "HKEYS %s", hash.c_str()));
+       if (!reply) {
+               throw DatabaseException(std::string(
+                       "Redis command 'HKEYS %s' failed: ") + ctx->errstr);
+       }
+       switch (reply->type) {
+       case REDIS_REPLY_ARRAY:
+               dst.reserve(reply->elements);
+               for (size_t i = 0; i < reply->elements; i++) {
+                       assert(reply->element[i]->type == REDIS_REPLY_STRING);
+                       dst.push_back(getIntegerAsBlock(stoi64(reply->element[i]->str)));
+               }
+               break;
+       case REDIS_REPLY_ERROR:
+               throw DatabaseException(std::string(
+                       "Failed to get keys from database: ") +
+                       std::string(reply->str, reply->len));
+       }
+       freeReplyObject(reply);
+}
+
+#endif // USE_REDIS
+
diff --git a/src/database/database-redis.h b/src/database/database-redis.h
new file mode 100644 (file)
index 0000000..6bea563
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+Minetest
+Copyright (C) 2014 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "config.h"
+
+#if USE_REDIS
+
+#include <hiredis.h>
+#include <string>
+#include "database.h"
+
+class Settings;
+
+class Database_Redis : public MapDatabase
+{
+public:
+       Database_Redis(Settings &conf);
+       ~Database_Redis();
+
+       void beginSave();
+       void endSave();
+
+       bool saveBlock(const v3s16 &pos, const std::string &data);
+       void loadBlock(const v3s16 &pos, std::string *block);
+       bool deleteBlock(const v3s16 &pos);
+       void listAllLoadableBlocks(std::vector<v3s16> &dst);
+
+private:
+       redisContext *ctx = nullptr;
+       std::string hash = "";
+};
+
+#endif // USE_REDIS
diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp
new file mode 100644 (file)
index 0000000..78c182f
--- /dev/null
@@ -0,0 +1,606 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+/*
+SQLite format specification:
+       blocks:
+               (PK) INT id
+               BLOB data
+*/
+
+
+#include "database-sqlite3.h"
+
+#include "log.h"
+#include "filesys.h"
+#include "exceptions.h"
+#include "settings.h"
+#include "porting.h"
+#include "util/string.h"
+#include "content_sao.h"
+#include "remoteplayer.h"
+
+#include <cassert>
+
+// When to print messages when the database is being held locked by another process
+// Note: I've seen occasional delays of over 250ms while running minetestmapper.
+#define BUSY_INFO_TRESHOLD     100     // Print first informational message after 100ms.
+#define BUSY_WARNING_TRESHOLD  250     // Print warning message after 250ms. Lag is increased.
+#define BUSY_ERROR_TRESHOLD    1000    // Print error message after 1000ms. Significant lag.
+#define BUSY_FATAL_TRESHOLD    3000    // Allow SQLITE_BUSY to be returned, which will cause a minetest crash.
+#define BUSY_ERROR_INTERVAL    10000   // Safety net: report again every 10 seconds
+
+
+#define SQLRES(s, r, m) \
+       if ((s) != (r)) { \
+               throw DatabaseException(std::string(m) + ": " +\
+                               sqlite3_errmsg(m_database)); \
+       }
+#define SQLOK(s, m) SQLRES(s, SQLITE_OK, m)
+
+#define PREPARE_STATEMENT(name, query) \
+       SQLOK(sqlite3_prepare_v2(m_database, query, -1, &m_stmt_##name, NULL),\
+               "Failed to prepare query '" query "'")
+
+#define SQLOK_ERRSTREAM(s, m)                           \
+       if ((s) != SQLITE_OK) {                             \
+               errorstream << (m) << ": "                      \
+                       << sqlite3_errmsg(m_database) << std::endl; \
+       }
+
+#define FINALIZE_STATEMENT(statement) SQLOK_ERRSTREAM(sqlite3_finalize(statement), \
+       "Failed to finalize " #statement)
+
+int Database_SQLite3::busyHandler(void *data, int count)
+{
+       s64 &first_time = reinterpret_cast<s64 *>(data)[0];
+       s64 &prev_time = reinterpret_cast<s64 *>(data)[1];
+       s64 cur_time = porting::getTimeMs();
+
+       if (count == 0) {
+               first_time = cur_time;
+               prev_time = first_time;
+       } else {
+               while (cur_time < prev_time)
+                       cur_time += s64(1)<<32;
+       }
+
+       if (cur_time - first_time < BUSY_INFO_TRESHOLD) {
+               ; // do nothing
+       } else if (cur_time - first_time >= BUSY_INFO_TRESHOLD &&
+                       prev_time - first_time < BUSY_INFO_TRESHOLD) {
+               infostream << "SQLite3 database has been locked for "
+                       << cur_time - first_time << " ms." << std::endl;
+       } else if (cur_time - first_time >= BUSY_WARNING_TRESHOLD &&
+                       prev_time - first_time < BUSY_WARNING_TRESHOLD) {
+               warningstream << "SQLite3 database has been locked for "
+                       << cur_time - first_time << " ms." << std::endl;
+       } else if (cur_time - first_time >= BUSY_ERROR_TRESHOLD &&
+                       prev_time - first_time < BUSY_ERROR_TRESHOLD) {
+               errorstream << "SQLite3 database has been locked for "
+                       << cur_time - first_time << " ms; this causes lag." << std::endl;
+       } else if (cur_time - first_time >= BUSY_FATAL_TRESHOLD &&
+                       prev_time - first_time < BUSY_FATAL_TRESHOLD) {
+               errorstream << "SQLite3 database has been locked for "
+                       << cur_time - first_time << " ms - giving up!" << std::endl;
+       } else if ((cur_time - first_time) / BUSY_ERROR_INTERVAL !=
+                       (prev_time - first_time) / BUSY_ERROR_INTERVAL) {
+               // Safety net: keep reporting every BUSY_ERROR_INTERVAL
+               errorstream << "SQLite3 database has been locked for "
+                       << (cur_time - first_time) / 1000 << " seconds!" << std::endl;
+       }
+
+       prev_time = cur_time;
+
+       // Make sqlite transaction fail if delay exceeds BUSY_FATAL_TRESHOLD
+       return cur_time - first_time < BUSY_FATAL_TRESHOLD;
+}
+
+
+Database_SQLite3::Database_SQLite3(const std::string &savedir, const std::string &dbname) :
+       m_savedir(savedir),
+       m_dbname(dbname)
+{
+}
+
+void Database_SQLite3::beginSave()
+{
+       verifyDatabase();
+       SQLRES(sqlite3_step(m_stmt_begin), SQLITE_DONE,
+               "Failed to start SQLite3 transaction");
+       sqlite3_reset(m_stmt_begin);
+}
+
+void Database_SQLite3::endSave()
+{
+       verifyDatabase();
+       SQLRES(sqlite3_step(m_stmt_end), SQLITE_DONE,
+               "Failed to commit SQLite3 transaction");
+       sqlite3_reset(m_stmt_end);
+}
+
+void Database_SQLite3::openDatabase()
+{
+       if (m_database) return;
+
+       std::string dbp = m_savedir + DIR_DELIM + m_dbname + ".sqlite";
+
+       // Open the database connection
+
+       if (!fs::CreateAllDirs(m_savedir)) {
+               infostream << "Database_SQLite3: Failed to create directory \""
+                       << m_savedir << "\"" << std::endl;
+               throw FileNotGoodException("Failed to create database "
+                               "save directory");
+       }
+
+       bool needs_create = !fs::PathExists(dbp);
+
+       SQLOK(sqlite3_open_v2(dbp.c_str(), &m_database,
+                       SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL),
+               std::string("Failed to open SQLite3 database file ") + dbp);
+
+       SQLOK(sqlite3_busy_handler(m_database, Database_SQLite3::busyHandler,
+               m_busy_handler_data), "Failed to set SQLite3 busy handler");
+
+       if (needs_create) {
+               createDatabase();
+       }
+
+       std::string query_str = std::string("PRAGMA synchronous = ")
+                        + itos(g_settings->getU16("sqlite_synchronous"));
+       SQLOK(sqlite3_exec(m_database, query_str.c_str(), NULL, NULL, NULL),
+               "Failed to modify sqlite3 synchronous mode");
+       SQLOK(sqlite3_exec(m_database, "PRAGMA foreign_keys = ON", NULL, NULL, NULL),
+               "Failed to enable sqlite3 foreign key support");
+}
+
+void Database_SQLite3::verifyDatabase()
+{
+       if (m_initialized) return;
+
+       openDatabase();
+
+       PREPARE_STATEMENT(begin, "BEGIN;");
+       PREPARE_STATEMENT(end, "COMMIT;");
+
+       initStatements();
+
+       m_initialized = true;
+}
+
+Database_SQLite3::~Database_SQLite3()
+{
+       FINALIZE_STATEMENT(m_stmt_begin)
+       FINALIZE_STATEMENT(m_stmt_end)
+
+       SQLOK_ERRSTREAM(sqlite3_close(m_database), "Failed to close database");
+}
+
+/*
+ * Map database
+ */
+
+MapDatabaseSQLite3::MapDatabaseSQLite3(const std::string &savedir):
+       Database_SQLite3(savedir, "map"),
+       MapDatabase()
+{
+}
+
+MapDatabaseSQLite3::~MapDatabaseSQLite3()
+{
+       FINALIZE_STATEMENT(m_stmt_read)
+       FINALIZE_STATEMENT(m_stmt_write)
+       FINALIZE_STATEMENT(m_stmt_list)
+       FINALIZE_STATEMENT(m_stmt_delete)
+}
+
+
+void MapDatabaseSQLite3::createDatabase()
+{
+       assert(m_database); // Pre-condition
+
+       SQLOK(sqlite3_exec(m_database,
+               "CREATE TABLE IF NOT EXISTS `blocks` (\n"
+                       "       `pos` INT PRIMARY KEY,\n"
+                       "       `data` BLOB\n"
+                       ");\n",
+               NULL, NULL, NULL),
+               "Failed to create database table");
+}
+
+void MapDatabaseSQLite3::initStatements()
+{
+       PREPARE_STATEMENT(read, "SELECT `data` FROM `blocks` WHERE `pos` = ? LIMIT 1");
+#ifdef __ANDROID__
+       PREPARE_STATEMENT(write,  "INSERT INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
+#else
+       PREPARE_STATEMENT(write, "REPLACE INTO `blocks` (`pos`, `data`) VALUES (?, ?)");
+#endif
+       PREPARE_STATEMENT(delete, "DELETE FROM `blocks` WHERE `pos` = ?");
+       PREPARE_STATEMENT(list, "SELECT `pos` FROM `blocks`");
+
+       verbosestream << "ServerMap: SQLite3 database opened." << std::endl;
+}
+
+inline void MapDatabaseSQLite3::bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index)
+{
+       SQLOK(sqlite3_bind_int64(stmt, index, getBlockAsInteger(pos)),
+               "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
+}
+
+bool MapDatabaseSQLite3::deleteBlock(const v3s16 &pos)
+{
+       verifyDatabase();
+
+       bindPos(m_stmt_delete, pos);
+
+       bool good = sqlite3_step(m_stmt_delete) == SQLITE_DONE;
+       sqlite3_reset(m_stmt_delete);
+
+       if (!good) {
+               warningstream << "deleteBlock: Block failed to delete "
+                       << PP(pos) << ": " << sqlite3_errmsg(m_database) << std::endl;
+       }
+       return good;
+}
+
+bool MapDatabaseSQLite3::saveBlock(const v3s16 &pos, const std::string &data)
+{
+       verifyDatabase();
+
+#ifdef __ANDROID__
+       /**
+        * Note: For some unknown reason SQLite3 fails to REPLACE blocks on Android,
+        * deleting them and then inserting works.
+        */
+       bindPos(m_stmt_read, pos);
+
+       if (sqlite3_step(m_stmt_read) == SQLITE_ROW) {
+               deleteBlock(pos);
+       }
+       sqlite3_reset(m_stmt_read);
+#endif
+
+       bindPos(m_stmt_write, pos);
+       SQLOK(sqlite3_bind_blob(m_stmt_write, 2, data.data(), data.size(), NULL),
+               "Internal error: failed to bind query at " __FILE__ ":" TOSTRING(__LINE__));
+
+       SQLRES(sqlite3_step(m_stmt_write), SQLITE_DONE, "Failed to save block")
+       sqlite3_reset(m_stmt_write);
+
+       return true;
+}
+
+void MapDatabaseSQLite3::loadBlock(const v3s16 &pos, std::string *block)
+{
+       verifyDatabase();
+
+       bindPos(m_stmt_read, pos);
+
+       if (sqlite3_step(m_stmt_read) != SQLITE_ROW) {
+               sqlite3_reset(m_stmt_read);
+               return;
+       }
+
+       const char *data = (const char *) sqlite3_column_blob(m_stmt_read, 0);
+       size_t len = sqlite3_column_bytes(m_stmt_read, 0);
+
+       *block = (data) ? std::string(data, len) : "";
+
+       sqlite3_step(m_stmt_read);
+       // We should never get more than 1 row, so ok to reset
+       sqlite3_reset(m_stmt_read);
+}
+
+void MapDatabaseSQLite3::listAllLoadableBlocks(std::vector<v3s16> &dst)
+{
+       verifyDatabase();
+
+       while (sqlite3_step(m_stmt_list) == SQLITE_ROW)
+               dst.push_back(getIntegerAsBlock(sqlite3_column_int64(m_stmt_list, 0)));
+
+       sqlite3_reset(m_stmt_list);
+}
+
+/*
+ * Player Database
+ */
+
+PlayerDatabaseSQLite3::PlayerDatabaseSQLite3(const std::string &savedir):
+       Database_SQLite3(savedir, "players"),
+       PlayerDatabase()
+{
+}
+
+PlayerDatabaseSQLite3::~PlayerDatabaseSQLite3()
+{
+       FINALIZE_STATEMENT(m_stmt_player_load)
+       FINALIZE_STATEMENT(m_stmt_player_add)
+       FINALIZE_STATEMENT(m_stmt_player_update)
+       FINALIZE_STATEMENT(m_stmt_player_remove)
+       FINALIZE_STATEMENT(m_stmt_player_list)
+       FINALIZE_STATEMENT(m_stmt_player_add_inventory)
+       FINALIZE_STATEMENT(m_stmt_player_add_inventory_items)
+       FINALIZE_STATEMENT(m_stmt_player_remove_inventory)
+       FINALIZE_STATEMENT(m_stmt_player_remove_inventory_items)
+       FINALIZE_STATEMENT(m_stmt_player_load_inventory)
+       FINALIZE_STATEMENT(m_stmt_player_load_inventory_items)
+       FINALIZE_STATEMENT(m_stmt_player_metadata_load)
+       FINALIZE_STATEMENT(m_stmt_player_metadata_add)
+       FINALIZE_STATEMENT(m_stmt_player_metadata_remove)
+};
+
+
+void PlayerDatabaseSQLite3::createDatabase()
+{
+       assert(m_database); // Pre-condition
+
+       SQLOK(sqlite3_exec(m_database,
+               "CREATE TABLE IF NOT EXISTS `player` ("
+                       "`name` VARCHAR(50) NOT NULL,"
+                       "`pitch` NUMERIC(11, 4) NOT NULL,"
+                       "`yaw` NUMERIC(11, 4) NOT NULL,"
+                       "`posX` NUMERIC(11, 4) NOT NULL,"
+                       "`posY` NUMERIC(11, 4) NOT NULL,"
+                       "`posZ` NUMERIC(11, 4) NOT NULL,"
+                       "`hp` INT NOT NULL,"
+                       "`breath` INT NOT NULL,"
+                       "`creation_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,"
+                       "`modification_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,"
+                       "PRIMARY KEY (`name`));",
+               NULL, NULL, NULL),
+               "Failed to create player table");
+
+       SQLOK(sqlite3_exec(m_database,
+               "CREATE TABLE IF NOT EXISTS `player_metadata` ("
+                       "    `player` VARCHAR(50) NOT NULL,"
+                       "    `metadata` VARCHAR(256) NOT NULL,"
+                       "    `value` TEXT,"
+                       "    PRIMARY KEY(`player`, `metadata`),"
+                       "    FOREIGN KEY (`player`) REFERENCES player (`name`) ON DELETE CASCADE );",
+               NULL, NULL, NULL),
+               "Failed to create player metadata table");
+
+       SQLOK(sqlite3_exec(m_database,
+               "CREATE TABLE IF NOT EXISTS `player_inventories` ("
+                       "   `player` VARCHAR(50) NOT NULL,"
+                       "       `inv_id` INT NOT NULL,"
+                       "       `inv_width` INT NOT NULL,"
+                       "       `inv_name` TEXT NOT NULL DEFAULT '',"
+                       "       `inv_size` INT NOT NULL,"
+                       "       PRIMARY KEY(player, inv_id),"
+                       "   FOREIGN KEY (`player`) REFERENCES player (`name`) ON DELETE CASCADE );",
+               NULL, NULL, NULL),
+               "Failed to create player inventory table");
+
+       SQLOK(sqlite3_exec(m_database,
+               "CREATE TABLE `player_inventory_items` ("
+                       "   `player` VARCHAR(50) NOT NULL,"
+                       "       `inv_id` INT NOT NULL,"
+                       "       `slot_id` INT NOT NULL,"
+                       "       `item` TEXT NOT NULL DEFAULT '',"
+                       "       PRIMARY KEY(player, inv_id, slot_id),"
+                       "   FOREIGN KEY (`player`) REFERENCES player (`name`) ON DELETE CASCADE );",
+               NULL, NULL, NULL),
+               "Failed to create player inventory items table");
+}
+
+void PlayerDatabaseSQLite3::initStatements()
+{
+       PREPARE_STATEMENT(player_load, "SELECT `pitch`, `yaw`, `posX`, `posY`, `posZ`, `hp`, "
+               "`breath`"
+               "FROM `player` WHERE `name` = ?")
+       PREPARE_STATEMENT(player_add, "INSERT INTO `player` (`name`, `pitch`, `yaw`, `posX`, "
+               "`posY`, `posZ`, `hp`, `breath`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)")
+       PREPARE_STATEMENT(player_update, "UPDATE `player` SET `pitch` = ?, `yaw` = ?, "
+                       "`posX` = ?, `posY` = ?, `posZ` = ?, `hp` = ?, `breath` = ?, "
+                       "`modification_date` = CURRENT_TIMESTAMP WHERE `name` = ?")
+       PREPARE_STATEMENT(player_remove, "DELETE FROM `player` WHERE `name` = ?")
+       PREPARE_STATEMENT(player_list, "SELECT `name` FROM `player`")
+
+       PREPARE_STATEMENT(player_add_inventory, "INSERT INTO `player_inventories` "
+               "(`player`, `inv_id`, `inv_width`, `inv_name`, `inv_size`) VALUES (?, ?, ?, ?, ?)")
+       PREPARE_STATEMENT(player_add_inventory_items, "INSERT INTO `player_inventory_items` "
+               "(`player`, `inv_id`, `slot_id`, `item`) VALUES (?, ?, ?, ?)")
+       PREPARE_STATEMENT(player_remove_inventory, "DELETE FROM `player_inventories` "
+               "WHERE `player` = ?")
+       PREPARE_STATEMENT(player_remove_inventory_items, "DELETE FROM `player_inventory_items` "
+               "WHERE `player` = ?")
+       PREPARE_STATEMENT(player_load_inventory, "SELECT `inv_id`, `inv_width`, `inv_name`, "
+               "`inv_size` FROM `player_inventories` WHERE `player` = ? ORDER BY inv_id")
+       PREPARE_STATEMENT(player_load_inventory_items, "SELECT `slot_id`, `item` "
+               "FROM `player_inventory_items` WHERE `player` = ? AND `inv_id` = ?")
+
+       PREPARE_STATEMENT(player_metadata_load, "SELECT `metadata`, `value` FROM "
+               "`player_metadata` WHERE `player` = ?")
+       PREPARE_STATEMENT(player_metadata_add, "INSERT INTO `player_metadata` "
+               "(`player`, `metadata`, `value`) VALUES (?, ?, ?)")
+       PREPARE_STATEMENT(player_metadata_remove, "DELETE FROM `player_metadata` "
+               "WHERE `player` = ?")
+       verbosestream << "ServerEnvironment: SQLite3 database opened (players)." << std::endl;
+}
+
+bool PlayerDatabaseSQLite3::playerDataExists(const std::string &name)
+{
+       verifyDatabase();
+       str_to_sqlite(m_stmt_player_load, 1, name);
+       bool res = (sqlite3_step(m_stmt_player_load) == SQLITE_ROW);
+       sqlite3_reset(m_stmt_player_load);
+       return res;
+}
+
+void PlayerDatabaseSQLite3::savePlayer(RemotePlayer *player)
+{
+       PlayerSAO* sao = player->getPlayerSAO();
+       sanity_check(sao);
+
+       const v3f &pos = sao->getBasePosition();
+       // Begin save in brace is mandatory
+       if (!playerDataExists(player->getName())) {
+               beginSave();
+               str_to_sqlite(m_stmt_player_add, 1, player->getName());
+               double_to_sqlite(m_stmt_player_add, 2, sao->getPitch());
+               double_to_sqlite(m_stmt_player_add, 3, sao->getYaw());
+               double_to_sqlite(m_stmt_player_add, 4, pos.X);
+               double_to_sqlite(m_stmt_player_add, 5, pos.Y);
+               double_to_sqlite(m_stmt_player_add, 6, pos.Z);
+               int64_to_sqlite(m_stmt_player_add, 7, sao->getHP());
+               int64_to_sqlite(m_stmt_player_add, 8, sao->getBreath());
+
+               sqlite3_vrfy(sqlite3_step(m_stmt_player_add), SQLITE_DONE);
+               sqlite3_reset(m_stmt_player_add);
+       } else {
+               beginSave();
+               double_to_sqlite(m_stmt_player_update, 1, sao->getPitch());
+               double_to_sqlite(m_stmt_player_update, 2, sao->getYaw());
+               double_to_sqlite(m_stmt_player_update, 3, pos.X);
+               double_to_sqlite(m_stmt_player_update, 4, pos.Y);
+               double_to_sqlite(m_stmt_player_update, 5, pos.Z);
+               int64_to_sqlite(m_stmt_player_update, 6, sao->getHP());
+               int64_to_sqlite(m_stmt_player_update, 7, sao->getBreath());
+               str_to_sqlite(m_stmt_player_update, 8, player->getName());
+
+               sqlite3_vrfy(sqlite3_step(m_stmt_player_update), SQLITE_DONE);
+               sqlite3_reset(m_stmt_player_update);
+       }
+
+       // Write player inventories
+       str_to_sqlite(m_stmt_player_remove_inventory, 1, player->getName());
+       sqlite3_vrfy(sqlite3_step(m_stmt_player_remove_inventory), SQLITE_DONE);
+       sqlite3_reset(m_stmt_player_remove_inventory);
+
+       str_to_sqlite(m_stmt_player_remove_inventory_items, 1, player->getName());
+       sqlite3_vrfy(sqlite3_step(m_stmt_player_remove_inventory_items), SQLITE_DONE);
+       sqlite3_reset(m_stmt_player_remove_inventory_items);
+
+       std::vector<const InventoryList*> inventory_lists = sao->getInventory()->getLists();
+       for (u16 i = 0; i < inventory_lists.size(); i++) {
+               const InventoryList* list = inventory_lists[i];
+
+               str_to_sqlite(m_stmt_player_add_inventory, 1, player->getName());
+               int_to_sqlite(m_stmt_player_add_inventory, 2, i);
+               int_to_sqlite(m_stmt_player_add_inventory, 3, list->getWidth());
+               str_to_sqlite(m_stmt_player_add_inventory, 4, list->getName());
+               int_to_sqlite(m_stmt_player_add_inventory, 5, list->getSize());
+               sqlite3_vrfy(sqlite3_step(m_stmt_player_add_inventory), SQLITE_DONE);
+               sqlite3_reset(m_stmt_player_add_inventory);
+
+               for (u32 j = 0; j < list->getSize(); j++) {
+                       std::ostringstream os;
+                       list->getItem(j).serialize(os);
+                       std::string itemStr = os.str();
+
+                       str_to_sqlite(m_stmt_player_add_inventory_items, 1, player->getName());
+                       int_to_sqlite(m_stmt_player_add_inventory_items, 2, i);
+                       int_to_sqlite(m_stmt_player_add_inventory_items, 3, j);
+                       str_to_sqlite(m_stmt_player_add_inventory_items, 4, itemStr);
+                       sqlite3_vrfy(sqlite3_step(m_stmt_player_add_inventory_items), SQLITE_DONE);
+                       sqlite3_reset(m_stmt_player_add_inventory_items);
+               }
+       }
+
+       str_to_sqlite(m_stmt_player_metadata_remove, 1, player->getName());
+       sqlite3_vrfy(sqlite3_step(m_stmt_player_metadata_remove), SQLITE_DONE);
+       sqlite3_reset(m_stmt_player_metadata_remove);
+
+       const PlayerAttributes &attrs = sao->getExtendedAttributes();
+       for (const auto &attr : attrs) {
+               str_to_sqlite(m_stmt_player_metadata_add, 1, player->getName());
+               str_to_sqlite(m_stmt_player_metadata_add, 2, attr.first);
+               str_to_sqlite(m_stmt_player_metadata_add, 3, attr.second);
+               sqlite3_vrfy(sqlite3_step(m_stmt_player_metadata_add), SQLITE_DONE);
+               sqlite3_reset(m_stmt_player_metadata_add);
+       }
+
+       endSave();
+}
+
+bool PlayerDatabaseSQLite3::loadPlayer(RemotePlayer *player, PlayerSAO *sao)
+{
+       verifyDatabase();
+
+       str_to_sqlite(m_stmt_player_load, 1, player->getName());
+       if (sqlite3_step(m_stmt_player_load) != SQLITE_ROW) {
+               sqlite3_reset(m_stmt_player_load);
+               return false;
+       }
+       sao->setPitch(sqlite_to_float(m_stmt_player_load, 0));
+       sao->setYaw(sqlite_to_float(m_stmt_player_load, 1));
+       sao->setBasePosition(sqlite_to_v3f(m_stmt_player_load, 2));
+       sao->setHPRaw((s16) MYMIN(sqlite_to_int(m_stmt_player_load, 5), S16_MAX));
+       sao->setBreath((u16) MYMIN(sqlite_to_int(m_stmt_player_load, 6), U16_MAX), false);
+       sqlite3_reset(m_stmt_player_load);
+
+       // Load inventory
+       str_to_sqlite(m_stmt_player_load_inventory, 1, player->getName());
+       while (sqlite3_step(m_stmt_player_load_inventory) == SQLITE_ROW) {
+               InventoryList *invList = player->inventory.addList(
+                       sqlite_to_string(m_stmt_player_load_inventory, 2),
+                       sqlite_to_uint(m_stmt_player_load_inventory, 3));
+               invList->setWidth(sqlite_to_uint(m_stmt_player_load_inventory, 1));
+
+               u32 invId = sqlite_to_uint(m_stmt_player_load_inventory, 0);
+
+               str_to_sqlite(m_stmt_player_load_inventory_items, 1, player->getName());
+               int_to_sqlite(m_stmt_player_load_inventory_items, 2, invId);
+               while (sqlite3_step(m_stmt_player_load_inventory_items) == SQLITE_ROW) {
+                       const std::string itemStr = sqlite_to_string(m_stmt_player_load_inventory_items, 1);
+                       if (itemStr.length() > 0) {
+                               ItemStack stack;
+                               stack.deSerialize(itemStr);
+                               invList->changeItem(sqlite_to_uint(m_stmt_player_load_inventory_items, 0), stack);
+                       }
+               }
+               sqlite3_reset(m_stmt_player_load_inventory_items);
+       }
+
+       sqlite3_reset(m_stmt_player_load_inventory);
+
+       str_to_sqlite(m_stmt_player_metadata_load, 1, sao->getPlayer()->getName());
+       while (sqlite3_step(m_stmt_player_metadata_load) == SQLITE_ROW) {
+               std::string attr = sqlite_to_string(m_stmt_player_metadata_load, 0);
+               std::string value = sqlite_to_string(m_stmt_player_metadata_load, 1);
+
+               sao->setExtendedAttribute(attr, value);
+       }
+       sqlite3_reset(m_stmt_player_metadata_load);
+       return true;
+}
+
+bool PlayerDatabaseSQLite3::removePlayer(const std::string &name)
+{
+       if (!playerDataExists(name))
+               return false;
+
+       str_to_sqlite(m_stmt_player_remove, 1, name);
+       sqlite3_vrfy(sqlite3_step(m_stmt_player_remove), SQLITE_DONE);
+       sqlite3_reset(m_stmt_player_remove);
+       return true;
+}
+
+void PlayerDatabaseSQLite3::listPlayers(std::vector<std::string> &res)
+{
+       verifyDatabase();
+
+       while (sqlite3_step(m_stmt_player_list) == SQLITE_ROW)
+               res.push_back(sqlite_to_string(m_stmt_player_list, 0));
+
+       sqlite3_reset(m_stmt_player_list);
+}
diff --git a/src/database/database-sqlite3.h b/src/database/database-sqlite3.h
new file mode 100644 (file)
index 0000000..8d9f91f
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include <cstring>
+#include <string>
+#include "database.h"
+#include "exceptions.h"
+
+extern "C" {
+#include "sqlite3.h"
+}
+
+class Database_SQLite3 : public Database
+{
+public:
+       virtual ~Database_SQLite3();
+
+       void beginSave();
+       void endSave();
+
+       bool initialized() const { return m_initialized; }
+protected:
+       Database_SQLite3(const std::string &savedir, const std::string &dbname);
+
+       // Open and initialize the database if needed
+       void verifyDatabase();
+
+       // Convertors
+       inline void str_to_sqlite(sqlite3_stmt *s, int iCol, const std::string &str) const
+       {
+               sqlite3_vrfy(sqlite3_bind_text(s, iCol, str.c_str(), str.size(), NULL));
+       }
+
+       inline void str_to_sqlite(sqlite3_stmt *s, int iCol, const char *str) const
+       {
+               sqlite3_vrfy(sqlite3_bind_text(s, iCol, str, strlen(str), NULL));
+       }
+
+       inline void int_to_sqlite(sqlite3_stmt *s, int iCol, int val) const
+       {
+               sqlite3_vrfy(sqlite3_bind_int(s, iCol, val));
+       }
+
+       inline void int64_to_sqlite(sqlite3_stmt *s, int iCol, s64 val) const
+       {
+               sqlite3_vrfy(sqlite3_bind_int64(s, iCol, (sqlite3_int64) val));
+       }
+
+       inline void double_to_sqlite(sqlite3_stmt *s, int iCol, double val) const
+       {
+               sqlite3_vrfy(sqlite3_bind_double(s, iCol, val));
+       }
+
+       inline std::string sqlite_to_string(sqlite3_stmt *s, int iCol)
+       {
+               const char* text = reinterpret_cast<const char*>(sqlite3_column_text(s, iCol));
+               return std::string(text ? text : "");
+       }
+
+       inline s32 sqlite_to_int(sqlite3_stmt *s, int iCol)
+       {
+               return sqlite3_column_int(s, iCol);
+       }
+
+       inline u32 sqlite_to_uint(sqlite3_stmt *s, int iCol)
+       {
+               return (u32) sqlite3_column_int(s, iCol);
+       }
+
+       inline float sqlite_to_float(sqlite3_stmt *s, int iCol)
+       {
+               return (float) sqlite3_column_double(s, iCol);
+       }
+
+       inline const v3f sqlite_to_v3f(sqlite3_stmt *s, int iCol)
+       {
+               return v3f(sqlite_to_float(s, iCol), sqlite_to_float(s, iCol + 1),
+                               sqlite_to_float(s, iCol + 2));
+       }
+
+       // Query verifiers helpers
+       inline void sqlite3_vrfy(int s, const std::string &m = "", int r = SQLITE_OK) const
+       {
+               if (s != r)
+                       throw DatabaseException(m + ": " + sqlite3_errmsg(m_database));
+       }
+
+       inline void sqlite3_vrfy(const int s, const int r, const std::string &m = "") const
+       {
+               sqlite3_vrfy(s, m, r);
+       }
+
+       // Create the database structure
+       virtual void createDatabase() = 0;
+       virtual void initStatements() = 0;
+
+       sqlite3 *m_database = nullptr;
+private:
+       // Open the database
+       void openDatabase();
+
+       bool m_initialized = false;
+
+       std::string m_savedir = "";
+       std::string m_dbname = "";
+
+       sqlite3_stmt *m_stmt_begin = nullptr;
+       sqlite3_stmt *m_stmt_end = nullptr;
+
+       s64 m_busy_handler_data[2];
+
+       static int busyHandler(void *data, int count);
+};
+
+class MapDatabaseSQLite3 : private Database_SQLite3, public MapDatabase
+{
+public:
+       MapDatabaseSQLite3(const std::string &savedir);
+       virtual ~MapDatabaseSQLite3();
+
+       bool saveBlock(const v3s16 &pos, const std::string &data);
+       void loadBlock(const v3s16 &pos, std::string *block);
+       bool deleteBlock(const v3s16 &pos);
+       void listAllLoadableBlocks(std::vector<v3s16> &dst);
+
+       void beginSave() { Database_SQLite3::beginSave(); }
+       void endSave() { Database_SQLite3::endSave(); }
+protected:
+       virtual void createDatabase();
+       virtual void initStatements();
+
+private:
+       void bindPos(sqlite3_stmt *stmt, const v3s16 &pos, int index = 1);
+
+       // Map
+       sqlite3_stmt *m_stmt_read = nullptr;
+       sqlite3_stmt *m_stmt_write = nullptr;
+       sqlite3_stmt *m_stmt_list = nullptr;
+       sqlite3_stmt *m_stmt_delete = nullptr;
+};
+
+class PlayerDatabaseSQLite3 : private Database_SQLite3, public PlayerDatabase
+{
+public:
+       PlayerDatabaseSQLite3(const std::string &savedir);
+       virtual ~PlayerDatabaseSQLite3();
+
+       void savePlayer(RemotePlayer *player);
+       bool loadPlayer(RemotePlayer *player, PlayerSAO *sao);
+       bool removePlayer(const std::string &name);
+       void listPlayers(std::vector<std::string> &res);
+
+protected:
+       virtual void createDatabase();
+       virtual void initStatements();
+
+private:
+       bool playerDataExists(const std::string &name);
+
+       // Players
+       sqlite3_stmt *m_stmt_player_load = nullptr;
+       sqlite3_stmt *m_stmt_player_add = nullptr;
+       sqlite3_stmt *m_stmt_player_update = nullptr;
+       sqlite3_stmt *m_stmt_player_remove = nullptr;
+       sqlite3_stmt *m_stmt_player_list = nullptr;
+       sqlite3_stmt *m_stmt_player_load_inventory = nullptr;
+       sqlite3_stmt *m_stmt_player_load_inventory_items = nullptr;
+       sqlite3_stmt *m_stmt_player_add_inventory = nullptr;
+       sqlite3_stmt *m_stmt_player_add_inventory_items = nullptr;
+       sqlite3_stmt *m_stmt_player_remove_inventory = nullptr;
+       sqlite3_stmt *m_stmt_player_remove_inventory_items = nullptr;
+       sqlite3_stmt *m_stmt_player_metadata_load = nullptr;
+       sqlite3_stmt *m_stmt_player_metadata_remove = nullptr;
+       sqlite3_stmt *m_stmt_player_metadata_add = nullptr;
+};
diff --git a/src/database/database.cpp b/src/database/database.cpp
new file mode 100644 (file)
index 0000000..12e0e1a
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "database.h"
+#include "irrlichttypes.h"
+
+
+/****************
+ * Black magic! *
+ ****************
+ * The position hashing is very messed up.
+ * It's a lot more complicated than it looks.
+ */
+
+static inline s16 unsigned_to_signed(u16 i, u16 max_positive)
+{
+       if (i < max_positive) {
+               return i;
+       }
+
+       return i - (max_positive * 2);
+}
+
+
+// Modulo of a negative number does not work consistently in C
+static inline s64 pythonmodulo(s64 i, s16 mod)
+{
+       if (i >= 0) {
+               return i % mod;
+       }
+       return mod - ((-i) % mod);
+}
+
+
+s64 MapDatabase::getBlockAsInteger(const v3s16 &pos)
+{
+       return (u64) pos.Z * 0x1000000 +
+               (u64) pos.Y * 0x1000 +
+               (u64) pos.X;
+}
+
+
+v3s16 MapDatabase::getIntegerAsBlock(s64 i)
+{
+       v3s16 pos;
+       pos.X = unsigned_to_signed(pythonmodulo(i, 4096), 2048);
+       i = (i - pos.X) / 4096;
+       pos.Y = unsigned_to_signed(pythonmodulo(i, 4096), 2048);
+       i = (i - pos.Y) / 4096;
+       pos.Z = unsigned_to_signed(pythonmodulo(i, 4096), 2048);
+       return pos;
+}
+
diff --git a/src/database/database.h b/src/database/database.h
new file mode 100644 (file)
index 0000000..9926c7b
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include <string>
+#include <vector>
+#include "irr_v3d.h"
+#include "irrlichttypes.h"
+#include "util/basic_macros.h"
+
+class Database
+{
+public:
+       virtual void beginSave() = 0;
+       virtual void endSave() = 0;
+       virtual bool initialized() const { return true; }
+};
+
+class MapDatabase : public Database
+{
+public:
+       virtual ~MapDatabase() = default;
+
+       virtual bool saveBlock(const v3s16 &pos, const std::string &data) = 0;
+       virtual void loadBlock(const v3s16 &pos, std::string *block) = 0;
+       virtual bool deleteBlock(const v3s16 &pos) = 0;
+
+       static s64 getBlockAsInteger(const v3s16 &pos);
+       static v3s16 getIntegerAsBlock(s64 i);
+
+       virtual void listAllLoadableBlocks(std::vector<v3s16> &dst) = 0;
+};
+
+class PlayerSAO;
+class RemotePlayer;
+
+class PlayerDatabase
+{
+public:
+       virtual ~PlayerDatabase() = default;
+
+       virtual void savePlayer(RemotePlayer *player) = 0;
+       virtual bool loadPlayer(RemotePlayer *player, PlayerSAO *sao) = 0;
+       virtual bool removePlayer(const std::string &name) = 0;
+       virtual void listPlayers(std::vector<std::string> &res) = 0;
+};
diff --git a/src/dungeongen.cpp b/src/dungeongen.cpp
deleted file mode 100644 (file)
index fa867b3..0000000
+++ /dev/null
@@ -1,677 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "dungeongen.h"
-#include "mapgen.h"
-#include "voxel.h"
-#include "noise.h"
-#include "mapblock.h"
-#include "mapnode.h"
-#include "map.h"
-#include "nodedef.h"
-#include "settings.h"
-
-//#define DGEN_USE_TORCHES
-
-NoiseParams nparams_dungeon_density(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0);
-NoiseParams nparams_dungeon_alt_wall(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-DungeonGen::DungeonGen(INodeDefManager *ndef,
-       GenerateNotifier *gennotify, DungeonParams *dparams)
-{
-       assert(ndef);
-
-       this->ndef      = ndef;
-       this->gennotify = gennotify;
-
-#ifdef DGEN_USE_TORCHES
-       c_torch  = ndef->getId("default:torch");
-#endif
-
-       if (dparams) {
-               memcpy(&dp, dparams, sizeof(dp));
-       } else {
-               // Default dungeon parameters
-               dp.seed = 0;
-
-               dp.c_water       = ndef->getId("mapgen_water_source");
-               dp.c_river_water = ndef->getId("mapgen_river_water_source");
-               dp.c_wall        = ndef->getId("mapgen_cobble");
-               dp.c_alt_wall    = ndef->getId("mapgen_mossycobble");
-               dp.c_stair       = ndef->getId("mapgen_stair_cobble");
-
-               if (dp.c_river_water == CONTENT_IGNORE)
-                       dp.c_river_water = ndef->getId("mapgen_water_source");
-
-               dp.diagonal_dirs       = false;
-               dp.only_in_ground      = true;
-               dp.holesize            = v3s16(1, 2, 1);
-               dp.corridor_len_min    = 1;
-               dp.corridor_len_max    = 13;
-               dp.room_size_min       = v3s16(4, 4, 4);
-               dp.room_size_max       = v3s16(8, 6, 8);
-               dp.room_size_large_min = v3s16(8, 8, 8);
-               dp.room_size_large_max = v3s16(16, 16, 16);
-               dp.rooms_min           = 2;
-               dp.rooms_max           = 16;
-               dp.y_min               = -MAX_MAP_GENERATION_LIMIT;
-               dp.y_max               = MAX_MAP_GENERATION_LIMIT;
-               dp.notifytype          = GENNOTIFY_DUNGEON;
-
-               dp.np_density  = nparams_dungeon_density;
-               dp.np_alt_wall = nparams_dungeon_alt_wall;
-       }
-}
-
-
-void DungeonGen::generate(MMVManip *vm, u32 bseed, v3s16 nmin, v3s16 nmax)
-{
-       assert(vm);
-
-       //TimeTaker t("gen dungeons");
-       if (nmin.Y < dp.y_min || nmax.Y > dp.y_max)
-               return;
-
-       float nval_density = NoisePerlin3D(&dp.np_density, nmin.X, nmin.Y, nmin.Z, dp.seed);
-       if (nval_density < 1.0f)
-               return;
-
-       static const bool preserve_ignore = !g_settings->getBool("projecting_dungeons");
-
-       this->vm = vm;
-       this->blockseed = bseed;
-       random.seed(bseed + 2);
-
-       // Dungeon generator doesn't modify places which have this set
-       vm->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE);
-
-       if (dp.only_in_ground) {
-               // Set all air and water to be untouchable to make dungeons open to
-               // caves and open air. Optionally set ignore to be untouchable to
-               // prevent protruding dungeons.
-               for (s16 z = nmin.Z; z <= nmax.Z; z++) {
-                       for (s16 y = nmin.Y; y <= nmax.Y; y++) {
-                               u32 i = vm->m_area.index(nmin.X, y, z);
-                               for (s16 x = nmin.X; x <= nmax.X; x++) {
-                                       content_t c = vm->m_data[i].getContent();
-                                       if (c == CONTENT_AIR || c == dp.c_water ||
-                                                       (preserve_ignore && c == CONTENT_IGNORE) ||
-                                                       c == dp.c_river_water)
-                                               vm->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
-                                       i++;
-                               }
-                       }
-               }
-       }
-
-       // Add them
-       for (u32 i = 0; i < floor(nval_density); i++)
-               makeDungeon(v3s16(1, 1, 1) * MAP_BLOCKSIZE);
-
-       // Optionally convert some structure to alternative structure
-       if (dp.c_alt_wall == CONTENT_IGNORE)
-               return;
-
-       for (s16 z = nmin.Z; z <= nmax.Z; z++)
-       for (s16 y = nmin.Y; y <= nmax.Y; y++) {
-               u32 i = vm->m_area.index(nmin.X, y, z);
-               for (s16 x = nmin.X; x <= nmax.X; x++) {
-                       if (vm->m_data[i].getContent() == dp.c_wall) {
-                               if (NoisePerlin3D(&dp.np_alt_wall, x, y, z, blockseed) > 0.0f)
-                                       vm->m_data[i].setContent(dp.c_alt_wall);
-                       }
-                       i++;
-               }
-       }
-
-       //printf("== gen dungeons: %dms\n", t.stop());
-}
-
-
-void DungeonGen::makeDungeon(v3s16 start_padding)
-{
-       const v3s16 &areasize = vm->m_area.getExtent();
-       v3s16 roomsize;
-       v3s16 roomplace;
-
-       /*
-               Find place for first room.
-               There is a 1 in 4 chance of the first room being 'large',
-               all other rooms are not 'large'.
-       */
-       bool fits = false;
-       for (u32 i = 0; i < 100 && !fits; i++) {
-               bool is_large_room = ((random.next() & 3) == 1);
-               if (is_large_room) {
-                       roomsize.Z = random.range(
-                               dp.room_size_large_min.Z, dp.room_size_large_max.Z);
-                       roomsize.Y = random.range(
-                               dp.room_size_large_min.Y, dp.room_size_large_max.Y);
-                       roomsize.X = random.range(
-                               dp.room_size_large_min.X, dp.room_size_large_max.X);
-               } else {
-                       roomsize.Z = random.range(dp.room_size_min.Z, dp.room_size_max.Z);
-                       roomsize.Y = random.range(dp.room_size_min.Y, dp.room_size_max.Y);
-                       roomsize.X = random.range(dp.room_size_min.X, dp.room_size_max.X);
-               }
-
-               // start_padding is used to disallow starting the generation of
-               // a dungeon in a neighboring generation chunk
-               roomplace = vm->m_area.MinEdge + start_padding;
-               roomplace.Z += random.range(0, areasize.Z - roomsize.Z - start_padding.Z);
-               roomplace.Y += random.range(0, areasize.Y - roomsize.Y - start_padding.Y);
-               roomplace.X += random.range(0, areasize.X - roomsize.X - start_padding.X);
-
-               /*
-                       Check that we're not putting the room to an unknown place,
-                       otherwise it might end up floating in the air
-               */
-               fits = true;
-               for (s16 z = 0; z < roomsize.Z; z++)
-               for (s16 y = 0; y < roomsize.Y; y++)
-               for (s16 x = 0; x < roomsize.X; x++) {
-                       v3s16 p = roomplace + v3s16(x, y, z);
-                       u32 vi = vm->m_area.index(p);
-                       if ((vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) ||
-                                       vm->m_data[vi].getContent() == CONTENT_IGNORE) {
-                               fits = false;
-                               break;
-                       }
-               }
-       }
-       // No place found
-       if (!fits)
-               return;
-
-       /*
-               Stores the center position of the last room made, so that
-               a new corridor can be started from the last room instead of
-               the new room, if chosen so.
-       */
-       v3s16 last_room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
-
-       u32 room_count = random.range(dp.rooms_min, dp.rooms_max);
-       for (u32 i = 0; i < room_count; i++) {
-               // Make a room to the determined place
-               makeRoom(roomsize, roomplace);
-
-               v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
-               if (gennotify)
-                       gennotify->addEvent(dp.notifytype, room_center);
-
-#ifdef DGEN_USE_TORCHES
-               // Place torch at room center (for testing)
-               vm->m_data[vm->m_area.index(room_center)] = MapNode(c_torch);
-#endif
-
-               // Quit if last room
-               if (i == room_count - 1)
-                       break;
-
-               // Determine walker start position
-
-               bool start_in_last_room = (random.range(0, 2) != 0);
-
-               v3s16 walker_start_place;
-
-               if (start_in_last_room) {
-                       walker_start_place = last_room_center;
-               } else {
-                       walker_start_place = room_center;
-                       // Store center of current room as the last one
-                       last_room_center = room_center;
-               }
-
-               // Create walker and find a place for a door
-               v3s16 doorplace;
-               v3s16 doordir;
-
-               m_pos = walker_start_place;
-               if (!findPlaceForDoor(doorplace, doordir))
-                       return;
-
-               if (random.range(0, 1) == 0)
-                       // Make the door
-                       makeDoor(doorplace, doordir);
-               else
-                       // Don't actually make a door
-                       doorplace -= doordir;
-
-               // Make a random corridor starting from the door
-               v3s16 corridor_end;
-               v3s16 corridor_end_dir;
-               makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir);
-
-               // Find a place for a random sized room
-               roomsize.Z = random.range(dp.room_size_min.Z, dp.room_size_max.Z);
-               roomsize.Y = random.range(dp.room_size_min.Y, dp.room_size_max.Y);
-               roomsize.X = random.range(dp.room_size_min.X, dp.room_size_max.X);
-
-               m_pos = corridor_end;
-               m_dir = corridor_end_dir;
-               if (!findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace))
-                       return;
-
-               if (random.range(0, 1) == 0)
-                       // Make the door
-                       makeDoor(doorplace, doordir);
-               else
-                       // Don't actually make a door
-                       roomplace -= doordir;
-
-       }
-}
-
-
-void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
-{
-       MapNode n_wall(dp.c_wall);
-       MapNode n_air(CONTENT_AIR);
-
-       // Make +-X walls
-       for (s16 z = 0; z < roomsize.Z; z++)
-       for (s16 y = 0; y < roomsize.Y; y++) {
-               {
-                       v3s16 p = roomplace + v3s16(0, y, z);
-                       if (!vm->m_area.contains(p))
-                               continue;
-                       u32 vi = vm->m_area.index(p);
-                       if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-                               continue;
-                       vm->m_data[vi] = n_wall;
-               }
-               {
-                       v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z);
-                       if (!vm->m_area.contains(p))
-                               continue;
-                       u32 vi = vm->m_area.index(p);
-                       if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-                               continue;
-                       vm->m_data[vi] = n_wall;
-               }
-       }
-
-       // Make +-Z walls
-       for (s16 x = 0; x < roomsize.X; x++)
-       for (s16 y = 0; y < roomsize.Y; y++) {
-               {
-                       v3s16 p = roomplace + v3s16(x, y, 0);
-                       if (!vm->m_area.contains(p))
-                               continue;
-                       u32 vi = vm->m_area.index(p);
-                       if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-                               continue;
-                       vm->m_data[vi] = n_wall;
-               }
-               {
-                       v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1);
-                       if (!vm->m_area.contains(p))
-                               continue;
-                       u32 vi = vm->m_area.index(p);
-                       if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-                               continue;
-                       vm->m_data[vi] = n_wall;
-               }
-       }
-
-       // Make +-Y walls (floor and ceiling)
-       for (s16 z = 0; z < roomsize.Z; z++)
-       for (s16 x = 0; x < roomsize.X; x++) {
-               {
-                       v3s16 p = roomplace + v3s16(x, 0, z);
-                       if (!vm->m_area.contains(p))
-                               continue;
-                       u32 vi = vm->m_area.index(p);
-                       if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-                               continue;
-                       vm->m_data[vi] = n_wall;
-               }
-               {
-                       v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z);
-                       if (!vm->m_area.contains(p))
-                               continue;
-                       u32 vi = vm->m_area.index(p);
-                       if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
-                               continue;
-                       vm->m_data[vi] = n_wall;
-               }
-       }
-
-       // Fill with air
-       for (s16 z = 1; z < roomsize.Z - 1; z++)
-       for (s16 y = 1; y < roomsize.Y - 1; y++)
-       for (s16 x = 1; x < roomsize.X - 1; x++) {
-               v3s16 p = roomplace + v3s16(x, y, z);
-               if (!vm->m_area.contains(p))
-                       continue;
-               u32 vi = vm->m_area.index(p);
-               vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
-               vm->m_data[vi] = n_air;
-       }
-}
-
-
-void DungeonGen::makeFill(v3s16 place, v3s16 size,
-       u8 avoid_flags, MapNode n, u8 or_flags)
-{
-       for (s16 z = 0; z < size.Z; z++)
-       for (s16 y = 0; y < size.Y; y++)
-       for (s16 x = 0; x < size.X; x++) {
-               v3s16 p = place + v3s16(x, y, z);
-               if (!vm->m_area.contains(p))
-                       continue;
-               u32 vi = vm->m_area.index(p);
-               if (vm->m_flags[vi] & avoid_flags)
-                       continue;
-               vm->m_flags[vi] |= or_flags;
-               vm->m_data[vi] = n;
-       }
-}
-
-
-void DungeonGen::makeHole(v3s16 place)
-{
-       makeFill(place, dp.holesize, 0, MapNode(CONTENT_AIR),
-               VMANIP_FLAG_DUNGEON_INSIDE);
-}
-
-
-void DungeonGen::makeDoor(v3s16 doorplace, v3s16 doordir)
-{
-       makeHole(doorplace);
-
-#ifdef DGEN_USE_TORCHES
-       // Place torch (for testing)
-       vm->m_data[vm->m_area.index(doorplace)] = MapNode(c_torch);
-#endif
-}
-
-
-void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir,
-       v3s16 &result_place, v3s16 &result_dir)
-{
-       makeHole(doorplace);
-       v3s16 p0 = doorplace;
-       v3s16 dir = doordir;
-       u32 length = random.range(dp.corridor_len_min, dp.corridor_len_max);
-       u32 partlength = random.range(dp.corridor_len_min, dp.corridor_len_max);
-       u32 partcount = 0;
-       s16 make_stairs = 0;
-
-       if (random.next() % 2 == 0 && partlength >= 3)
-               make_stairs = random.next() % 2 ? 1 : -1;
-
-       for (u32 i = 0; i < length; i++) {
-               v3s16 p = p0 + dir;
-               if (partcount != 0)
-                       p.Y += make_stairs;
-
-               // Check segment of minimum size corridor is in voxelmanip
-               if (vm->m_area.contains(p) && vm->m_area.contains(p + v3s16(0, 1, 0))) {
-                       if (make_stairs) {
-                               makeFill(p + v3s16(-1, -1, -1),
-                                       dp.holesize + v3s16(2, 3, 2),
-                                       VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
-                                       MapNode(dp.c_wall),
-                                       0);
-                               makeHole(p);
-                               makeHole(p - dir);
-
-                               // TODO: fix stairs code so it works 100%
-                               // (quite difficult)
-
-                               // exclude stairs from the bottom step
-                               // exclude stairs from diagonal steps
-                               if (((dir.X ^ dir.Z) & 1) &&
-                                               (((make_stairs ==  1) && i != 0) ||
-                                               ((make_stairs == -1) && i != length - 1))) {
-                                       // rotate face 180 deg if
-                                       // making stairs backwards
-                                       int facedir = dir_to_facedir(dir * make_stairs);
-                                       v3s16 ps = p;
-                                       u16 stair_width = (dir.Z != 0) ? dp.holesize.X : dp.holesize.Z;
-                                       // Stair width direction vector
-                                       v3s16 swv = (dir.Z != 0) ? v3s16(1, 0, 0) : v3s16(0, 0, 1);
-
-                                       for (u16 st = 0; st < stair_width; st++) {
-                                               u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z);
-                                               if (vm->m_area.contains(ps + v3s16(-dir.X, -1, -dir.Z)) &&
-                                                               vm->m_data[vi].getContent() == dp.c_wall)
-                                                       vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
-
-                                               vi = vm->m_area.index(ps.X, ps.Y, ps.Z);
-                                               if (vm->m_area.contains(ps) &&
-                                                               vm->m_data[vi].getContent() == dp.c_wall)
-                                                       vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
-
-                                               ps += swv;
-                                       }
-                               }
-                       } else {
-                               makeFill(p + v3s16(-1, -1, -1),
-                                       dp.holesize + v3s16(2, 2, 2),
-                                       VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
-                                       MapNode(dp.c_wall),
-                                       0);
-                               makeHole(p);
-                       }
-
-                       p0 = p;
-               } else {
-                       // Can't go here, turn away
-                       dir = turn_xz(dir, random.range(0, 1));
-                       make_stairs = -make_stairs;
-                       partcount = 0;
-                       partlength = random.range(1, length);
-                       continue;
-               }
-
-               partcount++;
-               if (partcount >= partlength) {
-                       partcount = 0;
-
-                       dir = random_turn(random, dir);
-
-                       partlength = random.range(1, length);
-
-                       make_stairs = 0;
-                       if (random.next() % 2 == 0 && partlength >= 3)
-                               make_stairs = random.next() % 2 ? 1 : -1;
-               }
-       }
-       result_place = p0;
-       result_dir = dir;
-}
-
-
-bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
-{
-       for (u32 i = 0; i < 100; i++) {
-               v3s16 p = m_pos + m_dir;
-               v3s16 p1 = p + v3s16(0, 1, 0);
-               if (!vm->m_area.contains(p) || !vm->m_area.contains(p1) || i % 4 == 0) {
-                       randomizeDir();
-                       continue;
-               }
-               if (vm->getNodeNoExNoEmerge(p).getContent() == dp.c_wall &&
-                               vm->getNodeNoExNoEmerge(p1).getContent() == dp.c_wall) {
-                       // Found wall, this is a good place!
-                       result_place = p;
-                       result_dir = m_dir;
-                       // Randomize next direction
-                       randomizeDir();
-                       return true;
-               }
-               /*
-                       Determine where to move next
-               */
-               // Jump one up if the actual space is there
-               if (vm->getNodeNoExNoEmerge(p +
-                               v3s16(0, 0, 0)).getContent() == dp.c_wall &&
-                               vm->getNodeNoExNoEmerge(p +
-                               v3s16(0, 1, 0)).getContent() == CONTENT_AIR &&
-                               vm->getNodeNoExNoEmerge(p +
-                               v3s16(0, 2, 0)).getContent() == CONTENT_AIR)
-                       p += v3s16(0,1,0);
-               // Jump one down if the actual space is there
-               if (vm->getNodeNoExNoEmerge(p +
-                               v3s16(0, 1, 0)).getContent() == dp.c_wall &&
-                               vm->getNodeNoExNoEmerge(p +
-                               v3s16(0, 0, 0)).getContent() == CONTENT_AIR &&
-                               vm->getNodeNoExNoEmerge(p +
-                               v3s16(0, -1, 0)).getContent() == CONTENT_AIR)
-                       p += v3s16(0, -1, 0);
-               // Check if walking is now possible
-               if (vm->getNodeNoExNoEmerge(p).getContent() != CONTENT_AIR ||
-                               vm->getNodeNoExNoEmerge(p +
-                               v3s16(0, 1, 0)).getContent() != CONTENT_AIR) {
-                       // Cannot continue walking here
-                       randomizeDir();
-                       continue;
-               }
-               // Move there
-               m_pos = p;
-       }
-       return false;
-}
-
-
-bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
-       v3s16 &result_doordir, v3s16 &result_roomplace)
-{
-       for (s16 trycount = 0; trycount < 30; trycount++) {
-               v3s16 doorplace;
-               v3s16 doordir;
-               bool r = findPlaceForDoor(doorplace, doordir);
-               if (!r)
-                       continue;
-               v3s16 roomplace;
-               // X east, Z north, Y up
-               if (doordir == v3s16(1, 0, 0)) // X+
-                       roomplace = doorplace +
-                               v3s16(0, -1, random.range(-roomsize.Z + 2, -2));
-               if (doordir == v3s16(-1, 0, 0)) // X-
-                       roomplace = doorplace +
-                               v3s16(-roomsize.X + 1, -1, random.range(-roomsize.Z + 2, -2));
-               if (doordir == v3s16(0, 0, 1)) // Z+
-                       roomplace = doorplace +
-                               v3s16(random.range(-roomsize.X + 2, -2), -1, 0);
-               if (doordir == v3s16(0, 0, -1)) // Z-
-                       roomplace = doorplace +
-                               v3s16(random.range(-roomsize.X + 2, -2), -1, -roomsize.Z + 1);
-
-               // Check fit
-               bool fits = true;
-               for (s16 z = 1; z < roomsize.Z - 1; z++)
-               for (s16 y = 1; y < roomsize.Y - 1; y++)
-               for (s16 x = 1; x < roomsize.X - 1; x++) {
-                       v3s16 p = roomplace + v3s16(x, y, z);
-                       if (!vm->m_area.contains(p)) {
-                               fits = false;
-                               break;
-                       }
-                       if (vm->m_flags[vm->m_area.index(p)] & VMANIP_FLAG_DUNGEON_INSIDE) {
-                               fits = false;
-                               break;
-                       }
-               }
-               if (!fits) {
-                       // Find new place
-                       continue;
-               }
-               result_doorplace = doorplace;
-               result_doordir   = doordir;
-               result_roomplace = roomplace;
-               return true;
-       }
-       return false;
-}
-
-
-v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs)
-{
-       // Make diagonal directions somewhat rare
-       if (diagonal_dirs && (random.next() % 4 == 0)) {
-               v3s16 dir;
-               int trycount = 0;
-
-               do {
-                       trycount++;
-
-                       dir.Z = random.next() % 3 - 1;
-                       dir.Y = 0;
-                       dir.X = random.next() % 3 - 1;
-               } while ((dir.X == 0 || dir.Z == 0) && trycount < 10);
-
-               return dir;
-       }
-
-       if (random.next() % 2 == 0)
-               return random.next() % 2 ? v3s16(-1, 0, 0) : v3s16(1, 0, 0);
-
-       return random.next() % 2 ? v3s16(0, 0, -1) : v3s16(0, 0, 1);
-}
-
-
-v3s16 turn_xz(v3s16 olddir, int t)
-{
-       v3s16 dir;
-       if (t == 0) {
-               // Turn right
-               dir.X = olddir.Z;
-               dir.Z = -olddir.X;
-               dir.Y = olddir.Y;
-       } else {
-               // Turn left
-               dir.X = -olddir.Z;
-               dir.Z = olddir.X;
-               dir.Y = olddir.Y;
-       }
-       return dir;
-}
-
-
-v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
-{
-       int turn = random.range(0, 2);
-       v3s16 dir;
-       if (turn == 0)
-               // Go straight
-               dir = olddir;
-       else if (turn == 1)
-               // Turn right
-               dir = turn_xz(olddir, 0);
-       else
-               // Turn left
-               dir = turn_xz(olddir, 1);
-       return dir;
-}
-
-
-int dir_to_facedir(v3s16 d)
-{
-       if (abs(d.X) > abs(d.Z))
-               return d.X < 0 ? 3 : 1;
-
-       return d.Z < 0 ? 2 : 0;
-}
diff --git a/src/dungeongen.h b/src/dungeongen.h
deleted file mode 100644 (file)
index 6799db7..0000000
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "voxel.h"
-#include "noise.h"
-#include "mapgen.h"
-
-#define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1
-#define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2
-#define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\
-               VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE)
-
-class MMVManip;
-class INodeDefManager;
-
-v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs);
-v3s16 turn_xz(v3s16 olddir, int t);
-v3s16 random_turn(PseudoRandom &random, v3s16 olddir);
-int dir_to_facedir(v3s16 d);
-
-
-struct DungeonParams {
-       s32 seed;
-
-       content_t c_water;
-       content_t c_river_water;
-       content_t c_wall;
-       content_t c_alt_wall;
-       content_t c_stair;
-
-       bool diagonal_dirs;
-       bool only_in_ground;
-       v3s16 holesize;
-       u16 corridor_len_min;
-       u16 corridor_len_max;
-       v3s16 room_size_min;
-       v3s16 room_size_max;
-       v3s16 room_size_large_min;
-       v3s16 room_size_large_max;
-       u16 rooms_min;
-       u16 rooms_max;
-       s16 y_min;
-       s16 y_max;
-       GenNotifyType notifytype;
-
-       NoiseParams np_density;
-       NoiseParams np_alt_wall;
-};
-
-class DungeonGen {
-public:
-       MMVManip *vm;
-       INodeDefManager *ndef;
-       GenerateNotifier *gennotify;
-
-       u32 blockseed;
-       PseudoRandom random;
-       v3s16 csize;
-
-       content_t c_torch;
-       DungeonParams dp;
-
-       // RoomWalker
-       v3s16 m_pos;
-       v3s16 m_dir;
-
-       DungeonGen(INodeDefManager *ndef,
-               GenerateNotifier *gennotify, DungeonParams *dparams);
-
-       void generate(MMVManip *vm, u32 bseed,
-               v3s16 full_node_min, v3s16 full_node_max);
-
-       void makeDungeon(v3s16 start_padding);
-       void makeRoom(v3s16 roomsize, v3s16 roomplace);
-       void makeCorridor(v3s16 doorplace, v3s16 doordir,
-               v3s16 &result_place, v3s16 &result_dir);
-       void makeDoor(v3s16 doorplace, v3s16 doordir);
-       void makeFill(v3s16 place, v3s16 size, u8 avoid_flags, MapNode n, u8 or_flags);
-       void makeHole(v3s16 place);
-
-       bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir);
-       bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
-                       v3s16 &result_doordir, v3s16 &result_roomplace);
-
-       inline void randomizeDir()
-       {
-               m_dir = rand_ortho_dir(random, dp.diagonal_dirs);
-       }
-};
-
-extern NoiseParams nparams_dungeon_density;
-extern NoiseParams nparams_dungeon_alt_wall;
index e9d96c0baf462539c731dfb73111fe0951ea9540..ffe387f636c6d42b37e0e301a3929b129548056f 100644 (file)
@@ -34,10 +34,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "map.h"
 #include "mapblock.h"
-#include "mg_biome.h"
-#include "mg_ore.h"
-#include "mg_decoration.h"
-#include "mg_schematic.h"
+#include "mapgen/mg_biome.h"
+#include "mapgen/mg_ore.h"
+#include "mapgen/mg_decoration.h"
+#include "mapgen/mg_schematic.h"
 #include "nodedef.h"
 #include "profiler.h"
 #include "scripting_server.h"
index 135121b39c74822e9a72083ab2513ebfee7aeb36..e1f5d5ab04468119658977f145756b29780a9030 100644 (file)
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "network/networkprotocol.h"
 #include "irr_v3d.h"
 #include "util/container.h"
-#include "mapgen.h" // for MapgenParams
+#include "mapgen/mapgen.h" // for MapgenParams
 #include "map.h"
 
 #define BLOCK_EMERGE_ALLOW_GEN   (1 << 0)
index 200de2c5942ad0519dc700a0b5a11b2f26917e5e..462fd37a90ef87d9b1defe004afefd841698eecf 100644 (file)
@@ -39,12 +39,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "filesys.h"
 #include "gettext.h"
-#include "guiChatConsole.h"
-#include "guiFormSpecMenu.h"
-#include "guiKeyChangeMenu.h"
-#include "guiPasswordChange.h"
-#include "guiVolumeChange.h"
-#include "mainmenumanager.h"
+#include "gui/guiChatConsole.h"
+#include "gui/guiFormSpecMenu.h"
+#include "gui/guiKeyChangeMenu.h"
+#include "gui/guiPasswordChange.h"
+#include "gui/guiVolumeChange.h"
+#include "gui/mainmenumanager.h"
 #include "mapblock.h"
 #include "minimap.h"
 #include "nodedef.h"         // Needed for determining pointing to nodes
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
new file mode 100644 (file)
index 0000000..067ba09
--- /dev/null
@@ -0,0 +1,13 @@
+set(gui_SRCS
+       ${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/guiEngine.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/guiFormSpecMenu.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/intlGUIEditBox.cpp
+       PARENT_SCOPE
+)
diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp
new file mode 100644 (file)
index 0000000..b194834
--- /dev/null
@@ -0,0 +1,642 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "guiChatConsole.h"
+#include "chat.h"
+#include "client.h"
+#include "debug.h"
+#include "gettime.h"
+#include "keycode.h"
+#include "settings.h"
+#include "porting.h"
+#include "client/tile.h"
+#include "fontengine.h"
+#include "log.h"
+#include "gettext.h"
+#include <string>
+
+#if USE_FREETYPE
+       #include "irrlicht_changes/CGUITTFont.h"
+#endif
+
+inline u32 clamp_u8(s32 value)
+{
+       return (u32) MYMIN(MYMAX(value, 0), 255);
+}
+
+
+GUIChatConsole::GUIChatConsole(
+               gui::IGUIEnvironment* env,
+               gui::IGUIElement* parent,
+               s32 id,
+               ChatBackend* backend,
+               Client* client,
+               IMenuManager* menumgr
+):
+       IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
+                       core::rect<s32>(0,0,100,100)),
+       m_chat_backend(backend),
+       m_client(client),
+       m_menumgr(menumgr),
+       m_animate_time_old(porting::getTimeMs())
+{
+       // load background settings
+       s32 console_alpha = g_settings->getS32("console_alpha");
+       m_background_color.setAlpha(clamp_u8(console_alpha));
+
+       // load the background texture depending on settings
+       ITextureSource *tsrc = client->getTextureSource();
+       if (tsrc->isKnownSourceImage("background_chat.jpg")) {
+               m_background = tsrc->getTexture("background_chat.jpg");
+               m_background_color.setRed(255);
+               m_background_color.setGreen(255);
+               m_background_color.setBlue(255);
+       } else {
+               v3f console_color = g_settings->getV3F("console_color");
+               m_background_color.setRed(clamp_u8(myround(console_color.X)));
+               m_background_color.setGreen(clamp_u8(myround(console_color.Y)));
+               m_background_color.setBlue(clamp_u8(myround(console_color.Z)));
+       }
+
+       m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono);
+
+       if (!m_font) {
+               errorstream << "GUIChatConsole: Unable to load mono font ";
+       } else {
+               core::dimension2d<u32> dim = m_font->getDimension(L"M");
+               m_fontsize = v2u32(dim.Width, dim.Height);
+               m_font->grab();
+       }
+       m_fontsize.X = MYMAX(m_fontsize.X, 1);
+       m_fontsize.Y = MYMAX(m_fontsize.Y, 1);
+
+       // set default cursor options
+       setCursor(true, true, 2.0, 0.1);
+}
+
+GUIChatConsole::~GUIChatConsole()
+{
+       if (m_font)
+               m_font->drop();
+}
+
+void GUIChatConsole::openConsole(f32 scale)
+{
+       assert(scale > 0.0f && scale <= 1.0f);
+
+       m_open = true;
+       m_desired_height_fraction = scale;
+       m_desired_height = scale * m_screensize.Y;
+       reformatConsole();
+       m_animate_time_old = porting::getTimeMs();
+       IGUIElement::setVisible(true);
+       Environment->setFocus(this);
+       m_menumgr->createdMenu(this);
+}
+
+bool GUIChatConsole::isOpen() const
+{
+       return m_open;
+}
+
+bool GUIChatConsole::isOpenInhibited() const
+{
+       return m_open_inhibited > 0;
+}
+
+void GUIChatConsole::closeConsole()
+{
+       m_open = false;
+       Environment->removeFocus(this);
+       m_menumgr->deletingMenu(this);
+}
+
+void GUIChatConsole::closeConsoleAtOnce()
+{
+       closeConsole();
+       m_height = 0;
+       recalculateConsolePosition();
+}
+
+f32 GUIChatConsole::getDesiredHeight() const
+{
+       return m_desired_height_fraction;
+}
+
+void GUIChatConsole::replaceAndAddToHistory(std::wstring line)
+{
+       ChatPrompt& prompt = m_chat_backend->getPrompt();
+       prompt.addToHistory(prompt.getLine());
+       prompt.replace(line);
+}
+
+
+void GUIChatConsole::setCursor(
+       bool visible, bool blinking, f32 blink_speed, f32 relative_height)
+{
+       if (visible)
+       {
+               if (blinking)
+               {
+                       // leave m_cursor_blink unchanged
+                       m_cursor_blink_speed = blink_speed;
+               }
+               else
+               {
+                       m_cursor_blink = 0x8000;  // on
+                       m_cursor_blink_speed = 0.0;
+               }
+       }
+       else
+       {
+               m_cursor_blink = 0;  // off
+               m_cursor_blink_speed = 0.0;
+       }
+       m_cursor_height = relative_height;
+}
+
+void GUIChatConsole::draw()
+{
+       if(!IsVisible)
+               return;
+
+       video::IVideoDriver* driver = Environment->getVideoDriver();
+
+       // Check screen size
+       v2u32 screensize = driver->getScreenSize();
+       if (screensize != m_screensize)
+       {
+               // screen size has changed
+               // scale current console height to new window size
+               if (m_screensize.Y != 0)
+                       m_height = m_height * screensize.Y / m_screensize.Y;
+               m_screensize = screensize;
+               m_desired_height = m_desired_height_fraction * m_screensize.Y;
+               reformatConsole();
+       }
+
+       // Animation
+       u64 now = porting::getTimeMs();
+       animate(now - m_animate_time_old);
+       m_animate_time_old = now;
+
+       // Draw console elements if visible
+       if (m_height > 0)
+       {
+               drawBackground();
+               drawText();
+               drawPrompt();
+       }
+
+       gui::IGUIElement::draw();
+}
+
+void GUIChatConsole::reformatConsole()
+{
+       s32 cols = m_screensize.X / m_fontsize.X - 2; // make room for a margin (looks better)
+       s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt
+       if (cols <= 0 || rows <= 0)
+               cols = rows = 0;
+       recalculateConsolePosition();
+       m_chat_backend->reformat(cols, rows);
+}
+
+void GUIChatConsole::recalculateConsolePosition()
+{
+       core::rect<s32> rect(0, 0, m_screensize.X, m_height);
+       DesiredRect = rect;
+       recalculateAbsolutePosition(false);
+}
+
+void GUIChatConsole::animate(u32 msec)
+{
+       // animate the console height
+       s32 goal = m_open ? m_desired_height : 0;
+
+       // Set invisible if close animation finished (reset by openConsole)
+       // This function (animate()) is never called once its visibility becomes false so do not
+       //              actually set visible to false before the inhibited period is over
+       if (!m_open && m_height == 0 && m_open_inhibited == 0)
+               IGUIElement::setVisible(false);
+
+       if (m_height != goal)
+       {
+               s32 max_change = msec * m_screensize.Y * (m_height_speed / 1000.0);
+               if (max_change == 0)
+                       max_change = 1;
+
+               if (m_height < goal)
+               {
+                       // increase height
+                       if (m_height + max_change < goal)
+                               m_height += max_change;
+                       else
+                               m_height = goal;
+               }
+               else
+               {
+                       // decrease height
+                       if (m_height > goal + max_change)
+                               m_height -= max_change;
+                       else
+                               m_height = goal;
+               }
+
+               recalculateConsolePosition();
+       }
+
+       // blink the cursor
+       if (m_cursor_blink_speed != 0.0)
+       {
+               u32 blink_increase = 0x10000 * msec * (m_cursor_blink_speed / 1000.0);
+               if (blink_increase == 0)
+                       blink_increase = 1;
+               m_cursor_blink = ((m_cursor_blink + blink_increase) & 0xffff);
+       }
+
+       // decrease open inhibit counter
+       if (m_open_inhibited > msec)
+               m_open_inhibited -= msec;
+       else
+               m_open_inhibited = 0;
+}
+
+void GUIChatConsole::drawBackground()
+{
+       video::IVideoDriver* driver = Environment->getVideoDriver();
+       if (m_background != NULL)
+       {
+               core::rect<s32> sourcerect(0, -m_height, m_screensize.X, 0);
+               driver->draw2DImage(
+                       m_background,
+                       v2s32(0, 0),
+                       sourcerect,
+                       &AbsoluteClippingRect,
+                       m_background_color,
+                       false);
+       }
+       else
+       {
+               driver->draw2DRectangle(
+                       m_background_color,
+                       core::rect<s32>(0, 0, m_screensize.X, m_height),
+                       &AbsoluteClippingRect);
+       }
+}
+
+void GUIChatConsole::drawText()
+{
+       if (m_font == NULL)
+               return;
+
+       ChatBuffer& buf = m_chat_backend->getConsoleBuffer();
+       for (u32 row = 0; row < buf.getRows(); ++row)
+       {
+               const ChatFormattedLine& line = buf.getFormattedLine(row);
+               if (line.fragments.empty())
+                       continue;
+
+               s32 line_height = m_fontsize.Y;
+               s32 y = row * line_height + m_height - m_desired_height;
+               if (y + line_height < 0)
+                       continue;
+
+               for (const ChatFormattedFragment &fragment : line.fragments) {
+                       s32 x = (fragment.column + 1) * m_fontsize.X;
+                       core::rect<s32> destrect(
+                               x, y, x + m_fontsize.X * fragment.text.size(), y + m_fontsize.Y);
+
+
+                       #if USE_FREETYPE
+                       // Draw colored text if FreeType is enabled
+                               irr::gui::CGUITTFont *tmp = dynamic_cast<irr::gui::CGUITTFont *>(m_font);
+                               tmp->draw(
+                                       fragment.text,
+                                       destrect,
+                                       video::SColor(255, 255, 255, 255),
+                                       false,
+                                       false,
+                                       &AbsoluteClippingRect);
+                       #else
+                       // Otherwise use standard text
+                               m_font->draw(
+                                       fragment.text.c_str(),
+                                       destrect,
+                                       video::SColor(255, 255, 255, 255),
+                                       false,
+                                       false,
+                                       &AbsoluteClippingRect);
+                       #endif
+               }
+       }
+}
+
+void GUIChatConsole::drawPrompt()
+{
+       if (!m_font)
+               return;
+
+       u32 row = m_chat_backend->getConsoleBuffer().getRows();
+       s32 line_height = m_fontsize.Y;
+       s32 y = row * line_height + m_height - m_desired_height;
+
+       ChatPrompt& prompt = m_chat_backend->getPrompt();
+       std::wstring prompt_text = prompt.getVisiblePortion();
+
+       // FIXME Draw string at once, not character by character
+       // That will only work with the cursor once we have a monospace font
+       for (u32 i = 0; i < prompt_text.size(); ++i)
+       {
+               wchar_t ws[2] = {prompt_text[i], 0};
+               s32 x = (1 + i) * m_fontsize.X;
+               core::rect<s32> destrect(
+                       x, y, x + m_fontsize.X, y + m_fontsize.Y);
+               m_font->draw(
+                       ws,
+                       destrect,
+                       video::SColor(255, 255, 255, 255),
+                       false,
+                       false,
+                       &AbsoluteClippingRect);
+       }
+
+       // Draw the cursor during on periods
+       if ((m_cursor_blink & 0x8000) != 0)
+       {
+               s32 cursor_pos = prompt.getVisibleCursorPosition();
+               if (cursor_pos >= 0)
+               {
+                       s32 cursor_len = prompt.getCursorLength();
+                       video::IVideoDriver* driver = Environment->getVideoDriver();
+                       s32 x = (1 + cursor_pos) * m_fontsize.X;
+                       core::rect<s32> destrect(
+                               x,
+                               y + m_fontsize.Y * (1.0 - m_cursor_height),
+                               x + m_fontsize.X * MYMAX(cursor_len, 1),
+                               y + m_fontsize.Y * (cursor_len ? m_cursor_height+1 : 1)
+                       );
+                       video::SColor cursor_color(255,255,255,255);
+                       driver->draw2DRectangle(
+                               cursor_color,
+                               destrect,
+                               &AbsoluteClippingRect);
+               }
+       }
+
+}
+
+bool GUIChatConsole::OnEvent(const SEvent& event)
+{
+
+       ChatPrompt &prompt = m_chat_backend->getPrompt();
+
+       if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
+       {
+               // Key input
+               if (KeyPress(event.KeyInput) == getKeySetting("keymap_console")) {
+                       closeConsole();
+
+                       // inhibit open so the_game doesn't reopen immediately
+                       m_open_inhibited = 50;
+                       m_close_on_enter = false;
+                       return true;
+               }
+
+               if (event.KeyInput.Key == KEY_ESCAPE) {
+                       closeConsoleAtOnce();
+                       m_close_on_enter = false;
+                       // inhibit open so the_game doesn't reopen immediately
+                       m_open_inhibited = 1; // so the ESCAPE button doesn't open the "pause menu"
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_PRIOR)
+               {
+                       m_chat_backend->scrollPageUp();
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_NEXT)
+               {
+                       m_chat_backend->scrollPageDown();
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_RETURN)
+               {
+                       prompt.addToHistory(prompt.getLine());
+                       std::wstring text = prompt.replace(L"");
+                       m_client->typeChatMessage(text);
+                       if (m_close_on_enter) {
+                               closeConsoleAtOnce();
+                               m_close_on_enter = false;
+                       }
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_UP)
+               {
+                       // Up pressed
+                       // Move back in history
+                       prompt.historyPrev();
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_DOWN)
+               {
+                       // Down pressed
+                       // Move forward in history
+                       prompt.historyNext();
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT)
+               {
+                       // Left/right pressed
+                       // Move/select character/word to the left depending on control and shift keys
+                       ChatPrompt::CursorOp op = event.KeyInput.Shift ?
+                               ChatPrompt::CURSOROP_SELECT :
+                               ChatPrompt::CURSOROP_MOVE;
+                       ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ?
+                               ChatPrompt::CURSOROP_DIR_LEFT :
+                               ChatPrompt::CURSOROP_DIR_RIGHT;
+                       ChatPrompt::CursorOpScope scope = event.KeyInput.Control ?
+                               ChatPrompt::CURSOROP_SCOPE_WORD :
+                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
+                       prompt.cursorOperation(op, dir, scope);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_HOME)
+               {
+                       // Home pressed
+                       // move to beginning of line
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_MOVE,
+                               ChatPrompt::CURSOROP_DIR_LEFT,
+                               ChatPrompt::CURSOROP_SCOPE_LINE);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_END)
+               {
+                       // End pressed
+                       // move to end of line
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_MOVE,
+                               ChatPrompt::CURSOROP_DIR_RIGHT,
+                               ChatPrompt::CURSOROP_SCOPE_LINE);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_BACK)
+               {
+                       // Backspace or Ctrl-Backspace pressed
+                       // delete character / word to the left
+                       ChatPrompt::CursorOpScope scope =
+                               event.KeyInput.Control ?
+                               ChatPrompt::CURSOROP_SCOPE_WORD :
+                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_DELETE,
+                               ChatPrompt::CURSOROP_DIR_LEFT,
+                               scope);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_DELETE)
+               {
+                       // Delete or Ctrl-Delete pressed
+                       // delete character / word to the right
+                       ChatPrompt::CursorOpScope scope =
+                               event.KeyInput.Control ?
+                               ChatPrompt::CURSOROP_SCOPE_WORD :
+                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_DELETE,
+                               ChatPrompt::CURSOROP_DIR_RIGHT,
+                               scope);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control)
+               {
+                       // Ctrl-A pressed
+                       // Select all text
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_SELECT,
+                               ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
+                               ChatPrompt::CURSOROP_SCOPE_LINE);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control)
+               {
+                       // Ctrl-C pressed
+                       // Copy text to clipboard
+                       if (prompt.getCursorLength() <= 0)
+                               return true;
+                       std::wstring wselected = prompt.getSelection();
+                       std::string selected(wselected.begin(), wselected.end());
+                       Environment->getOSOperator()->copyToClipboard(selected.c_str());
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control)
+               {
+                       // Ctrl-V pressed
+                       // paste text from clipboard
+                       if (prompt.getCursorLength() > 0) {
+                               // Delete selected section of text
+                               prompt.cursorOperation(
+                                       ChatPrompt::CURSOROP_DELETE,
+                                       ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
+                                       ChatPrompt::CURSOROP_SCOPE_SELECTION);
+                       }
+                       IOSOperator *os_operator = Environment->getOSOperator();
+                       const c8 *text = os_operator->getTextFromClipboard();
+                       if (!text)
+                               return true;
+                       std::basic_string<unsigned char> str((const unsigned char*)text);
+                       prompt.input(std::wstring(str.begin(), str.end()));
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control)
+               {
+                       // Ctrl-X pressed
+                       // Cut text to clipboard
+                       if (prompt.getCursorLength() <= 0)
+                               return true;
+                       std::wstring wselected = prompt.getSelection();
+                       std::string selected(wselected.begin(), wselected.end());
+                       Environment->getOSOperator()->copyToClipboard(selected.c_str());
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_DELETE,
+                               ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
+                               ChatPrompt::CURSOROP_SCOPE_SELECTION);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_U && event.KeyInput.Control)
+               {
+                       // Ctrl-U pressed
+                       // kill line to left end
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_DELETE,
+                               ChatPrompt::CURSOROP_DIR_LEFT,
+                               ChatPrompt::CURSOROP_SCOPE_LINE);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_KEY_K && event.KeyInput.Control)
+               {
+                       // Ctrl-K pressed
+                       // kill line to right end
+                       prompt.cursorOperation(
+                               ChatPrompt::CURSOROP_DELETE,
+                               ChatPrompt::CURSOROP_DIR_RIGHT,
+                               ChatPrompt::CURSOROP_SCOPE_LINE);
+                       return true;
+               }
+               else if(event.KeyInput.Key == KEY_TAB)
+               {
+                       // Tab or Shift-Tab pressed
+                       // Nick completion
+                       std::list<std::string> names = m_client->getConnectedPlayerNames();
+                       bool backwards = event.KeyInput.Shift;
+                       prompt.nickCompletion(names, backwards);
+                       return true;
+               } else if (!iswcntrl(event.KeyInput.Char) && !event.KeyInput.Control) {
+                       #if defined(__linux__) && (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9)
+                               wchar_t wc = L'_';
+                               mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
+                               prompt.input(wc);
+                       #else
+                               prompt.input(event.KeyInput.Char);
+                       #endif
+                       return true;
+               }
+       }
+       else if(event.EventType == EET_MOUSE_INPUT_EVENT)
+       {
+               if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
+               {
+                       s32 rows = myround(-3.0 * event.MouseInput.Wheel);
+                       m_chat_backend->scroll(rows);
+               }
+       }
+
+       return Parent ? Parent->OnEvent(event) : false;
+}
+
+void GUIChatConsole::setVisible(bool visible)
+{
+       m_open = visible;
+       IGUIElement::setVisible(visible);
+       if (!visible) {
+               m_height = 0;
+               recalculateConsolePosition();
+       }
+}
+
diff --git a/src/gui/guiChatConsole.h b/src/gui/guiChatConsole.h
new file mode 100644 (file)
index 0000000..ef8a876
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "irrlichttypes_extrabloated.h"
+#include "modalMenu.h"
+#include "chat.h"
+#include "config.h"
+
+class Client;
+
+class GUIChatConsole : public gui::IGUIElement
+{
+public:
+       GUIChatConsole(gui::IGUIEnvironment* env,
+                       gui::IGUIElement* parent,
+                       s32 id,
+                       ChatBackend* backend,
+                       Client* client,
+                       IMenuManager* menumgr);
+       virtual ~GUIChatConsole();
+
+       // Open the console (height = desired fraction of screen size)
+       // This doesn't open immediately but initiates an animation.
+       // You should call isOpenInhibited() before this.
+       void openConsole(f32 scale);
+
+       bool isOpen() const;
+
+       // Check if the console should not be opened at the moment
+       // This is to avoid reopening the console immediately after closing
+       bool isOpenInhibited() const;
+       // Close the console, equivalent to openConsole(0).
+       // This doesn't close immediately but initiates an animation.
+       void closeConsole();
+       // Close the console immediately, without animation.
+       void closeConsoleAtOnce();
+       // Set whether to close the console after the user presses enter.
+       void setCloseOnEnter(bool close) { m_close_on_enter = close; }
+
+       // Return the desired height (fraction of screen size)
+       // Zero if the console is closed or getting closed
+       f32 getDesiredHeight() const;
+
+       // Replace actual line when adding the actual to the history (if there is any)
+       void replaceAndAddToHistory(std::wstring line);
+
+       // Change how the cursor looks
+       void setCursor(
+               bool visible,
+               bool blinking = false,
+               f32 blink_speed = 1.0,
+               f32 relative_height = 1.0);
+
+       // Irrlicht draw method
+       virtual void draw();
+
+       bool canTakeFocus(gui::IGUIElement* element) { return false; }
+
+       virtual bool OnEvent(const SEvent& event);
+
+       virtual void setVisible(bool visible);
+
+private:
+       void reformatConsole();
+       void recalculateConsolePosition();
+
+       // These methods are called by draw
+       void animate(u32 msec);
+       void drawBackground();
+       void drawText();
+       void drawPrompt();
+
+private:
+       ChatBackend* m_chat_backend;
+       Client* m_client;
+       IMenuManager* m_menumgr;
+
+       // current screen size
+       v2u32 m_screensize;
+
+       // used to compute how much time passed since last animate()
+       u64 m_animate_time_old;
+
+       // should the console be opened or closed?
+       bool m_open = false;
+       // should it close after you press enter?
+       bool m_close_on_enter = false;
+       // current console height [pixels]
+       s32 m_height = 0;
+       // desired height [pixels]
+       f32 m_desired_height = 0.0f;
+       // desired height [screen height fraction]
+       f32 m_desired_height_fraction = 0.0f;
+       // console open/close animation speed [screen height fraction / second]
+       f32 m_height_speed = 5.0f;
+       // if nonzero, opening the console is inhibited [milliseconds]
+       u32 m_open_inhibited = 0;
+
+       // cursor blink frame (16-bit value)
+       // cursor is off during [0,32767] and on during [32768,65535]
+       u32 m_cursor_blink = 0;
+       // cursor blink speed [on/off toggles / second]
+       f32 m_cursor_blink_speed = 0.0f;
+       // cursor height [line height]
+       f32 m_cursor_height = 0.0f;
+
+       // background texture
+       video::ITexture *m_background = nullptr;
+       // background color (including alpha)
+       video::SColor m_background_color = video::SColor(255, 0, 0, 0);
+
+       // font
+       gui::IGUIFont *m_font = nullptr;
+       v2u32 m_fontsize;
+};
diff --git a/src/gui/guiEditBoxWithScrollbar.cpp b/src/gui/guiEditBoxWithScrollbar.cpp
new file mode 100644 (file)
index 0000000..d4d2a0c
--- /dev/null
@@ -0,0 +1,1524 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt
+// Modified by Mustapha T.
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#include "guiEditBoxWithScrollbar.h"
+
+#include "IGUISkin.h"
+#include "IGUIEnvironment.h"
+#include "IGUIFont.h"
+#include "IVideoDriver.h"
+#include "rect.h"
+#include "porting.h"
+#include "Keycodes.h"
+
+
+/*
+todo:
+optional scrollbars [done]
+ctrl+left/right to select word
+double click/ctrl click: word select + drag to select whole words, triple click to select line
+optional? dragging selected text
+numerical
+*/
+
+
+//! constructor
+GUIEditBoxWithScrollBar::GUIEditBoxWithScrollBar(const wchar_t* text, bool border,
+       IGUIEnvironment* environment, IGUIElement* parent, s32 id,
+       const core::rect<s32>& rectangle, bool writable, bool has_vscrollbar)
+       : IGUIEditBox(environment, parent, id, rectangle), m_mouse_marking(false),
+       m_border(border), m_background(true), m_override_color_enabled(false), m_mark_begin(0), m_mark_end(0),
+       m_override_color(video::SColor(101, 255, 255, 255)), m_override_font(0), m_last_break_font(0),
+       m_operator(0), m_blink_start_time(0), m_cursor_pos(0), m_hscroll_pos(0), m_vscroll_pos(0), m_max(0),
+       m_word_wrap(false), m_multiline(false), m_autoscroll(true), m_passwordbox(false),
+       m_passwordchar(L'*'), m_halign(EGUIA_UPPERLEFT), m_valign(EGUIA_CENTER),
+       m_current_text_rect(0, 0, 1, 1), m_frame_rect(rectangle),
+       m_scrollbar_width(0), m_vscrollbar(NULL), m_writable(writable),
+       m_bg_color_used(false)
+{
+#ifdef _DEBUG
+       setDebugName("GUIEditBoxWithScrollBar");
+#endif
+
+
+       Text = text;
+
+       if (Environment)
+               m_operator = Environment->getOSOperator();
+
+       if (m_operator)
+               m_operator->grab();
+
+       // this element can be tabbed to
+       setTabStop(true);
+       setTabOrder(-1);
+
+       if (has_vscrollbar) {
+               createVScrollBar();
+       }
+
+       calculateFrameRect();
+       breakText();
+
+       calculateScrollPos();
+       setWritable(writable);
+}
+
+
+//! destructor
+GUIEditBoxWithScrollBar::~GUIEditBoxWithScrollBar()
+{
+       if (m_override_font)
+               m_override_font->drop();
+
+       if (m_operator)
+               m_operator->drop();
+
+       m_vscrollbar->remove();
+}
+
+
+//! Sets another skin independent font.
+void GUIEditBoxWithScrollBar::setOverrideFont(IGUIFont* font)
+{
+       if (m_override_font == font)
+               return;
+
+       if (m_override_font)
+               m_override_font->drop();
+
+       m_override_font = font;
+
+       if (m_override_font)
+               m_override_font->grab();
+
+       breakText();
+}
+
+//! Gets the override font (if any)
+IGUIFont * GUIEditBoxWithScrollBar::getOverrideFont() const
+{
+       return m_override_font;
+}
+
+//! Get the font which is used right now for drawing
+IGUIFont* GUIEditBoxWithScrollBar::getActiveFont() const
+{
+       if (m_override_font)
+               return m_override_font;
+       IGUISkin* skin = Environment->getSkin();
+       if (skin)
+               return skin->getFont();
+       return 0;
+}
+
+//! Sets another color for the text.
+void GUIEditBoxWithScrollBar::setOverrideColor(video::SColor color)
+{
+       m_override_color = color;
+       m_override_color_enabled = true;
+}
+
+
+video::SColor GUIEditBoxWithScrollBar::getOverrideColor() const
+{
+       return m_override_color;
+}
+
+
+//! Turns the border on or off
+void GUIEditBoxWithScrollBar::setDrawBorder(bool border)
+{
+       m_border = border;
+}
+
+//! Sets whether to draw the background
+void GUIEditBoxWithScrollBar::setDrawBackground(bool draw)
+{
+       m_background = draw;
+}
+
+//! Sets if the text should use the overide color or the color in the gui skin.
+void GUIEditBoxWithScrollBar::enableOverrideColor(bool enable)
+{
+       m_override_color_enabled = enable;
+}
+
+bool GUIEditBoxWithScrollBar::isOverrideColorEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return m_override_color_enabled;
+}
+
+//! Enables or disables word wrap
+void GUIEditBoxWithScrollBar::setWordWrap(bool enable)
+{
+       m_word_wrap = enable;
+       breakText();
+}
+
+
+void GUIEditBoxWithScrollBar::updateAbsolutePosition()
+{
+       core::rect<s32> old_absolute_rect(AbsoluteRect);
+       IGUIElement::updateAbsolutePosition();
+       if (old_absolute_rect != AbsoluteRect) {
+               calculateFrameRect();
+               breakText();
+               calculateScrollPos();
+       }
+}
+
+//! Checks if word wrap is enabled
+bool GUIEditBoxWithScrollBar::isWordWrapEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return m_word_wrap;
+}
+
+
+//! Enables or disables newlines.
+void GUIEditBoxWithScrollBar::setMultiLine(bool enable)
+{
+       m_multiline = enable;
+}
+
+
+//! Checks if multi line editing is enabled
+bool GUIEditBoxWithScrollBar::isMultiLineEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return m_multiline;
+}
+
+
+void GUIEditBoxWithScrollBar::setPasswordBox(bool password_box, wchar_t password_char)
+{
+       m_passwordbox = password_box;
+       if (m_passwordbox) {
+               m_passwordchar = password_char;
+               setMultiLine(false);
+               setWordWrap(false);
+               m_broken_text.clear();
+       }
+}
+
+
+bool GUIEditBoxWithScrollBar::isPasswordBox() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return m_passwordbox;
+}
+
+
+//! Sets text justification
+void GUIEditBoxWithScrollBar::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
+{
+       m_halign = horizontal;
+       m_valign = vertical;
+}
+
+
+//! called if an event happened.
+bool GUIEditBoxWithScrollBar::OnEvent(const SEvent& event)
+{
+       if (isEnabled()) {
+               switch (event.EventType)
+               {
+               case EET_GUI_EVENT:
+                       if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) {
+                               if (event.GUIEvent.Caller == this) {
+                                       m_mouse_marking = false;
+                                       setTextMarkers(0, 0);
+                               }
+                       }
+                       break;
+               case EET_KEY_INPUT_EVENT:
+                       if (processKey(event))
+                               return true;
+                       break;
+               case EET_MOUSE_INPUT_EVENT:
+                       if (processMouse(event))
+                               return true;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return IGUIElement::OnEvent(event);
+}
+
+
+bool GUIEditBoxWithScrollBar::processKey(const SEvent& event)
+{
+       if (!m_writable) {
+               return false;
+       }
+
+       if (!event.KeyInput.PressedDown)
+               return false;
+
+       bool text_changed = false;
+       s32 new_mark_begin = m_mark_begin;
+       s32 new_mark_end = m_mark_end;
+
+       // control shortcut handling
+
+       if (event.KeyInput.Control) {
+
+               // german backlash '\' entered with control + '?'
+               if (event.KeyInput.Char == '\\') {
+                       inputChar(event.KeyInput.Char);
+                       return true;
+               }
+
+               switch (event.KeyInput.Key) {
+               case KEY_KEY_A:
+                       // select all
+                       new_mark_begin = 0;
+                       new_mark_end = Text.size();
+                       break;
+               case KEY_KEY_C:
+                       // copy to clipboard
+                       if (!m_passwordbox && m_operator && m_mark_begin != m_mark_end)
+                       {
+                               const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+                               const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+                               core::stringc s;
+                               s = Text.subString(realmbgn, realmend - realmbgn).c_str();
+                               m_operator->copyToClipboard(s.c_str());
+                       }
+                       break;
+               case KEY_KEY_X:
+                       // cut to the clipboard
+                       if (!m_passwordbox && m_operator && m_mark_begin != m_mark_end) {
+                               const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+                               const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+                               // copy
+                               core::stringc sc;
+                               sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
+                               m_operator->copyToClipboard(sc.c_str());
+
+                               if (isEnabled())
+                               {
+                                       // delete
+                                       core::stringw s;
+                                       s = Text.subString(0, realmbgn);
+                                       s.append(Text.subString(realmend, Text.size() - realmend));
+                                       Text = s;
+
+                                       m_cursor_pos = realmbgn;
+                                       new_mark_begin = 0;
+                                       new_mark_end = 0;
+                                       text_changed = true;
+                               }
+                       }
+                       break;
+               case KEY_KEY_V:
+                       if (!isEnabled())
+                               break;
+
+                       // paste from the clipboard
+                       if (m_operator) {
+                               const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+                               const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+                               // add new character
+                               const c8* p = m_operator->getTextFromClipboard();
+                               if (p) {
+                                       if (m_mark_begin == m_mark_end) {
+                                               // insert text
+                                               core::stringw s = Text.subString(0, m_cursor_pos);
+                                               s.append(p);
+                                               s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
+
+                                               if (!m_max || s.size() <= m_max) // thx to Fish FH for fix
+                                               {
+                                                       Text = s;
+                                                       s = p;
+                                                       m_cursor_pos += s.size();
+                                               }
+                                       } else {
+                                               // replace text
+
+                                               core::stringw s = Text.subString(0, realmbgn);
+                                               s.append(p);
+                                               s.append(Text.subString(realmend, Text.size() - realmend));
+
+                                               if (!m_max || s.size() <= m_max)  // thx to Fish FH for fix
+                                               {
+                                                       Text = s;
+                                                       s = p;
+                                                       m_cursor_pos = realmbgn + s.size();
+                                               }
+                                       }
+                               }
+
+                               new_mark_begin = 0;
+                               new_mark_end = 0;
+                               text_changed = true;
+                       }
+                       break;
+               case KEY_HOME:
+                       // move/highlight to start of text
+                       if (event.KeyInput.Shift) {
+                               new_mark_end = m_cursor_pos;
+                               new_mark_begin = 0;
+                               m_cursor_pos = 0;
+                       } else {
+                               m_cursor_pos = 0;
+                               new_mark_begin = 0;
+                               new_mark_end = 0;
+                       }
+                       break;
+               case KEY_END:
+                       // move/highlight to end of text
+                       if (event.KeyInput.Shift) {
+                               new_mark_begin = m_cursor_pos;
+                               new_mark_end = Text.size();
+                               m_cursor_pos = 0;
+                       } else {
+                               m_cursor_pos = Text.size();
+                               new_mark_begin = 0;
+                               new_mark_end = 0;
+                       }
+                       break;
+               default:
+                       return false;
+               }
+       }
+       // default keyboard handling
+       else
+               switch (event.KeyInput.Key)     {
+               case KEY_END:
+               {
+                       s32 p = Text.size();
+                       if (m_word_wrap || m_multiline) {
+                               p = getLineFromPos(m_cursor_pos);
+                               p = m_broken_text_positions[p] + (s32)m_broken_text[p].size();
+                               if (p > 0 && (Text[p - 1] == L'\r' || Text[p - 1] == L'\n'))
+                                       p -= 1;
+                       }
+
+                       if (event.KeyInput.Shift) {
+                               if (m_mark_begin == m_mark_end)
+                                       new_mark_begin = m_cursor_pos;
+
+                               new_mark_end = p;
+                       } else {
+                               new_mark_begin = 0;
+                               new_mark_end = 0;
+                       }
+                       m_cursor_pos = p;
+                       m_blink_start_time = porting::getTimeMs();
+               }
+               break;
+               case KEY_HOME:
+               {
+
+                       s32 p = 0;
+                       if (m_word_wrap || m_multiline) {
+                               p = getLineFromPos(m_cursor_pos);
+                               p = m_broken_text_positions[p];
+                       }
+
+                       if (event.KeyInput.Shift) {
+                               if (m_mark_begin == m_mark_end)
+                                       new_mark_begin = m_cursor_pos;
+                               new_mark_end = p;
+                       } else {
+                               new_mark_begin = 0;
+                               new_mark_end = 0;
+                       }
+                       m_cursor_pos = p;
+                       m_blink_start_time = porting::getTimeMs();
+               }
+               break;
+               case KEY_RETURN:
+                       if (m_multiline) {
+                               inputChar(L'\n');
+                       } else {
+                               calculateScrollPos();
+                               sendGuiEvent(EGET_EDITBOX_ENTER);
+                       }
+                       return true;
+               case KEY_LEFT:
+
+                       if (event.KeyInput.Shift) {
+                               if (m_cursor_pos > 0) {
+                                       if (m_mark_begin == m_mark_end)
+                                               new_mark_begin = m_cursor_pos;
+
+                                       new_mark_end = m_cursor_pos - 1;
+                               }
+                       } else {
+                               new_mark_begin = 0;
+                               new_mark_end = 0;
+                       }
+
+                       if (m_cursor_pos > 0)
+                               m_cursor_pos--;
+                       m_blink_start_time = porting::getTimeMs();
+                       break;
+
+               case KEY_RIGHT:
+                       if (event.KeyInput.Shift) {
+                               if (Text.size() > (u32)m_cursor_pos) {
+                                       if (m_mark_begin == m_mark_end)
+                                               new_mark_begin = m_cursor_pos;
+
+                                       new_mark_end = m_cursor_pos + 1;
+                               }
+                       } else {
+                               new_mark_begin = 0;
+                               new_mark_end = 0;
+                       }
+
+                       if (Text.size() > (u32)m_cursor_pos)
+                               m_cursor_pos++;
+                       m_blink_start_time = porting::getTimeMs();
+                       break;
+               case KEY_UP:
+                       if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
+                               s32 lineNo = getLineFromPos(m_cursor_pos);
+                               s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos : (m_mark_begin > m_mark_end ? m_mark_begin : m_mark_end);
+                               if (lineNo > 0) {
+                                       s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
+                                       if ((s32)m_broken_text[lineNo - 1].size() < cp)
+                                               m_cursor_pos = m_broken_text_positions[lineNo - 1] + core::max_((u32)1, m_broken_text[lineNo - 1].size()) - 1;
+                                       else
+                                               m_cursor_pos = m_broken_text_positions[lineNo - 1] + cp;
+                               }
+
+                               if (event.KeyInput.Shift) {
+                                       new_mark_begin = mb;
+                                       new_mark_end = m_cursor_pos;
+                               } else {
+                                       new_mark_begin = 0;
+                                       new_mark_end = 0;
+                               }
+                       } else {
+                               return false;
+                       }
+                       break;
+               case KEY_DOWN:
+                       if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
+                               s32 lineNo = getLineFromPos(m_cursor_pos);
+                               s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos : (m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end);
+                               if (lineNo < (s32)m_broken_text.size() - 1)
+                               {
+                                       s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
+                                       if ((s32)m_broken_text[lineNo + 1].size() < cp)
+                                               m_cursor_pos = m_broken_text_positions[lineNo + 1] + core::max_((u32)1, m_broken_text[lineNo + 1].size()) - 1;
+                                       else
+                                               m_cursor_pos = m_broken_text_positions[lineNo + 1] + cp;
+                               }
+
+                               if (event.KeyInput.Shift) {
+                                       new_mark_begin = mb;
+                                       new_mark_end = m_cursor_pos;
+                               } else {
+                                       new_mark_begin = 0;
+                                       new_mark_end = 0;
+                               }
+
+                       } else {
+                               return false;
+                       }
+                       break;
+
+               case KEY_BACK:
+                       if (!isEnabled())
+                               break;
+
+                       if (Text.size()) {
+                               core::stringw s;
+
+                               if (m_mark_begin != m_mark_end) {
+                                       // delete marked text
+                                       const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+                                       const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+                                       s = Text.subString(0, realmbgn);
+                                       s.append(Text.subString(realmend, Text.size() - realmend));
+                                       Text = s;
+
+                                       m_cursor_pos = realmbgn;
+                               } else {
+                                       // delete text behind cursor
+                                       if (m_cursor_pos > 0)
+                                               s = Text.subString(0, m_cursor_pos - 1);
+                                       else
+                                               s = L"";
+                                       s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
+                                       Text = s;
+                                       --m_cursor_pos;
+                               }
+
+                               if (m_cursor_pos < 0)
+                                       m_cursor_pos = 0;
+                               m_blink_start_time = porting::getTimeMs(); // os::Timer::getTime();
+                               new_mark_begin = 0;
+                               new_mark_end = 0;
+                               text_changed = true;
+                       }
+                       break;
+               case KEY_DELETE:
+                       if (!isEnabled())
+                               break;
+
+                       if (Text.size() != 0) {
+                               core::stringw s;
+
+                               if (m_mark_begin != m_mark_end) {
+                                       // delete marked text
+                                       const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+                                       const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+                                       s = Text.subString(0, realmbgn);
+                                       s.append(Text.subString(realmend, Text.size() - realmend));
+                                       Text = s;
+
+                                       m_cursor_pos = realmbgn;
+                               } else {
+                                       // delete text before cursor
+                                       s = Text.subString(0, m_cursor_pos);
+                                       s.append(Text.subString(m_cursor_pos + 1, Text.size() - m_cursor_pos - 1));
+                                       Text = s;
+                               }
+
+                               if (m_cursor_pos > (s32)Text.size())
+                                       m_cursor_pos = (s32)Text.size();
+
+                               m_blink_start_time = porting::getTimeMs(); // os::Timer::getTime();
+                               new_mark_begin = 0;
+                               new_mark_end = 0;
+                               text_changed = true;
+                       }
+                       break;
+
+               case KEY_ESCAPE:
+               case KEY_TAB:
+               case KEY_SHIFT:
+               case KEY_F1:
+               case KEY_F2:
+               case KEY_F3:
+               case KEY_F4:
+               case KEY_F5:
+               case KEY_F6:
+               case KEY_F7:
+               case KEY_F8:
+               case KEY_F9:
+               case KEY_F10:
+               case KEY_F11:
+               case KEY_F12:
+               case KEY_F13:
+               case KEY_F14:
+               case KEY_F15:
+               case KEY_F16:
+               case KEY_F17:
+               case KEY_F18:
+               case KEY_F19:
+               case KEY_F20:
+               case KEY_F21:
+               case KEY_F22:
+               case KEY_F23:
+               case KEY_F24:
+                       // ignore these keys
+                       return false;
+
+               default:
+                       inputChar(event.KeyInput.Char);
+                       return true;
+               }
+
+       // Set new text markers
+       setTextMarkers(new_mark_begin, new_mark_end);
+
+       // break the text if it has changed
+       if (text_changed) {
+               breakText();
+               calculateScrollPos();
+               sendGuiEvent(EGET_EDITBOX_CHANGED);
+       }
+       else
+       {
+               calculateScrollPos();
+       }
+
+       return true;
+}
+
+
+//! draws the element and its children
+void GUIEditBoxWithScrollBar::draw()
+{
+       if (!IsVisible)
+               return;
+
+       const bool focus = Environment->hasFocus(this);
+
+       IGUISkin* skin = Environment->getSkin();
+       if (!skin)
+               return;
+
+       video::SColor default_bg_color;
+       video::SColor bg_color;
+
+       default_bg_color = m_writable ? skin->getColor(EGDC_WINDOW) : video::SColor(0);
+       bg_color = m_bg_color_used ? m_bg_color : default_bg_color;
+
+       if (!m_border && m_background) {
+               skin->draw2DRectangle(this, bg_color, AbsoluteRect, &AbsoluteClippingRect);
+       }
+
+       // draw the border
+
+       if (m_border) {
+
+               if (m_writable) {
+                       skin->draw3DSunkenPane(this, bg_color, false, m_background,
+                               AbsoluteRect, &AbsoluteClippingRect);
+               }
+
+               calculateFrameRect();
+       }
+
+       core::rect<s32> local_clip_rect = m_frame_rect;
+       local_clip_rect.clipAgainst(AbsoluteClippingRect);
+
+       // draw the text
+
+       IGUIFont* font = getActiveFont();
+
+       s32 cursor_line = 0;
+       s32 charcursorpos = 0;
+
+       if (font) {
+               if (m_last_break_font != font) {
+                       breakText();
+               }
+
+               // calculate cursor pos
+
+               core::stringw *txt_line = &Text;
+               s32 start_pos = 0;
+
+               core::stringw s, s2;
+
+               // get mark position
+               const bool ml = (!m_passwordbox && (m_word_wrap || m_multiline));
+               const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+               const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+               const s32 hline_start = ml ? getLineFromPos(realmbgn) : 0;
+               const s32 hline_count = ml ? getLineFromPos(realmend) - hline_start + 1 : 1;
+               const s32 line_count = ml ? m_broken_text.size() : 1;
+
+               // Save the override color information.
+               // Then, alter it if the edit box is disabled.
+               const bool prevOver = m_override_color_enabled;
+               const video::SColor prevColor = m_override_color;
+
+               if (Text.size()) {
+                       if (!isEnabled() && !m_override_color_enabled) {
+                               m_override_color_enabled = true;
+                               m_override_color = skin->getColor(EGDC_GRAY_TEXT);
+                       }
+
+                       for (s32 i = 0; i < line_count; ++i) {
+                               setTextRect(i);
+
+                               // clipping test - don't draw anything outside the visible area
+                               core::rect<s32> c = local_clip_rect;
+                               c.clipAgainst(m_current_text_rect);
+                               if (!c.isValid())
+                                       continue;
+
+                               // get current line
+                               if (m_passwordbox) {
+                                       if (m_broken_text.size() != 1) {
+                                               m_broken_text.clear();
+                                               m_broken_text.push_back(core::stringw());
+                                       }
+                                       if (m_broken_text[0].size() != Text.size()){
+                                               m_broken_text[0] = Text;
+                                               for (u32 q = 0; q < Text.size(); ++q)
+                                               {
+                                                       m_broken_text[0][q] = m_passwordchar;
+                                               }
+                                       }
+                                       txt_line = &m_broken_text[0];
+                                       start_pos = 0;
+                               } else {
+                                       txt_line = ml ? &m_broken_text[i] : &Text;
+                                       start_pos = ml ? m_broken_text_positions[i] : 0;
+                               }
+
+
+                               // draw normal text
+                               font->draw(txt_line->c_str(), m_current_text_rect,
+                                       m_override_color_enabled ? m_override_color : skin->getColor(EGDC_BUTTON_TEXT),
+                                       false, true, &local_clip_rect);
+
+                               // draw mark and marked text
+                               if (focus && m_mark_begin != m_mark_end && i >= hline_start && i < hline_start + hline_count) {
+
+                                       s32 mbegin = 0, mend = 0;
+                                       s32 lineStartPos = 0, lineEndPos = txt_line->size();
+
+                                       if (i == hline_start) {
+                                               // highlight start is on this line
+                                               s = txt_line->subString(0, realmbgn - start_pos);
+                                               mbegin = font->getDimension(s.c_str()).Width;
+
+                                               // deal with kerning
+                                               mbegin += font->getKerningWidth(
+                                                       &((*txt_line)[realmbgn - start_pos]),
+                                                       realmbgn - start_pos > 0 ? &((*txt_line)[realmbgn - start_pos - 1]) : 0);
+
+                                               lineStartPos = realmbgn - start_pos;
+                                       }
+                                       if (i == hline_start + hline_count - 1) {
+                                               // highlight end is on this line
+                                               s2 = txt_line->subString(0, realmend - start_pos);
+                                               mend = font->getDimension(s2.c_str()).Width;
+                                               lineEndPos = (s32)s2.size();
+                                       } else {
+                                               mend = font->getDimension(txt_line->c_str()).Width;
+                                       }
+
+
+                                       m_current_text_rect.UpperLeftCorner.X += mbegin;
+                                       m_current_text_rect.LowerRightCorner.X = m_current_text_rect.UpperLeftCorner.X + mend - mbegin;
+
+
+                                       // draw mark
+                                       skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), m_current_text_rect, &local_clip_rect);
+
+                                       // draw marked text
+                                       s = txt_line->subString(lineStartPos, lineEndPos - lineStartPos);
+
+                                       if (s.size())
+                                               font->draw(s.c_str(), m_current_text_rect,
+                                                       m_override_color_enabled ? m_override_color : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
+                                                       false, true, &local_clip_rect);
+
+                               }
+                       }
+
+                       // Return the override color information to its previous settings.
+                       m_override_color_enabled = prevOver;
+                       m_override_color = prevColor;
+               }
+
+               // draw cursor
+               if (IsEnabled && m_writable) {
+                       if (m_word_wrap || m_multiline) {
+                               cursor_line = getLineFromPos(m_cursor_pos);
+                               txt_line = &m_broken_text[cursor_line];
+                               start_pos = m_broken_text_positions[cursor_line];
+                       }
+                       s = txt_line->subString(0, m_cursor_pos - start_pos);
+                       charcursorpos = font->getDimension(s.c_str()).Width +
+                               font->getKerningWidth(L"_", m_cursor_pos - start_pos > 0 ? &((*txt_line)[m_cursor_pos - start_pos - 1]) : 0);
+
+                       if (focus && (porting::getTimeMs() - m_blink_start_time) % 700 < 350) {
+                               setTextRect(cursor_line);
+                               m_current_text_rect.UpperLeftCorner.X += charcursorpos;
+
+                               font->draw(L"_", m_current_text_rect,
+                                       m_override_color_enabled ? m_override_color : skin->getColor(EGDC_BUTTON_TEXT),
+                                       false, true, &local_clip_rect);
+                       }
+               }
+       }
+
+       // draw children
+       IGUIElement::draw();
+}
+
+
+//! Sets the new caption of this element.
+void GUIEditBoxWithScrollBar::setText(const wchar_t* text)
+{
+       Text = text;
+       if (u32(m_cursor_pos) > Text.size())
+               m_cursor_pos = Text.size();
+       m_hscroll_pos = 0;
+       breakText();
+}
+
+
+//! Enables or disables automatic scrolling with cursor position
+//! \param enable: If set to true, the text will move around with the cursor position
+void GUIEditBoxWithScrollBar::setAutoScroll(bool enable)
+{
+       m_autoscroll = enable;
+}
+
+
+//! Checks to see if automatic scrolling is enabled
+//! \return true if automatic scrolling is enabled, false if not
+bool GUIEditBoxWithScrollBar::isAutoScrollEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return m_autoscroll;
+}
+
+
+//! Gets the area of the text in the edit box
+//! \return Returns the size in pixels of the text
+core::dimension2du GUIEditBoxWithScrollBar::getTextDimension()
+{
+       core::rect<s32> ret;
+
+       setTextRect(0);
+       ret = m_current_text_rect;
+
+       for (u32 i = 1; i < m_broken_text.size(); ++i) {
+               setTextRect(i);
+               ret.addInternalPoint(m_current_text_rect.UpperLeftCorner);
+               ret.addInternalPoint(m_current_text_rect.LowerRightCorner);
+       }
+
+       return core::dimension2du(ret.getSize());
+}
+
+
+//! Sets the maximum amount of characters which may be entered in the box.
+//! \param max: Maximum amount of characters. If 0, the character amount is
+//! infinity.
+void GUIEditBoxWithScrollBar::setMax(u32 max)
+{
+       m_max = max;
+
+       if (Text.size() > m_max && m_max != 0)
+               Text = Text.subString(0, m_max);
+}
+
+
+//! Returns maximum amount of characters, previously set by setMax();
+u32 GUIEditBoxWithScrollBar::getMax() const
+{
+       return m_max;
+}
+
+
+bool GUIEditBoxWithScrollBar::processMouse(const SEvent& event)
+{
+       switch (event.MouseInput.Event)
+       {
+       case irr::EMIE_LMOUSE_LEFT_UP:
+               if (Environment->hasFocus(this)) {
+                       m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+                       if (m_mouse_marking) {
+                               setTextMarkers(m_mark_begin, m_cursor_pos);
+                       }
+                       m_mouse_marking = false;
+                       calculateScrollPos();
+                       return true;
+               }
+               break;
+       case irr::EMIE_MOUSE_MOVED:
+       {
+               if (m_mouse_marking) {
+                       m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+                       setTextMarkers(m_mark_begin, m_cursor_pos);
+                       calculateScrollPos();
+                       return true;
+               }
+       }
+       break;
+       case EMIE_LMOUSE_PRESSED_DOWN:
+
+               if (!Environment->hasFocus(this)) {
+                       m_blink_start_time = porting::getTimeMs();
+                       m_mouse_marking = true;
+                       m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+                       setTextMarkers(m_cursor_pos, m_cursor_pos);
+                       calculateScrollPos();
+                       return true;
+               } else {
+                       if (!AbsoluteClippingRect.isPointInside(
+                               core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y))) {
+                               return false;
+                       } else {
+                               // move cursor
+                               m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+
+                               s32 newMarkBegin = m_mark_begin;
+                               if (!m_mouse_marking)
+                                       newMarkBegin = m_cursor_pos;
+
+                               m_mouse_marking = true;
+                               setTextMarkers(newMarkBegin, m_cursor_pos);
+                               calculateScrollPos();
+                               return true;
+                       }
+               }
+       default:
+               break;
+       }
+
+       return false;
+}
+
+
+s32 GUIEditBoxWithScrollBar::getCursorPos(s32 x, s32 y)
+{
+       IGUIFont* font = getActiveFont();
+
+       const u32 line_count = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1;
+
+       core::stringw *txt_line = 0;
+       s32 start_pos = 0;
+       x += 3;
+
+       for (u32 i = 0; i < line_count; ++i) {
+               setTextRect(i);
+               if (i == 0 && y < m_current_text_rect.UpperLeftCorner.Y)
+                       y = m_current_text_rect.UpperLeftCorner.Y;
+               if (i == line_count - 1 && y > m_current_text_rect.LowerRightCorner.Y)
+                       y = m_current_text_rect.LowerRightCorner.Y;
+
+               // is it inside this region?
+               if (y >= m_current_text_rect.UpperLeftCorner.Y && y <= m_current_text_rect.LowerRightCorner.Y) {
+                       // we've found the clicked line
+                       txt_line = (m_word_wrap || m_multiline) ? &m_broken_text[i] : &Text;
+                       start_pos = (m_word_wrap || m_multiline) ? m_broken_text_positions[i] : 0;
+                       break;
+               }
+       }
+
+       if (x < m_current_text_rect.UpperLeftCorner.X)
+               x = m_current_text_rect.UpperLeftCorner.X;
+
+       if (!txt_line)
+               return 0;
+
+       s32 idx = font->getCharacterFromPos(txt_line->c_str(), x - m_current_text_rect.UpperLeftCorner.X);
+
+       // click was on or left of the line
+       if (idx != -1)
+               return idx + start_pos;
+
+       // click was off the right edge of the line, go to end.
+       return txt_line->size() + start_pos;
+}
+
+
+//! Breaks the single text line.
+void GUIEditBoxWithScrollBar::breakText()
+{
+       if ((!m_word_wrap && !m_multiline))
+               return;
+
+       m_broken_text.clear(); // need to reallocate :/
+       m_broken_text_positions.clear();
+
+       IGUIFont* font = getActiveFont();
+       if (!font)
+               return;
+
+       m_last_break_font = font;
+
+       core::stringw line;
+       core::stringw word;
+       core::stringw whitespace;
+       s32 last_line_start = 0;
+       s32 size = Text.size();
+       s32 length = 0;
+       s32 el_width = RelativeRect.getWidth() - 6;
+       wchar_t c;
+
+       for (s32 i = 0; i < size; ++i) {
+               c = Text[i];
+               bool line_break = false;
+
+               if (c == L'\r') { // Mac or Windows breaks
+
+                       line_break = true;
+                       c = 0;
+                       if (Text[i + 1] == L'\n') { // Windows breaks
+                               // TODO: I (Michael) think that we shouldn't change the text given by the user for whatever reason.
+                               // Instead rework the cursor positioning to be able to handle this (but not in stable release
+                               // branch as users might already expect this behavior).
+                               Text.erase(i + 1);
+                               --size;
+                               if (m_cursor_pos > i)
+                                       --m_cursor_pos;
+                       }
+               } else if (c == L'\n') { // Unix breaks
+                       line_break = true;
+                       c = 0;
+               }
+
+               // don't break if we're not a multi-line edit box
+               if (!m_multiline)
+                       line_break = false;
+
+               if (c == L' ' || c == 0 || i == (size - 1)) {
+                       // here comes the next whitespace, look if
+                       // we can break the last word to the next line
+                       // We also break whitespace, otherwise cursor would vanish beside the right border.
+                       s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
+                       s32 worldlgth = font->getDimension(word.c_str()).Width;
+
+                       if (m_word_wrap && length + worldlgth + whitelgth > el_width && line.size() > 0) {
+                               // break to next line
+                               length = worldlgth;
+                               m_broken_text.push_back(line);
+                               m_broken_text_positions.push_back(last_line_start);
+                               last_line_start = i - (s32)word.size();
+                               line = word;
+                       } else {
+                               // add word to line
+                               line += whitespace;
+                               line += word;
+                               length += whitelgth + worldlgth;
+                       }
+
+                       word = L"";
+                       whitespace = L"";
+
+
+                       if (c)
+                               whitespace += c;
+
+                       // compute line break
+                       if (line_break) {
+                               line += whitespace;
+                               line += word;
+                               m_broken_text.push_back(line);
+                               m_broken_text_positions.push_back(last_line_start);
+                               last_line_start = i + 1;
+                               line = L"";
+                               word = L"";
+                               whitespace = L"";
+                               length = 0;
+                       }
+               } else {
+                       // yippee this is a word..
+                       word += c;
+               }
+       }
+
+       line += whitespace;
+       line += word;
+       m_broken_text.push_back(line);
+       m_broken_text_positions.push_back(last_line_start);
+}
+
+// TODO: that function does interpret VAlign according to line-index (indexed line is placed on top-center-bottom)
+// but HAlign according to line-width (pixels) and not by row.
+// Intuitively I suppose HAlign handling is better as VScrollPos should handle the line-scrolling.
+// But please no one change this without also rewriting (and this time fucking testing!!!) autoscrolling (I noticed this when fixing the old autoscrolling).
+void GUIEditBoxWithScrollBar::setTextRect(s32 line)
+{
+       if (line < 0)
+               return;
+
+       IGUIFont* font = getActiveFont();
+       if (!font)
+               return;
+
+       core::dimension2du d;
+
+       // get text dimension
+       const u32 line_count = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1;
+       if (m_word_wrap || m_multiline) {
+               d = font->getDimension(m_broken_text[line].c_str());
+       } else {
+               d = font->getDimension(Text.c_str());
+               d.Height = AbsoluteRect.getHeight();
+       }
+       d.Height += font->getKerningHeight();
+
+       // justification
+       switch (m_halign) {
+       case EGUIA_CENTER:
+               // align to h centre
+               m_current_text_rect.UpperLeftCorner.X = (m_frame_rect.getWidth() / 2) - (d.Width / 2);
+               m_current_text_rect.LowerRightCorner.X = (m_frame_rect.getWidth() / 2) + (d.Width / 2);
+               break;
+       case EGUIA_LOWERRIGHT:
+               // align to right edge
+               m_current_text_rect.UpperLeftCorner.X = m_frame_rect.getWidth() - d.Width;
+               m_current_text_rect.LowerRightCorner.X = m_frame_rect.getWidth();
+               break;
+       default:
+               // align to left edge
+               m_current_text_rect.UpperLeftCorner.X = 0;
+               m_current_text_rect.LowerRightCorner.X = d.Width;
+
+       }
+
+       switch (m_valign) {
+       case EGUIA_CENTER:
+               // align to v centre
+               m_current_text_rect.UpperLeftCorner.Y =
+                       (m_frame_rect.getHeight() / 2) - (line_count*d.Height) / 2 + d.Height*line;
+               break;
+       case EGUIA_LOWERRIGHT:
+               // align to bottom edge
+               m_current_text_rect.UpperLeftCorner.Y =
+                       m_frame_rect.getHeight() - line_count*d.Height + d.Height*line;
+               break;
+       default:
+               // align to top edge
+               m_current_text_rect.UpperLeftCorner.Y = d.Height*line;
+               break;
+       }
+
+       m_current_text_rect.UpperLeftCorner.X -= m_hscroll_pos;
+       m_current_text_rect.LowerRightCorner.X -= m_hscroll_pos;
+       m_current_text_rect.UpperLeftCorner.Y -= m_vscroll_pos;
+       m_current_text_rect.LowerRightCorner.Y = m_current_text_rect.UpperLeftCorner.Y + d.Height;
+
+       m_current_text_rect += m_frame_rect.UpperLeftCorner;
+}
+
+
+s32 GUIEditBoxWithScrollBar::getLineFromPos(s32 pos)
+{
+       if (!m_word_wrap && !m_multiline)
+               return 0;
+
+       s32 i = 0;
+       while (i < (s32)m_broken_text_positions.size()) {
+               if (m_broken_text_positions[i] > pos)
+                       return i - 1;
+               ++i;
+       }
+       return (s32)m_broken_text_positions.size() - 1;
+}
+
+
+void GUIEditBoxWithScrollBar::inputChar(wchar_t c)
+{
+       if (!isEnabled())
+               return;
+
+       if (c != 0)     {
+               if (Text.size() < m_max || m_max == 0) {
+                       core::stringw s;
+
+                       if (m_mark_begin != m_mark_end) {
+                               // replace marked text
+                               const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
+                               const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
+
+                               s = Text.subString(0, realmbgn);
+                               s.append(c);
+                               s.append(Text.subString(realmend, Text.size() - realmend));
+                               Text = s;
+                               m_cursor_pos = realmbgn + 1;
+                       } else {
+                               // add new character
+                               s = Text.subString(0, m_cursor_pos);
+                               s.append(c);
+                               s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
+                               Text = s;
+                               ++m_cursor_pos;
+                       }
+
+                       m_blink_start_time = porting::getTimeMs();
+                       setTextMarkers(0, 0);
+               }
+       }
+       breakText();
+       calculateScrollPos();
+       sendGuiEvent(EGET_EDITBOX_CHANGED);
+}
+
+// calculate autoscroll
+void GUIEditBoxWithScrollBar::calculateScrollPos()
+{
+       if (!m_autoscroll)
+               return;
+
+       IGUISkin* skin = Environment->getSkin();
+       if (!skin)
+               return;
+       IGUIFont* font = m_override_font ? m_override_font : skin->getFont();
+       if (!font)
+               return;
+
+       s32 curs_line = getLineFromPos(m_cursor_pos);
+       if (curs_line < 0)
+               return;
+       setTextRect(curs_line);
+       const bool has_broken_text = m_multiline || m_word_wrap;
+
+       // Check horizonal scrolling
+       // NOTE: Calculations different to vertical scrolling because setTextRect interprets VAlign relative to line but HAlign not relative to row
+       {
+               // get cursor position
+               IGUIFont* font = getActiveFont();
+               if (!font)
+                       return;
+
+               // get cursor area
+               irr::u32 cursor_width = font->getDimension(L"_").Width;
+               core::stringw *txt_line = has_broken_text ? &m_broken_text[curs_line] : &Text;
+               s32 cpos = has_broken_text ? m_cursor_pos - m_broken_text_positions[curs_line] : m_cursor_pos;  // column
+               s32 cstart = font->getDimension(txt_line->subString(0, cpos).c_str()).Width;            // pixels from text-start
+               s32 cend = cstart + cursor_width;
+               s32 txt_width = font->getDimension(txt_line->c_str()).Width;
+
+               if (txt_width < m_frame_rect.getWidth()) {
+                       // TODO: Needs a clean left and right gap removal depending on HAlign, similar to vertical scrolling tests for top/bottom.
+                       // This check just fixes the case where it was most noticable (text smaller than clipping area).
+
+                       m_hscroll_pos = 0;
+                       setTextRect(curs_line);
+               }
+
+               if (m_current_text_rect.UpperLeftCorner.X + cstart < m_frame_rect.UpperLeftCorner.X) {
+                       // cursor to the left of the clipping area
+                       m_hscroll_pos -= m_frame_rect.UpperLeftCorner.X - (m_current_text_rect.UpperLeftCorner.X + cstart);
+                       setTextRect(curs_line);
+
+                       // TODO: should show more characters to the left when we're scrolling left
+                       //      and the cursor reaches the border.
+               } else if (m_current_text_rect.UpperLeftCorner.X + cend > m_frame_rect.LowerRightCorner.X)      {
+                       // cursor to the right of the clipping area
+                       m_hscroll_pos += (m_current_text_rect.UpperLeftCorner.X + cend) - m_frame_rect.LowerRightCorner.X;
+                       setTextRect(curs_line);
+               }
+       }
+
+       // calculate vertical scrolling
+       if (has_broken_text) {
+               irr::u32 line_height = font->getDimension(L"A").Height + font->getKerningHeight();
+               // only up to 1 line fits?
+               if (line_height >= (irr::u32)m_frame_rect.getHeight()) {
+                       m_vscroll_pos = 0;
+                       setTextRect(curs_line);
+                       s32 unscrolledPos = m_current_text_rect.UpperLeftCorner.Y;
+                       s32 pivot = m_frame_rect.UpperLeftCorner.Y;
+                       switch (m_valign) {
+                       case EGUIA_CENTER:
+                               pivot += m_frame_rect.getHeight() / 2;
+                               unscrolledPos += line_height / 2;
+                               break;
+                       case EGUIA_LOWERRIGHT:
+                               pivot += m_frame_rect.getHeight();
+                               unscrolledPos += line_height;
+                               break;
+                       default:
+                               break;
+                       }
+                       m_vscroll_pos = unscrolledPos - pivot;
+                       setTextRect(curs_line);
+               } else {
+                       // First 2 checks are necessary when people delete lines
+                       setTextRect(0);
+                       if (m_current_text_rect.UpperLeftCorner.Y > m_frame_rect.UpperLeftCorner.Y && m_valign != EGUIA_LOWERRIGHT) {
+                               // first line is leaving a gap on top
+                               m_vscroll_pos = 0;
+                       } else if (m_valign != EGUIA_UPPERLEFT) {
+                               u32 lastLine = m_broken_text_positions.empty() ? 0 : m_broken_text_positions.size() - 1;
+                               setTextRect(lastLine);
+                               if (m_current_text_rect.LowerRightCorner.Y < m_frame_rect.LowerRightCorner.Y)
+                               {
+                                       // last line is leaving a gap on bottom
+                                       m_vscroll_pos -= m_frame_rect.LowerRightCorner.Y - m_current_text_rect.LowerRightCorner.Y;
+                               }
+                       }
+
+                       setTextRect(curs_line);
+                       if (m_current_text_rect.UpperLeftCorner.Y < m_frame_rect.UpperLeftCorner.Y) {
+                               // text above valid area
+                               m_vscroll_pos -= m_frame_rect.UpperLeftCorner.Y - m_current_text_rect.UpperLeftCorner.Y;
+                               setTextRect(curs_line);
+                       } else if (m_current_text_rect.LowerRightCorner.Y > m_frame_rect.LowerRightCorner.Y){
+                               // text below valid area
+                               m_vscroll_pos += m_current_text_rect.LowerRightCorner.Y - m_frame_rect.LowerRightCorner.Y;
+                               setTextRect(curs_line);
+                       }
+               }
+       }
+
+       if (m_vscrollbar) {
+               m_vscrollbar->setPos(m_vscroll_pos);
+       }
+}
+
+void GUIEditBoxWithScrollBar::calculateFrameRect()
+{
+       m_frame_rect = AbsoluteRect;
+
+
+       IGUISkin *skin = 0;
+       if (Environment)
+               skin = Environment->getSkin();
+       if (m_border && skin) {
+               m_frame_rect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X) + 1;
+               m_frame_rect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y) + 1;
+               m_frame_rect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X) + 1;
+               m_frame_rect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y) + 1;
+       }
+
+       updateVScrollBar();
+}
+
+//! set text markers
+void GUIEditBoxWithScrollBar::setTextMarkers(s32 begin, s32 end)
+{
+       if (begin != m_mark_begin || end != m_mark_end) {
+               m_mark_begin = begin;
+               m_mark_end = end;
+               sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
+       }
+}
+
+//! send some gui event to parent
+void GUIEditBoxWithScrollBar::sendGuiEvent(EGUI_EVENT_TYPE type)
+{
+       if (Parent) {
+               SEvent e;
+               e.EventType = EET_GUI_EVENT;
+               e.GUIEvent.Caller = this;
+               e.GUIEvent.Element = 0;
+               e.GUIEvent.EventType = type;
+
+               Parent->OnEvent(e);
+       }
+}
+
+//! create a vertical scroll bar
+void GUIEditBoxWithScrollBar::createVScrollBar()
+{
+       IGUISkin *skin = 0;
+       if (Environment)
+               skin = Environment->getSkin();
+
+       m_scrollbar_width = skin ? skin->getSize(gui::EGDS_SCROLLBAR_SIZE) : 16;
+
+       irr::core::rect<s32> scrollbarrect = m_frame_rect;
+       scrollbarrect.UpperLeftCorner.X += m_frame_rect.getWidth() - m_scrollbar_width;
+       m_vscrollbar = Environment->addScrollBar(false, scrollbarrect, getParent(), getID());
+       m_vscrollbar->setVisible(false);
+       m_vscrollbar->setSmallStep(1);
+       m_vscrollbar->setLargeStep(1);
+}
+
+void GUIEditBoxWithScrollBar::updateVScrollBar()
+{
+       if (!m_vscrollbar) {
+               return;
+       }
+
+       // OnScrollBarChanged(...)
+       if (m_vscrollbar->getPos() != m_vscroll_pos) {
+               s32 deltaScrollY = m_vscrollbar->getPos() - m_vscroll_pos;
+               m_current_text_rect.UpperLeftCorner.Y -= deltaScrollY;
+               m_current_text_rect.LowerRightCorner.Y -= deltaScrollY;
+
+               s32 scrollymax = getTextDimension().Height - m_frame_rect.getHeight();
+               if (scrollymax != m_vscrollbar->getMax()) {
+                       // manage a newline or a deleted line
+                       m_vscrollbar->setMax(scrollymax);
+                       calculateScrollPos();
+               } else {
+                       // manage a newline or a deleted line
+                       m_vscroll_pos = m_vscrollbar->getPos();
+               }
+       }
+
+       // check if a vertical scrollbar is needed ?
+       if (getTextDimension().Height > (u32) m_frame_rect.getHeight()) {
+               m_frame_rect.LowerRightCorner.X -= m_scrollbar_width;
+
+               s32 scrollymax = getTextDimension().Height - m_frame_rect.getHeight();
+               if (scrollymax != m_vscrollbar->getMax()) {
+                       m_vscrollbar->setMax(scrollymax);
+               }
+
+               if (!m_vscrollbar->isVisible()) {
+                       m_vscrollbar->setVisible(true);
+               }
+       } else {
+               if (m_vscrollbar->isVisible())
+               {
+                       m_vscrollbar->setVisible(false);
+                       m_vscroll_pos = 0;
+                       m_vscrollbar->setPos(0);
+                       m_vscrollbar->setMax(1);
+               }
+       }
+
+
+}
+
+//! set true if this editbox is writable
+void GUIEditBoxWithScrollBar::setWritable(bool writable)
+{
+       m_writable = writable;
+}
+
+//! Change the background color
+void GUIEditBoxWithScrollBar::setBackgroundColor(const video::SColor &bg_color)
+{
+       m_bg_color = bg_color;
+       m_bg_color_used = true;
+}
+
+//! Writes attributes of the element.
+void GUIEditBoxWithScrollBar::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options = 0) const
+{
+       // IGUIEditBox::serializeAttributes(out,options);
+
+       out->addBool("Border", m_border);
+       out->addBool("Background", m_background);
+       out->addBool("OverrideColorEnabled", m_override_color_enabled);
+       out->addColor("OverrideColor", m_override_color);
+       // out->addFont("OverrideFont", OverrideFont);
+       out->addInt("MaxChars", m_max);
+       out->addBool("WordWrap", m_word_wrap);
+       out->addBool("MultiLine", m_multiline);
+       out->addBool("AutoScroll", m_autoscroll);
+       out->addBool("PasswordBox", m_passwordbox);
+       core::stringw ch = L" ";
+       ch[0] = m_passwordchar;
+       out->addString("PasswordChar", ch.c_str());
+       out->addEnum("HTextAlign", m_halign, GUIAlignmentNames);
+       out->addEnum("VTextAlign", m_valign, GUIAlignmentNames);
+       out->addBool("Writable", m_writable);
+
+       IGUIEditBox::serializeAttributes(out, options);
+}
+
+
+//! Reads attributes of the element
+void GUIEditBoxWithScrollBar::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options = 0)
+{
+       IGUIEditBox::deserializeAttributes(in, options);
+
+       setDrawBorder(in->getAttributeAsBool("Border"));
+       setDrawBackground(in->getAttributeAsBool("Background"));
+       setOverrideColor(in->getAttributeAsColor("OverrideColor"));
+       enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
+       setMax(in->getAttributeAsInt("MaxChars"));
+       setWordWrap(in->getAttributeAsBool("WordWrap"));
+       setMultiLine(in->getAttributeAsBool("MultiLine"));
+       setAutoScroll(in->getAttributeAsBool("AutoScroll"));
+       core::stringw ch = in->getAttributeAsStringW("PasswordChar");
+
+       if (!ch.size())
+               setPasswordBox(in->getAttributeAsBool("PasswordBox"));
+       else
+               setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
+
+       setTextAlignment((EGUI_ALIGNMENT)in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
+               (EGUI_ALIGNMENT)in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
+
+       // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
+       setWritable(in->getAttributeAsBool("Writable"));
+}
diff --git a/src/gui/guiEditBoxWithScrollbar.h b/src/gui/guiEditBoxWithScrollbar.h
new file mode 100644 (file)
index 0000000..cca2f65
--- /dev/null
@@ -0,0 +1,192 @@
+// Copyright (C) 2002-2012 Nikolaus Gebhardt, Modified by Mustapha Tachouct
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#ifndef GUIEDITBOXWITHSCROLLBAR_HEADER
+#define GUIEDITBOXWITHSCROLLBAR_HEADER
+
+#include "IGUIEditBox.h"
+#include "IOSOperator.h"
+#include "IGUIScrollBar.h"
+#include <vector>
+
+using namespace irr;
+using namespace irr::gui;
+
+class GUIEditBoxWithScrollBar : public IGUIEditBox
+{
+public:
+
+       //! constructor
+       GUIEditBoxWithScrollBar(const wchar_t* text, bool border, IGUIEnvironment* environment,
+               IGUIElement* parent, s32 id, const core::rect<s32>& rectangle,
+               bool writable = true, bool has_vscrollbar = true);
+
+       //! destructor
+       virtual ~GUIEditBoxWithScrollBar();
+
+       //! Sets another skin independent font.
+       virtual void setOverrideFont(IGUIFont* font = 0);
+
+       //! Gets the override font (if any)
+       /** \return The override font (may be 0) */
+       virtual IGUIFont* getOverrideFont() const;
+
+       //! Get the font which is used right now for drawing
+       /** Currently this is the override font when one is set and the
+       font of the active skin otherwise */
+       virtual IGUIFont* getActiveFont() const;
+
+       //! Sets another color for the text.
+       virtual void setOverrideColor(video::SColor color);
+
+       //! Gets the override color
+       virtual video::SColor getOverrideColor() const;
+
+       //! Sets if the text should use the overide color or the
+       //! color in the gui skin.
+       virtual void enableOverrideColor(bool enable);
+
+       //! Checks if an override color is enabled
+       /** \return true if the override color is enabled, false otherwise */
+       virtual bool isOverrideColorEnabled(void) const;
+
+       //! Sets whether to draw the background
+       virtual void setDrawBackground(bool draw);
+
+       //! Turns the border on or off
+       virtual void setDrawBorder(bool border);
+
+       //! Enables or disables word wrap for using the edit box as multiline text editor.
+       virtual void setWordWrap(bool enable);
+
+       //! Checks if word wrap is enabled
+       //! \return true if word wrap is enabled, false otherwise
+       virtual bool isWordWrapEnabled() const;
+
+       //! Enables or disables newlines.
+       /** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
+       instead a newline character will be inserted. */
+       virtual void setMultiLine(bool enable);
+
+       //! Checks if multi line editing is enabled
+       //! \return true if mult-line is enabled, false otherwise
+       virtual bool isMultiLineEnabled() const;
+
+       //! Enables or disables automatic scrolling with cursor position
+       //! \param enable: If set to true, the text will move around with the cursor position
+       virtual void setAutoScroll(bool enable);
+
+       //! Checks to see if automatic scrolling is enabled
+       //! \return true if automatic scrolling is enabled, false if not
+       virtual bool isAutoScrollEnabled() const;
+
+       //! Gets the size area of the text in the edit box
+       //! \return Returns the size in pixels of the text
+       virtual core::dimension2du getTextDimension();
+
+       //! Sets text justification
+       virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
+
+       //! called if an event happened.
+       virtual bool OnEvent(const SEvent& event);
+
+       //! draws the element and its children
+       virtual void draw();
+
+       //! Sets the new caption of this element.
+       virtual void setText(const wchar_t* text);
+
+       //! Sets the maximum amount of characters which may be entered in the box.
+       //! \param max: Maximum amount of characters. If 0, the character amount is
+       //! infinity.
+       virtual void setMax(u32 max);
+
+       //! Returns maximum amount of characters, previously set by setMax();
+       virtual u32 getMax() const;
+
+       //! Sets whether the edit box is a password box. Setting this to true will
+       /** disable MultiLine, WordWrap and the ability to copy with ctrl+c or ctrl+x
+       \param passwordBox: true to enable password, false to disable
+       \param passwordChar: the character that is displayed instead of letters */
+       virtual void setPasswordBox(bool passwordBox, wchar_t passwordChar = L'*');
+
+       //! Returns true if the edit box is currently a password box.
+       virtual bool isPasswordBox() const;
+
+       //! Updates the absolute position, splits text if required
+       virtual void updateAbsolutePosition();
+       
+       virtual void setWritable(bool writable);
+
+       //! Change the background color
+       virtual void setBackgroundColor(const video::SColor &bg_color);
+
+       //! Writes attributes of the element.
+       virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const;
+
+       //! Reads attributes of the element
+       virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options);
+
+protected:
+       //! Breaks the single text line.
+       void breakText();
+       //! sets the area of the given line
+       void setTextRect(s32 line);
+       //! returns the line number that the cursor is on
+       s32 getLineFromPos(s32 pos);
+       //! adds a letter to the edit box
+       void inputChar(wchar_t c);
+       //! calculates the current scroll position
+       void calculateScrollPos();
+       //! calculated the FrameRect
+       void calculateFrameRect();
+       //! send some gui event to parent
+       void sendGuiEvent(EGUI_EVENT_TYPE type);
+       //! set text markers
+       void setTextMarkers(s32 begin, s32 end);
+       //! create a Vertical ScrollBar
+       void createVScrollBar();
+       //! update the vertical scrollBar (visibilty & position)
+       void updateVScrollBar();
+
+       bool processKey(const SEvent& event);
+       bool processMouse(const SEvent& event);
+       s32 getCursorPos(s32 x, s32 y);
+
+       bool m_mouse_marking;
+       bool m_border;
+       bool m_background;
+       bool m_override_color_enabled;
+       s32 m_mark_begin;
+       s32 m_mark_end;
+
+       video::SColor m_override_color;
+       gui::IGUIFont *m_override_font, *m_last_break_font;
+       IOSOperator* m_operator;
+
+       u32 m_blink_start_time;
+       s32 m_cursor_pos;
+       s32 m_hscroll_pos, m_vscroll_pos; // scroll position in characters
+       u32 m_max;
+
+       bool m_word_wrap, m_multiline, m_autoscroll, m_passwordbox;
+       wchar_t m_passwordchar;
+       EGUI_ALIGNMENT m_halign, m_valign;
+
+       std::vector<core::stringw> m_broken_text;
+       std::vector<s32> m_broken_text_positions;
+
+       core::rect<s32> m_current_text_rect, m_frame_rect; // temporary values
+
+       u32 m_scrollbar_width;
+       IGUIScrollBar *m_vscrollbar;
+       bool m_writable;
+
+       bool m_bg_color_used;
+       video::SColor m_bg_color;
+};
+
+
+#endif // GUIEDITBOXWITHSCROLLBAR_HEADER
+
diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp
new file mode 100644 (file)
index 0000000..e9b4e54
--- /dev/null
@@ -0,0 +1,587 @@
+/*
+Minetest
+Copyright (C) 2013 sapier
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "guiEngine.h"
+
+#include <IGUIStaticText.h>
+#include <ICameraSceneNode.h>
+#include "client/renderingengine.h"
+#include "scripting_mainmenu.h"
+#include "util/numeric.h"
+#include "config.h"
+#include "version.h"
+#include "porting.h"
+#include "filesys.h"
+#include "settings.h"
+#include "guiMainMenu.h"
+#include "sound.h"
+#include "sound_openal.h"
+#include "clouds.h"
+#include "httpfetch.h"
+#include "log.h"
+#include "fontengine.h"
+#include "guiscalingfilter.h"
+#include "irrlicht_changes/static_text.h"
+
+#ifdef __ANDROID__
+#include "client/tile.h"
+#include <GLES/gl.h>
+#endif
+
+
+/******************************************************************************/
+void TextDestGuiEngine::gotText(const StringMap &fields)
+{
+       m_engine->getScriptIface()->handleMainMenuButtons(fields);
+}
+
+/******************************************************************************/
+void TextDestGuiEngine::gotText(const std::wstring &text)
+{
+       m_engine->getScriptIface()->handleMainMenuEvent(wide_to_utf8(text));
+}
+
+/******************************************************************************/
+MenuTextureSource::~MenuTextureSource()
+{
+       for (const std::string &texture_to_delete : m_to_delete) {
+               const char *tname = texture_to_delete.c_str();
+               video::ITexture *texture = m_driver->getTexture(tname);
+               m_driver->removeTexture(texture);
+       }
+}
+
+/******************************************************************************/
+video::ITexture *MenuTextureSource::getTexture(const std::string &name, u32 *id)
+{
+       if(id)
+               *id = 0;
+       if(name.empty())
+               return NULL;
+       m_to_delete.insert(name);
+
+#ifdef __ANDROID__
+       video::IImage *image = m_driver->createImageFromFile(name.c_str());
+       if (image) {
+               image = Align2Npot2(image, m_driver);
+               video::ITexture* retval = m_driver->addTexture(name.c_str(), image);
+               image->drop();
+               return retval;
+       }
+#endif
+       return m_driver->getTexture(name.c_str());
+}
+
+/******************************************************************************/
+/** MenuMusicFetcher                                                          */
+/******************************************************************************/
+void MenuMusicFetcher::fetchSounds(const std::string &name,
+                       std::set<std::string> &dst_paths,
+                       std::set<std::string> &dst_datas)
+{
+       if(m_fetched.count(name))
+               return;
+       m_fetched.insert(name);
+       std::string base;
+       base = porting::path_share + DIR_DELIM + "sounds";
+       dst_paths.insert(base + DIR_DELIM + name + ".ogg");
+       int i;
+       for(i=0; i<10; i++)
+               dst_paths.insert(base + DIR_DELIM + name + "."+itos(i)+".ogg");
+       base = porting::path_user + DIR_DELIM + "sounds";
+       dst_paths.insert(base + DIR_DELIM + name + ".ogg");
+       for(i=0; i<10; i++)
+               dst_paths.insert(base + DIR_DELIM + name + "."+itos(i)+".ogg");
+}
+
+/******************************************************************************/
+/** GUIEngine                                                                 */
+/******************************************************************************/
+GUIEngine::GUIEngine(JoystickController *joystick,
+               gui::IGUIElement *parent,
+               IMenuManager *menumgr,
+               MainMenuData *data,
+               bool &kill) :
+       m_parent(parent),
+       m_menumanager(menumgr),
+       m_smgr(RenderingEngine::get_scene_manager()),
+       m_data(data),
+       m_kill(kill)
+{
+       //initialize texture pointers
+       for (image_definition &texture : m_textures) {
+               texture.texture = NULL;
+       }
+       // is deleted by guiformspec!
+       m_buttonhandler = new TextDestGuiEngine(this);
+
+       //create texture source
+       m_texture_source = new MenuTextureSource(RenderingEngine::get_video_driver());
+
+       //create soundmanager
+       MenuMusicFetcher soundfetcher;
+#if USE_SOUND
+       m_sound_manager = createOpenALSoundManager(&soundfetcher);
+#endif
+       if(!m_sound_manager)
+               m_sound_manager = &dummySoundManager;
+
+       //create topleft header
+       m_toplefttext = L"";
+
+       core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()),
+               g_fontengine->getTextHeight());
+       rect += v2s32(4, 0);
+
+       m_irr_toplefttext =
+               addStaticText(RenderingEngine::get_gui_env(), m_toplefttext,
+                       rect, false, true, 0, -1);
+
+       //create formspecsource
+       m_formspecgui = new FormspecFormSource("");
+
+       /* Create menu */
+       m_menu = new GUIFormSpecMenu(joystick,
+                       m_parent,
+                       -1,
+                       m_menumanager,
+                       NULL /* &client */,
+                       m_texture_source,
+                       m_formspecgui,
+                       m_buttonhandler,
+                       false);
+
+       m_menu->allowClose(false);
+       m_menu->lockSize(true,v2u32(800,600));
+
+       // Initialize scripting
+
+       infostream << "GUIEngine: Initializing Lua" << std::endl;
+
+       m_script = new MainMenuScripting(this);
+
+       try {
+               m_script->setMainMenuData(&m_data->script_data);
+               m_data->script_data.errormessage = "";
+
+               if (!loadMainMenuScript()) {
+                       errorstream << "No future without main menu!" << std::endl;
+                       abort();
+               }
+
+               run();
+       } catch (LuaError &e) {
+               errorstream << "Main menu error: " << e.what() << std::endl;
+               m_data->script_data.errormessage = e.what();
+       }
+
+       m_menu->quitMenu();
+       m_menu->drop();
+       m_menu = NULL;
+}
+
+/******************************************************************************/
+bool GUIEngine::loadMainMenuScript()
+{
+       // Set main menu path (for core.get_mainmenu_path())
+       m_scriptdir = g_settings->get("main_menu_path");
+       if (m_scriptdir.empty()) {
+               m_scriptdir = porting::path_share + DIR_DELIM + "builtin" + DIR_DELIM + "mainmenu";
+       }
+
+       // Load builtin (which will load the main menu script)
+       std::string script = porting::path_share + DIR_DELIM "builtin" + DIR_DELIM "init.lua";
+       try {
+               m_script->loadScript(script);
+               // Menu script loaded
+               return true;
+       } catch (const ModError &e) {
+               errorstream << "GUIEngine: execution of menu script failed: "
+                       << e.what() << std::endl;
+       }
+
+       return false;
+}
+
+/******************************************************************************/
+void GUIEngine::run()
+{
+       // Always create clouds because they may or may not be
+       // needed based on the game selected
+       video::IVideoDriver *driver = RenderingEngine::get_video_driver();
+
+       cloudInit();
+
+       unsigned int text_height = g_fontengine->getTextHeight();
+
+       irr::core::dimension2d<u32> previous_screen_size(g_settings->getU16("screen_w"),
+               g_settings->getU16("screen_h"));
+
+       while (RenderingEngine::run() && (!m_startgame) && (!m_kill)) {
+
+               const irr::core::dimension2d<u32> &current_screen_size =
+                       RenderingEngine::get_video_driver()->getScreenSize();
+               // Verify if window size has changed and save it if it's the case
+               // Ensure evaluating settings->getBool after verifying screensize
+               // First condition is cheaper
+               if (previous_screen_size != current_screen_size &&
+                               current_screen_size != irr::core::dimension2d<u32>(0,0) &&
+                               g_settings->getBool("autosave_screensize")) {
+                       g_settings->setU16("screen_w", current_screen_size.Width);
+                       g_settings->setU16("screen_h", current_screen_size.Height);
+                       previous_screen_size = current_screen_size;
+               }
+
+               //check if we need to update the "upper left corner"-text
+               if (text_height != g_fontengine->getTextHeight()) {
+                       updateTopLeftTextSize();
+                       text_height = g_fontengine->getTextHeight();
+               }
+
+               driver->beginScene(true, true, video::SColor(255,140,186,250));
+
+               if (m_clouds_enabled)
+               {
+                       cloudPreProcess();
+                       drawOverlay(driver);
+               }
+               else
+                       drawBackground(driver);
+
+               drawHeader(driver);
+               drawFooter(driver);
+
+               RenderingEngine::get_gui_env()->drawAll();
+
+               driver->endScene();
+
+               if (m_clouds_enabled)
+                       cloudPostProcess();
+               else
+                       sleep_ms(25);
+
+               m_script->step();
+
+#ifdef __ANDROID__
+               m_menu->getAndroidUIInput();
+#endif
+       }
+}
+
+/******************************************************************************/
+GUIEngine::~GUIEngine()
+{
+       if (m_sound_manager != &dummySoundManager){
+               delete m_sound_manager;
+               m_sound_manager = NULL;
+       }
+
+       infostream<<"GUIEngine: Deinitializing scripting"<<std::endl;
+       delete m_script;
+
+       m_irr_toplefttext->setText(L"");
+
+       //clean up texture pointers
+       for (image_definition &texture : m_textures) {
+               if (texture.texture)
+                       RenderingEngine::get_video_driver()->removeTexture(texture.texture);
+       }
+
+       delete m_texture_source;
+
+       if (m_cloud.clouds)
+               m_cloud.clouds->drop();
+}
+
+/******************************************************************************/
+void GUIEngine::cloudInit()
+{
+       m_cloud.clouds = new Clouds(m_smgr, -1, rand());
+       m_cloud.clouds->setHeight(100.0f);
+       m_cloud.clouds->update(v3f(0, 0, 0), video::SColor(255,200,200,255));
+
+       m_cloud.camera = m_smgr->addCameraSceneNode(0,
+                               v3f(0,0,0), v3f(0, 60, 100));
+       m_cloud.camera->setFarValue(10000);
+
+       m_cloud.lasttime = RenderingEngine::get_timer_time();
+}
+
+/******************************************************************************/
+void GUIEngine::cloudPreProcess()
+{
+       u32 time = RenderingEngine::get_timer_time();
+
+       if(time > m_cloud.lasttime)
+               m_cloud.dtime = (time - m_cloud.lasttime) / 1000.0;
+       else
+               m_cloud.dtime = 0;
+
+       m_cloud.lasttime = time;
+
+       m_cloud.clouds->step(m_cloud.dtime*3);
+       m_cloud.clouds->render();
+       m_smgr->drawAll();
+}
+
+/******************************************************************************/
+void GUIEngine::cloudPostProcess()
+{
+       float fps_max = g_settings->getFloat("pause_fps_max");
+       // Time of frame without fps limit
+       u32 busytime_u32;
+
+       // not using getRealTime is necessary for wine
+       u32 time = RenderingEngine::get_timer_time();
+       if(time > m_cloud.lasttime)
+               busytime_u32 = time - m_cloud.lasttime;
+       else
+               busytime_u32 = 0;
+
+       // FPS limiter
+       u32 frametime_min = 1000./fps_max;
+
+       if (busytime_u32 < frametime_min) {
+               u32 sleeptime = frametime_min - busytime_u32;
+               RenderingEngine::get_raw_device()->sleep(sleeptime);
+       }
+}
+
+/******************************************************************************/
+void GUIEngine::drawBackground(video::IVideoDriver *driver)
+{
+       v2u32 screensize = driver->getScreenSize();
+
+       video::ITexture* texture = m_textures[TEX_LAYER_BACKGROUND].texture;
+
+       /* If no texture, draw background of solid color */
+       if(!texture){
+               video::SColor color(255,80,58,37);
+               core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
+               driver->draw2DRectangle(color, rect, NULL);
+               return;
+       }
+
+       v2u32 sourcesize = texture->getOriginalSize();
+
+       if (m_textures[TEX_LAYER_BACKGROUND].tile)
+       {
+               v2u32 tilesize(
+                               MYMAX(sourcesize.X,m_textures[TEX_LAYER_BACKGROUND].minsize),
+                               MYMAX(sourcesize.Y,m_textures[TEX_LAYER_BACKGROUND].minsize));
+               for (unsigned int x = 0; x < screensize.X; x += tilesize.X )
+               {
+                       for (unsigned int y = 0; y < screensize.Y; y += tilesize.Y )
+                       {
+                               draw2DImageFilterScaled(driver, texture,
+                                       core::rect<s32>(x, y, x+tilesize.X, y+tilesize.Y),
+                                       core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
+                                       NULL, NULL, true);
+                       }
+               }
+               return;
+       }
+
+       /* Draw background texture */
+       draw2DImageFilterScaled(driver, texture,
+               core::rect<s32>(0, 0, screensize.X, screensize.Y),
+               core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
+               NULL, NULL, true);
+}
+
+/******************************************************************************/
+void GUIEngine::drawOverlay(video::IVideoDriver *driver)
+{
+       v2u32 screensize = driver->getScreenSize();
+
+       video::ITexture* texture = m_textures[TEX_LAYER_OVERLAY].texture;
+
+       /* If no texture, draw nothing */
+       if(!texture)
+               return;
+
+       /* Draw background texture */
+       v2u32 sourcesize = texture->getOriginalSize();
+       draw2DImageFilterScaled(driver, texture,
+               core::rect<s32>(0, 0, screensize.X, screensize.Y),
+               core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
+               NULL, NULL, true);
+}
+
+/******************************************************************************/
+void GUIEngine::drawHeader(video::IVideoDriver *driver)
+{
+       core::dimension2d<u32> screensize = driver->getScreenSize();
+
+       video::ITexture* texture = m_textures[TEX_LAYER_HEADER].texture;
+
+       /* If no texture, draw nothing */
+       if(!texture)
+               return;
+
+       f32 mult = (((f32)screensize.Width / 2.0)) /
+                       ((f32)texture->getOriginalSize().Width);
+
+       v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
+                       ((f32)texture->getOriginalSize().Height) * mult);
+
+       // Don't draw the header if there isn't enough room
+       s32 free_space = (((s32)screensize.Height)-320)/2;
+
+       if (free_space > splashsize.Y) {
+               core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
+               splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
+                               ((free_space/2)-splashsize.Y/2)+10);
+
+       video::SColor bgcolor(255,50,50,50);
+
+       draw2DImageFilterScaled(driver, texture, splashrect,
+               core::rect<s32>(core::position2d<s32>(0,0),
+               core::dimension2di(texture->getOriginalSize())),
+               NULL, NULL, true);
+       }
+}
+
+/******************************************************************************/
+void GUIEngine::drawFooter(video::IVideoDriver *driver)
+{
+       core::dimension2d<u32> screensize = driver->getScreenSize();
+
+       video::ITexture* texture = m_textures[TEX_LAYER_FOOTER].texture;
+
+       /* If no texture, draw nothing */
+       if(!texture)
+               return;
+
+       f32 mult = (((f32)screensize.Width)) /
+                       ((f32)texture->getOriginalSize().Width);
+
+       v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
+                       ((f32)texture->getOriginalSize().Height) * mult);
+
+       // Don't draw the footer if there isn't enough room
+       s32 free_space = (((s32)screensize.Height)-320)/2;
+
+       if (free_space > footersize.Y) {
+               core::rect<s32> rect(0,0,footersize.X,footersize.Y);
+               rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
+               rect -= v2s32(footersize.X/2, 0);
+
+               draw2DImageFilterScaled(driver, texture, rect,
+                       core::rect<s32>(core::position2d<s32>(0,0),
+                       core::dimension2di(texture->getOriginalSize())),
+                       NULL, NULL, true);
+       }
+}
+
+/******************************************************************************/
+bool GUIEngine::setTexture(texture_layer layer, std::string texturepath,
+               bool tile_image, unsigned int minsize)
+{
+       video::IVideoDriver *driver = RenderingEngine::get_video_driver();
+
+       if (m_textures[layer].texture) {
+               driver->removeTexture(m_textures[layer].texture);
+               m_textures[layer].texture = NULL;
+       }
+
+       if (texturepath.empty() || !fs::PathExists(texturepath)) {
+               return false;
+       }
+
+       m_textures[layer].texture = driver->getTexture(texturepath.c_str());
+       m_textures[layer].tile    = tile_image;
+       m_textures[layer].minsize = minsize;
+
+       if (!m_textures[layer].texture) {
+               return false;
+       }
+
+       return true;
+}
+
+/******************************************************************************/
+bool GUIEngine::downloadFile(const std::string &url, const std::string &target)
+{
+#if USE_CURL
+       std::ofstream target_file(target.c_str(), std::ios::out | std::ios::binary);
+
+       if (!target_file.good()) {
+               return false;
+       }
+
+       HTTPFetchRequest fetch_request;
+       HTTPFetchResult fetch_result;
+       fetch_request.url = url;
+       fetch_request.caller = HTTPFETCH_SYNC;
+       fetch_request.timeout = g_settings->getS32("curl_file_download_timeout");
+       httpfetch_sync(fetch_request, fetch_result);
+
+       if (!fetch_result.succeeded) {
+               return false;
+       }
+       target_file << fetch_result.data;
+
+       return true;
+#else
+       return false;
+#endif
+}
+
+/******************************************************************************/
+void GUIEngine::setTopleftText(const std::string &text)
+{
+       m_toplefttext = translate_string(utf8_to_wide(text));
+
+       updateTopLeftTextSize();
+}
+
+/******************************************************************************/
+void GUIEngine::updateTopLeftTextSize()
+{
+       core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()),
+               g_fontengine->getTextHeight());
+       rect += v2s32(4, 0);
+
+       m_irr_toplefttext->remove();
+       m_irr_toplefttext =
+               addStaticText(RenderingEngine::get_gui_env(), m_toplefttext,
+                       rect, false, true, 0, -1);
+}
+
+/******************************************************************************/
+s32 GUIEngine::playSound(SimpleSoundSpec spec, bool looped)
+{
+       s32 handle = m_sound_manager->playSound(spec, looped);
+       return handle;
+}
+
+/******************************************************************************/
+void GUIEngine::stopSound(s32 handle)
+{
+       m_sound_manager->stopSound(handle);
+}
+
+/******************************************************************************/
+unsigned int GUIEngine::queueAsync(const std::string &serialized_func,
+               const std::string &serialized_params)
+{
+       return m_script->queueAsync(serialized_func, serialized_params);
+}
+
diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h
new file mode 100644 (file)
index 0000000..817d760
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+Minetest
+Copyright (C) 2013 sapier
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+/******************************************************************************/
+/* Includes                                                                   */
+/******************************************************************************/
+#include "irrlichttypes.h"
+#include "modalMenu.h"
+#include "guiFormSpecMenu.h"
+#include "sound.h"
+#include "client/tile.h"
+#include "util/enriched_string.h"
+
+/******************************************************************************/
+/* Typedefs and macros                                                        */
+/******************************************************************************/
+/** texture layer ids */
+typedef enum {
+       TEX_LAYER_BACKGROUND = 0,
+       TEX_LAYER_OVERLAY,
+       TEX_LAYER_HEADER,
+       TEX_LAYER_FOOTER,
+       TEX_LAYER_MAX
+} texture_layer;
+
+typedef struct {
+       video::ITexture *texture = nullptr;
+       bool             tile;
+       unsigned int     minsize;
+} image_definition;
+
+/******************************************************************************/
+/* forward declarations                                                       */
+/******************************************************************************/
+class GUIEngine;
+class MainMenuScripting;
+class Clouds;
+struct MainMenuData;
+
+/******************************************************************************/
+/* declarations                                                               */
+/******************************************************************************/
+
+/** GUIEngine specific implementation of TextDest used within guiFormSpecMenu */
+class TextDestGuiEngine : public TextDest
+{
+public:
+       /**
+        * default constructor
+        * @param engine the engine data is transmitted for further processing
+        */
+       TextDestGuiEngine(GUIEngine* engine) : m_engine(engine) {};
+
+       /**
+        * receive fields transmitted by guiFormSpecMenu
+        * @param fields map containing formspec field elements currently active
+        */
+       void gotText(const StringMap &fields);
+
+       /**
+        * receive text/events transmitted by guiFormSpecMenu
+        * @param text textual representation of event
+        */
+       void gotText(const std::wstring &text);
+
+private:
+       /** target to transmit data to */
+       GUIEngine *m_engine = nullptr;
+};
+
+/** GUIEngine specific implementation of ISimpleTextureSource */
+class MenuTextureSource : public ISimpleTextureSource
+{
+public:
+       /**
+        * default constructor
+        * @param driver the video driver to load textures from
+        */
+       MenuTextureSource(video::IVideoDriver *driver) : m_driver(driver) {};
+
+       /**
+        * destructor, removes all loaded textures
+        */
+       virtual ~MenuTextureSource();
+
+       /**
+        * get a texture, loading it if required
+        * @param name path to the texture
+        * @param id receives the texture ID, always 0 in this implementation
+        */
+       video::ITexture *getTexture(const std::string &name, u32 *id = NULL);
+
+private:
+       /** driver to get textures from */
+       video::IVideoDriver *m_driver = nullptr;
+       /** set of texture names to delete */
+       std::set<std::string> m_to_delete;
+};
+
+/** GUIEngine specific implementation of OnDemandSoundFetcher */
+class MenuMusicFetcher: public OnDemandSoundFetcher
+{
+public:
+       /**
+        * get sound file paths according to sound name
+        * @param name sound name
+        * @param dst_paths receives possible paths to sound files
+        * @param dst_datas receives binary sound data (not used here)
+        */
+       void fetchSounds(const std::string &name,
+                       std::set<std::string> &dst_paths,
+                       std::set<std::string> &dst_datas);
+
+private:
+       /** set of fetched sound names */
+       std::set<std::string> m_fetched;
+};
+
+/** implementation of main menu based uppon formspecs */
+class GUIEngine {
+       /** grant ModApiMainMenu access to private members */
+       friend class ModApiMainMenu;
+       friend class ModApiSound;
+
+public:
+       /**
+        * default constructor
+        * @param dev device to draw at
+        * @param parent parent gui element
+        * @param menumgr manager to add menus to
+        * @param smgr scene manager to add scene elements to
+        * @param data struct to transfer data to main game handling
+        */
+       GUIEngine(JoystickController *joystick,
+                       gui::IGUIElement *parent,
+                       IMenuManager *menumgr,
+                       MainMenuData *data,
+                       bool &kill);
+
+       /** default destructor */
+       virtual ~GUIEngine();
+
+       /**
+        * return MainMenuScripting interface
+        */
+       MainMenuScripting *getScriptIface()
+       {
+               return m_script;
+       }
+
+       /**
+        * return dir of current menuscript
+        */
+       std::string getScriptDir()
+       {
+               return m_scriptdir;
+       }
+
+       /** pass async callback to scriptengine **/
+       unsigned int queueAsync(const std::string &serialized_fct,
+                       const std::string &serialized_params);
+
+private:
+
+       /** find and run the main menu script */
+       bool loadMainMenuScript();
+
+       /** run main menu loop */
+       void run();
+
+       /** update size of topleftext element */
+       void updateTopLeftTextSize();
+
+       /** parent gui element */
+       gui::IGUIElement        *m_parent = nullptr;
+       /** manager to add menus to */
+       IMenuManager            *m_menumanager = nullptr;
+       /** scene manager to add scene elements to */
+       scene::ISceneManager    *m_smgr = nullptr;
+       /** pointer to data beeing transfered back to main game handling */
+       MainMenuData            *m_data = nullptr;
+       /** pointer to texture source */
+       ISimpleTextureSource    *m_texture_source = nullptr;
+       /** pointer to soundmanager*/
+       ISoundManager           *m_sound_manager = nullptr;
+
+       /** representation of form source to be used in mainmenu formspec */
+       FormspecFormSource      *m_formspecgui = nullptr;
+       /** formspec input receiver */
+       TextDestGuiEngine       *m_buttonhandler = nullptr;
+       /** the formspec menu */
+       GUIFormSpecMenu         *m_menu = nullptr;
+
+       /** reference to kill variable managed by SIGINT handler */
+       bool                    &m_kill;
+
+       /** variable used to abort menu and return back to main game handling */
+       bool                     m_startgame = false;
+
+       /** scripting interface */
+       MainMenuScripting       *m_script = nullptr;
+
+       /** script basefolder */
+       std::string              m_scriptdir = "";
+
+       /**
+        * draw background layer
+        * @param driver to use for drawing
+        */
+       void drawBackground(video::IVideoDriver *driver);
+       /**
+        * draw overlay layer
+        * @param driver to use for drawing
+        */
+       void drawOverlay(video::IVideoDriver *driver);
+       /**
+        * draw header layer
+        * @param driver to use for drawing
+        */
+       void drawHeader(video::IVideoDriver *driver);
+       /**
+        * draw footer layer
+        * @param driver to use for drawing
+        */
+       void drawFooter(video::IVideoDriver *driver);
+
+       /**
+        * load a texture for a specified layer
+        * @param layer draw layer to specify texture
+        * @param texturepath full path of texture to load
+        */
+       bool setTexture(texture_layer layer, std::string texturepath,
+                       bool tile_image, unsigned int minsize);
+
+       /**
+        * download a file using curl
+        * @param url url to download
+        * @param target file to store to
+        */
+       static bool downloadFile(const std::string &url, const std::string &target);
+
+       /** array containing pointers to current specified texture layers */
+       image_definition m_textures[TEX_LAYER_MAX];
+
+       /**
+        * specify text to appear as top left string
+        * @param text to set
+        */
+       void setTopleftText(const std::string &text);
+
+       /** pointer to gui element shown at topleft corner */
+       irr::gui::IGUIStaticText *m_irr_toplefttext = nullptr;
+       /** and text that is in it */
+       EnrichedString m_toplefttext;
+
+       /** initialize cloud subsystem */
+       void cloudInit();
+       /** do preprocessing for cloud subsystem */
+       void cloudPreProcess();
+       /** do postprocessing for cloud subsystem */
+       void cloudPostProcess();
+
+       /** internam data required for drawing clouds */
+       struct clouddata {
+               /** delta time since last cloud processing */
+               f32     dtime;
+               /** absolute time of last cloud processing */
+               u32     lasttime;
+               /** pointer to cloud class */
+               Clouds *clouds = nullptr;
+               /** camera required for drawing clouds */
+               scene::ICameraSceneNode *camera = nullptr;
+       };
+
+       /** is drawing of clouds enabled atm */
+       bool        m_clouds_enabled = true;
+       /** data used to draw clouds */
+       clouddata   m_cloud;
+
+       /** start playing a sound and return handle */
+       s32 playSound(SimpleSoundSpec spec, bool looped);
+       /** stop playing a sound started with playSound() */
+       void stopSound(s32 handle);
+
+
+};
diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp
new file mode 100644 (file)
index 0000000..0691bc5
--- /dev/null
@@ -0,0 +1,3864 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 <cstdlib>
+#include <algorithm>
+#include <iterator>
+#include <sstream>
+#include <limits>
+#include "guiFormSpecMenu.h"
+#include "guiTable.h"
+#include "constants.h"
+#include "gamedef.h"
+#include "keycode.h"
+#include "util/strfnd.h"
+#include <IGUICheckBox.h>
+#include <IGUIEditBox.h>
+#include <IGUIButton.h>
+#include <IGUIStaticText.h>
+#include <IGUIFont.h>
+#include <IGUITabControl.h>
+#include <IGUIComboBox.h>
+#include "client/renderingengine.h"
+#include "log.h"
+#include "client/tile.h" // ITextureSource
+#include "hud.h" // drawItemStack
+#include "filesys.h"
+#include "gettime.h"
+#include "gettext.h"
+#include "scripting_server.h"
+#include "porting.h"
+#include "settings.h"
+#include "client.h"
+#include "fontengine.h"
+#include "util/hex.h"
+#include "util/numeric.h"
+#include "util/string.h" // for parseColorString()
+#include "irrlicht_changes/static_text.h"
+#include "guiscalingfilter.h"
+#include "guiEditBoxWithScrollbar.h"
+
+#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
+#include "intlGUIEditBox.h"
+#endif
+
+#define MY_CHECKPOS(a,b)                                                                                                       \
+       if (v_pos.size() != 2) {                                                                                                \
+               errorstream<< "Invalid pos for element " << a << "specified: \""        \
+                       << parts[b] << "\"" << std::endl;                                                               \
+                       return;                                                                                                                 \
+       }
+
+#define MY_CHECKGEOM(a,b)                                                                                                      \
+       if (v_geom.size() != 2) {                                                                                               \
+               errorstream<< "Invalid pos for element " << a << "specified: \""        \
+                       << parts[b] << "\"" << std::endl;                                                               \
+                       return;                                                                                                                 \
+       }
+/*
+       GUIFormSpecMenu
+*/
+static unsigned int font_line_height(gui::IGUIFont *font)
+{
+       return font->getDimension(L"Ay").Height + font->getKerningHeight();
+}
+
+inline u32 clamp_u8(s32 value)
+{
+       return (u32) MYMIN(MYMAX(value, 0), 255);
+}
+
+GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
+               gui::IGUIElement *parent, s32 id, IMenuManager *menumgr,
+               Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst,
+               bool remap_dbl_click) :
+       GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr),
+       m_invmgr(client),
+       m_tsrc(tsrc),
+       m_client(client),
+       m_form_src(fsrc),
+       m_text_dst(tdst),
+       m_joystick(joystick),
+       m_remap_dbl_click(remap_dbl_click)
+#ifdef __ANDROID__
+       , m_JavaDialogFieldName("")
+#endif
+{
+       current_keys_pending.key_down = false;
+       current_keys_pending.key_up = false;
+       current_keys_pending.key_enter = false;
+       current_keys_pending.key_escape = false;
+
+       m_doubleclickdetect[0].time = 0;
+       m_doubleclickdetect[1].time = 0;
+
+       m_doubleclickdetect[0].pos = v2s32(0, 0);
+       m_doubleclickdetect[1].pos = v2s32(0, 0);
+
+       m_tooltip_show_delay = (u32)g_settings->getS32("tooltip_show_delay");
+       m_tooltip_append_itemname = g_settings->getBool("tooltip_append_itemname");
+}
+
+GUIFormSpecMenu::~GUIFormSpecMenu()
+{
+       removeChildren();
+
+       for (auto &table_it : m_tables) {
+               table_it.second->drop();
+       }
+
+       delete m_selected_item;
+       delete m_form_src;
+       delete m_text_dst;
+}
+
+void GUIFormSpecMenu::removeChildren()
+{
+       const core::list<gui::IGUIElement*> &children = getChildren();
+
+       while(!children.empty()) {
+               (*children.getLast())->remove();
+       }
+
+       if(m_tooltip_element) {
+               m_tooltip_element->remove();
+               m_tooltip_element->drop();
+               m_tooltip_element = NULL;
+       }
+
+}
+
+void GUIFormSpecMenu::setInitialFocus()
+{
+       // Set initial focus according to following order of precedence:
+       // 1. first empty editbox
+       // 2. first editbox
+       // 3. first table
+       // 4. last button
+       // 5. first focusable (not statictext, not tabheader)
+       // 6. first child element
+
+       core::list<gui::IGUIElement*> children = getChildren();
+
+       // in case "children" contains any NULL elements, remove them
+       for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
+                       it != children.end();) {
+               if (*it)
+                       ++it;
+               else
+                       it = children.erase(it);
+       }
+
+       // 1. first empty editbox
+       for (gui::IGUIElement *it : children) {
+               if (it->getType() == gui::EGUIET_EDIT_BOX
+                               && it->getText()[0] == 0) {
+                       Environment->setFocus(it);
+                       return;
+               }
+       }
+
+       // 2. first editbox
+       for (gui::IGUIElement *it : children) {
+               if (it->getType() == gui::EGUIET_EDIT_BOX) {
+                       Environment->setFocus(it);
+                       return;
+               }
+       }
+
+       // 3. first table
+       for (gui::IGUIElement *it : children) {
+               if (it->getTypeName() == std::string("GUITable")) {
+                       Environment->setFocus(it);
+                       return;
+               }
+       }
+
+       // 4. last button
+       for (core::list<gui::IGUIElement*>::Iterator it = children.getLast();
+                       it != children.end(); --it) {
+               if ((*it)->getType() == gui::EGUIET_BUTTON) {
+                       Environment->setFocus(*it);
+                       return;
+               }
+       }
+
+       // 5. first focusable (not statictext, not tabheader)
+       for (gui::IGUIElement *it : children) {
+               if (it->getType() != gui::EGUIET_STATIC_TEXT &&
+                       it->getType() != gui::EGUIET_TAB_CONTROL) {
+                       Environment->setFocus(it);
+                       return;
+               }
+       }
+
+       // 6. first child element
+       if (children.empty())
+               Environment->setFocus(this);
+       else
+               Environment->setFocus(*(children.begin()));
+}
+
+GUITable* GUIFormSpecMenu::getTable(const std::string &tablename)
+{
+       for (auto &table : m_tables) {
+               if (tablename == table.first.fname)
+                       return table.second;
+       }
+       return 0;
+}
+
+std::vector<std::string>* GUIFormSpecMenu::getDropDownValues(const std::string &name)
+{
+       for (auto &dropdown : m_dropdowns) {
+               if (name == dropdown.first.fname)
+                       return &dropdown.second;
+       }
+       return NULL;
+}
+
+void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,',');
+
+       if (((parts.size() == 2) || parts.size() == 3) ||
+               ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               if (parts[1].find(';') != std::string::npos)
+                       parts[1] = parts[1].substr(0,parts[1].find(';'));
+
+               data->invsize.X = MYMAX(0, stof(parts[0]));
+               data->invsize.Y = MYMAX(0, stof(parts[1]));
+
+               lockSize(false);
+               if (parts.size() == 3) {
+                       if (parts[2] == "true") {
+                               lockSize(true,v2u32(800,600));
+                       }
+               }
+
+               data->explicit_size = true;
+               return;
+       }
+       errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseContainer(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element, ',');
+
+       if (parts.size() >= 2) {
+               if (parts[1].find(';') != std::string::npos)
+                       parts[1] = parts[1].substr(0, parts[1].find(';'));
+
+               container_stack.push(pos_offset);
+               pos_offset.X += MYMAX(0, stof(parts[0]));
+               pos_offset.Y += MYMAX(0, stof(parts[1]));
+               return;
+       }
+       errorstream<< "Invalid container start element (" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseContainerEnd(parserData* data)
+{
+       if (container_stack.empty()) {
+               errorstream<< "Invalid container end element, no matching container start element"  << std::endl;
+       } else {
+               pos_offset = container_stack.top();
+               container_stack.pop();
+       }
+}
+
+void GUIFormSpecMenu::parseList(parserData* data, const std::string &element)
+{
+       if (m_client == 0) {
+               warningstream<<"invalid use of 'list' with m_client==0"<<std::endl;
+               return;
+       }
+
+       std::vector<std::string> parts = split(element,';');
+
+       if (((parts.size() == 4) || (parts.size() == 5)) ||
+               ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::string location = parts[0];
+               std::string listname = parts[1];
+               std::vector<std::string> v_pos  = split(parts[2],',');
+               std::vector<std::string> v_geom = split(parts[3],',');
+               std::string startindex;
+               if (parts.size() == 5)
+                       startindex = parts[4];
+
+               MY_CHECKPOS("list",2);
+               MY_CHECKGEOM("list",3);
+
+               InventoryLocation loc;
+
+               if(location == "context" || location == "current_name")
+                       loc = m_current_inventory_location;
+               else
+                       loc.deSerialize(location);
+
+               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               v2s32 geom;
+               geom.X = stoi(v_geom[0]);
+               geom.Y = stoi(v_geom[1]);
+
+               s32 start_i = 0;
+               if (!startindex.empty())
+                       start_i = stoi(startindex);
+
+               if (geom.X < 0 || geom.Y < 0 || start_i < 0) {
+                       errorstream<< "Invalid list element: '" << element << "'"  << std::endl;
+                       return;
+               }
+
+               if(!data->explicit_size)
+                       warningstream<<"invalid use of list without a size[] element"<<std::endl;
+               m_inventorylists.emplace_back(loc, listname, pos, geom, start_i);
+               return;
+       }
+       errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseListRing(parserData* data, const std::string &element)
+{
+       if (m_client == 0) {
+               errorstream << "WARNING: invalid use of 'listring' with m_client==0" << std::endl;
+               return;
+       }
+
+       std::vector<std::string> parts = split(element, ';');
+
+       if (parts.size() == 2) {
+               std::string location = parts[0];
+               std::string listname = parts[1];
+
+               InventoryLocation loc;
+
+               if (location == "context" || location == "current_name")
+                       loc = m_current_inventory_location;
+               else
+                       loc.deSerialize(location);
+
+               m_inventory_rings.emplace_back(loc, listname);
+               return;
+       }
+
+       if (element.empty() && m_inventorylists.size() > 1) {
+               size_t siz = m_inventorylists.size();
+               // insert the last two inv list elements into the list ring
+               const ListDrawSpec &spa = m_inventorylists[siz - 2];
+               const ListDrawSpec &spb = m_inventorylists[siz - 1];
+               m_inventory_rings.emplace_back(spa.inventoryloc, spa.listname);
+               m_inventory_rings.emplace_back(spb.inventoryloc, spb.listname);
+               return;
+       }
+
+       errorstream<< "Invalid list ring element(" << parts.size() << ", "
+               << m_inventorylists.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if (((parts.size() >= 3) && (parts.size() <= 4)) ||
+               ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::string name = parts[1];
+               std::string label = parts[2];
+               std::string selected;
+
+               if (parts.size() >= 4)
+                       selected = parts[3];
+
+               MY_CHECKPOS("checkbox",0);
+
+               v2s32 pos = padding + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float) spacing.X;
+               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+               bool fselected = false;
+
+               if (selected == "true")
+                       fselected = true;
+
+               std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
+
+               core::rect<s32> rect = core::rect<s32>(
+                               pos.X, pos.Y + ((imgsize.Y/2) - m_btn_height),
+                               pos.X + m_font->getDimension(wlabel.c_str()).Width + 25, // text size + size of checkbox
+                               pos.Y + ((imgsize.Y/2) + m_btn_height));
+
+               FieldSpec spec(
+                               name,
+                               wlabel, //Needed for displaying text on MSVC
+                               wlabel,
+                               258+m_fields.size()
+                       );
+
+               spec.ftype = f_CheckBox;
+
+               gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this,
+                                       spec.fid, spec.flabel.c_str());
+
+               if (spec.fname == data->focused_fieldname) {
+                       Environment->setFocus(e);
+               }
+
+               m_checkboxes.emplace_back(spec,e);
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() >= 5) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_dim = split(parts[1],',');
+               std::string name = parts[3];
+               std::string value = parts[4];
+
+               MY_CHECKPOS("scrollbar",0);
+
+               v2s32 pos = padding + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float) spacing.X;
+               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+               if (v_dim.size() != 2) {
+                       errorstream<< "Invalid size for element " << "scrollbar"
+                               << "specified: \"" << parts[1] << "\"" << std::endl;
+                       return;
+               }
+
+               v2s32 dim;
+               dim.X = stof(v_dim[0]) * (float) spacing.X;
+               dim.Y = stof(v_dim[1]) * (float) spacing.Y;
+
+               core::rect<s32> rect =
+                               core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y);
+
+               FieldSpec spec(
+                               name,
+                               L"",
+                               L"",
+                               258+m_fields.size()
+                       );
+
+               bool is_horizontal = true;
+
+               if (parts[2] == "vertical")
+                       is_horizontal = false;
+
+               spec.ftype = f_ScrollBar;
+               spec.send  = true;
+               gui::IGUIScrollBar* e =
+                               Environment->addScrollBar(is_horizontal,rect,this,spec.fid);
+
+               e->setMax(1000);
+               e->setMin(0);
+               e->setPos(stoi(parts[4]));
+               e->setSmallStep(10);
+               e->setLargeStep(100);
+
+               m_scrollbars.emplace_back(spec,e);
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream<< "Invalid scrollbar element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 3) ||
+               ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = unescape_string(parts[2]);
+
+               MY_CHECKPOS("image", 0);
+               MY_CHECKGEOM("image", 1);
+
+               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float) spacing.X;
+               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+               v2s32 geom;
+               geom.X = stof(v_geom[0]) * (float)imgsize.X;
+               geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
+
+               if (!data->explicit_size)
+                       warningstream<<"invalid use of image without a size[] element"<<std::endl;
+               m_images.emplace_back(name, pos, geom);
+               return;
+       }
+
+       if (parts.size() == 2) {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::string name = unescape_string(parts[1]);
+
+               MY_CHECKPOS("image", 0);
+
+               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float) spacing.X;
+               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+               if (!data->explicit_size)
+                       warningstream<<"invalid use of image without a size[] element"<<std::endl;
+               m_images.emplace_back(name, pos);
+               return;
+       }
+       errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 3) ||
+               ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = parts[2];
+
+               MY_CHECKPOS("itemimage",0);
+               MY_CHECKGEOM("itemimage",1);
+
+               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float) spacing.X;
+               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+               v2s32 geom;
+               geom.X = stof(v_geom[0]) * (float)imgsize.X;
+               geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
+
+               if(!data->explicit_size)
+                       warningstream<<"invalid use of item_image without a size[] element"<<std::endl;
+               m_itemimages.emplace_back("", name, pos, geom);
+               return;
+       }
+       errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
+               const std::string &type)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 4) ||
+               ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = parts[2];
+               std::string label = parts[3];
+
+               MY_CHECKPOS("button",0);
+               MY_CHECKGEOM("button",1);
+
+               v2s32 pos = padding + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               v2s32 geom;
+               geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+               pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+
+               core::rect<s32> rect =
+                               core::rect<s32>(pos.X, pos.Y - m_btn_height,
+                                               pos.X + geom.X, pos.Y + m_btn_height);
+
+               if(!data->explicit_size)
+                       warningstream<<"invalid use of button without a size[] element"<<std::endl;
+
+               std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
+
+               FieldSpec spec(
+                       name,
+                       wlabel,
+                       L"",
+                       258+m_fields.size()
+               );
+               spec.ftype = f_Button;
+               if(type == "button_exit")
+                       spec.is_exit = true;
+               gui::IGUIButton* e = Environment->addButton(rect, this, spec.fid,
+                               spec.flabel.c_str());
+
+               if (spec.fname == data->focused_fieldname) {
+                       Environment->setFocus(e);
+               }
+
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if (((parts.size() == 3) || (parts.size() == 4)) ||
+               ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = unescape_string(parts[2]);
+
+               MY_CHECKPOS("background",0);
+               MY_CHECKGEOM("background",1);
+
+               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X - (float)imgsize.X)/2;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y - (float)imgsize.Y)/2;
+
+               v2s32 geom;
+               geom.X = stof(v_geom[0]) * (float)spacing.X;
+               geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+
+               if (!data->explicit_size)
+                       warningstream<<"invalid use of background without a size[] element"<<std::endl;
+
+               bool clip = false;
+               if (parts.size() == 4 && is_yes(parts[3])) {
+                       pos.X = stoi(v_pos[0]); //acts as offset
+                       pos.Y = stoi(v_pos[1]); //acts as offset
+                       clip = true;
+               }
+               m_backgrounds.emplace_back(name, pos, geom, clip);
+
+               return;
+       }
+       errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseTableOptions(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       data->table_options.clear();
+       for (const std::string &part : parts) {
+               // Parse table option
+               std::string opt = unescape_string(part);
+               data->table_options.push_back(GUITable::splitOption(opt));
+       }
+}
+
+void GUIFormSpecMenu::parseTableColumns(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       data->table_columns.clear();
+       for (const std::string &part : parts) {
+               std::vector<std::string> col_parts = split(part,',');
+               GUITable::TableColumn column;
+               // Parse column type
+               if (!col_parts.empty())
+                       column.type = col_parts[0];
+               // Parse column options
+               for (size_t j = 1; j < col_parts.size(); ++j) {
+                       std::string opt = unescape_string(col_parts[j]);
+                       column.options.push_back(GUITable::splitOption(opt));
+               }
+               data->table_columns.push_back(column);
+       }
+}
+
+void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if (((parts.size() == 4) || (parts.size() == 5)) ||
+               ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = parts[2];
+               std::vector<std::string> items = split(parts[3],',');
+               std::string str_initial_selection;
+               std::string str_transparent = "false";
+
+               if (parts.size() >= 5)
+                       str_initial_selection = parts[4];
+
+               MY_CHECKPOS("table",0);
+               MY_CHECKGEOM("table",1);
+
+               v2s32 pos = padding + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               v2s32 geom;
+               geom.X = stof(v_geom[0]) * (float)spacing.X;
+               geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+               FieldSpec spec(
+                       name,
+                       L"",
+                       L"",
+                       258+m_fields.size()
+               );
+
+               spec.ftype = f_Table;
+
+               for (std::string &item : items) {
+                       item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item))));
+               }
+
+               //now really show table
+               GUITable *e = new GUITable(Environment, this, spec.fid, rect,
+                               m_tsrc);
+
+               if (spec.fname == data->focused_fieldname) {
+                       Environment->setFocus(e);
+               }
+
+               e->setTable(data->table_options, data->table_columns, items);
+
+               if (data->table_dyndata.find(name) != data->table_dyndata.end()) {
+                       e->setDynamicData(data->table_dyndata[name]);
+               }
+
+               if (!str_initial_selection.empty() && str_initial_selection != "0")
+                       e->setSelected(stoi(str_initial_selection));
+
+               m_tables.emplace_back(spec, e);
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream<< "Invalid table element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if (((parts.size() == 4) || (parts.size() == 5) || (parts.size() == 6)) ||
+               ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = parts[2];
+               std::vector<std::string> items = split(parts[3],',');
+               std::string str_initial_selection;
+               std::string str_transparent = "false";
+
+               if (parts.size() >= 5)
+                       str_initial_selection = parts[4];
+
+               if (parts.size() >= 6)
+                       str_transparent = parts[5];
+
+               MY_CHECKPOS("textlist",0);
+               MY_CHECKGEOM("textlist",1);
+
+               v2s32 pos = padding + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               v2s32 geom;
+               geom.X = stof(v_geom[0]) * (float)spacing.X;
+               geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+               FieldSpec spec(
+                       name,
+                       L"",
+                       L"",
+                       258+m_fields.size()
+               );
+
+               spec.ftype = f_Table;
+
+               for (std::string &item : items) {
+                       item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item))));
+               }
+
+               //now really show list
+               GUITable *e = new GUITable(Environment, this, spec.fid, rect,
+                               m_tsrc);
+
+               if (spec.fname == data->focused_fieldname) {
+                       Environment->setFocus(e);
+               }
+
+               e->setTextList(items, is_yes(str_transparent));
+
+               if (data->table_dyndata.find(name) != data->table_dyndata.end()) {
+                       e->setDynamicData(data->table_dyndata[name]);
+               }
+
+               if (!str_initial_selection.empty() && str_initial_selection != "0")
+                       e->setSelected(stoi(str_initial_selection));
+
+               m_tables.emplace_back(spec, e);
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream<< "Invalid textlist element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+
+void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 5) ||
+               ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::string name = parts[2];
+               std::vector<std::string> items = split(parts[3],',');
+               std::string str_initial_selection;
+               str_initial_selection = parts[4];
+
+               MY_CHECKPOS("dropdown",0);
+
+               v2s32 pos = padding + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               s32 width = stof(parts[1]) * (float)spacing.Y;
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y,
+                               pos.X + width, pos.Y + (m_btn_height * 2));
+
+               FieldSpec spec(
+                       name,
+                       L"",
+                       L"",
+                       258+m_fields.size()
+               );
+
+               spec.ftype = f_DropDown;
+               spec.send = true;
+
+               //now really show list
+               gui::IGUIComboBox *e = Environment->addComboBox(rect, this,spec.fid);
+
+               if (spec.fname == data->focused_fieldname) {
+                       Environment->setFocus(e);
+               }
+
+               for (const std::string &item : items) {
+                       e->addItem(unescape_translate(unescape_string(
+                               utf8_to_wide(item))).c_str());
+               }
+
+               if (!str_initial_selection.empty())
+                       e->setSelected(stoi(str_initial_selection)-1);
+
+               m_fields.push_back(spec);
+
+               m_dropdowns.emplace_back(spec, std::vector<std::string>());
+               std::vector<std::string> &values = m_dropdowns.back().second;
+               for (const std::string &item : items) {
+                       values.push_back(unescape_string(item));
+               }
+
+               return;
+       }
+       errorstream << "Invalid dropdown element(" << parts.size() << "): '"
+                               << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseFieldCloseOnEnter(parserData *data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+       if (parts.size() == 2 ||
+                       (parts.size() > 2 && m_formspec_version > FORMSPEC_API_VERSION)) {
+               field_close_on_enter[parts[0]] = is_yes(parts[1]);
+       }
+}
+
+void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 4) || (parts.size() == 5) ||
+               ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string name = parts[2];
+               std::string label = parts[3];
+
+               MY_CHECKPOS("pwdfield",0);
+               MY_CHECKGEOM("pwdfield",1);
+
+               v2s32 pos = pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               v2s32 geom;
+               geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+
+               pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+               pos.Y -= m_btn_height;
+               geom.Y = m_btn_height*2;
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+               std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
+
+               FieldSpec spec(
+                       name,
+                       wlabel,
+                       L"",
+                       258+m_fields.size()
+                       );
+
+               spec.send = true;
+               gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid);
+
+               if (spec.fname == data->focused_fieldname) {
+                       Environment->setFocus(e);
+               }
+
+               if (label.length() >= 1)
+               {
+                       int font_height = g_fontengine->getTextHeight();
+                       rect.UpperLeftCorner.Y -= font_height;
+                       rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
+                       addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
+               }
+
+               e->setPasswordBox(true,L'*');
+
+               irr::SEvent evt;
+               evt.EventType            = EET_KEY_INPUT_EVENT;
+               evt.KeyInput.Key         = KEY_END;
+               evt.KeyInput.Char        = 0;
+               evt.KeyInput.Control     = false;
+               evt.KeyInput.Shift       = false;
+               evt.KeyInput.PressedDown = true;
+               e->OnEvent(evt);
+
+               if (parts.size() >= 5) {
+                       // TODO: remove after 2016-11-03
+                       warningstream << "pwdfield: use field_close_on_enter[name, enabled]" <<
+                                       " instead of the 5th param" << std::endl;
+                       field_close_on_enter[name] = is_yes(parts[4]);
+               }
+
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream<< "Invalid pwdfield element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseSimpleField(parserData* data,
+               std::vector<std::string> &parts)
+{
+       std::string name = parts[0];
+       std::string label = parts[1];
+       std::string default_val = parts[2];
+
+       core::rect<s32> rect;
+
+       if(data->explicit_size)
+               warningstream<<"invalid use of unpositioned \"field\" in inventory"<<std::endl;
+
+       v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
+       pos.Y = ((m_fields.size()+2)*60);
+       v2s32 size = DesiredRect.getSize();
+
+       rect = core::rect<s32>(size.X / 2 - 150, pos.Y,
+                       (size.X / 2 - 150) + 300, pos.Y + (m_btn_height*2));
+
+
+       if(m_form_src)
+               default_val = m_form_src->resolveText(default_val);
+
+
+       std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
+
+       FieldSpec spec(
+               name,
+               wlabel,
+               utf8_to_wide(unescape_string(default_val)),
+               258+m_fields.size()
+       );
+
+       if (name.empty()) {
+               // spec field id to 0, this stops submit searching for a value that isn't there
+               addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid);
+       } else {
+               spec.send = true;
+               gui::IGUIElement *e;
+#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
+               if (g_settings->getBool("freetype")) {
+                       e = (gui::IGUIElement *) new gui::intlGUIEditBox(spec.fdefault.c_str(),
+                               true, Environment, this, spec.fid, rect);
+                       e->drop();
+               } else {
+#else
+               {
+#endif
+                       e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
+               }
+               if (spec.fname == data->focused_fieldname) {
+                       Environment->setFocus(e);
+               }
+
+               irr::SEvent evt;
+               evt.EventType            = EET_KEY_INPUT_EVENT;
+               evt.KeyInput.Key         = KEY_END;
+               evt.KeyInput.Char        = 0;
+               evt.KeyInput.Control     = 0;
+               evt.KeyInput.Shift       = 0;
+               evt.KeyInput.PressedDown = true;
+               e->OnEvent(evt);
+
+               if (label.length() >= 1)
+               {
+                       int font_height = g_fontengine->getTextHeight();
+                       rect.UpperLeftCorner.Y -= font_height;
+                       rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
+                       addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
+               }
+       }
+
+       if (parts.size() >= 4) {
+               // TODO: remove after 2016-11-03
+               warningstream << "field/simple: use field_close_on_enter[name, enabled]" <<
+                               " instead of the 4th param" << std::endl;
+               field_close_on_enter[name] = is_yes(parts[3]);
+       }
+
+       m_fields.push_back(spec);
+}
+
+void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector<std::string>& parts,
+               const std::string &type)
+{
+
+       std::vector<std::string> v_pos = split(parts[0],',');
+       std::vector<std::string> v_geom = split(parts[1],',');
+       std::string name = parts[2];
+       std::string label = parts[3];
+       std::string default_val = parts[4];
+
+       MY_CHECKPOS(type,0);
+       MY_CHECKGEOM(type,1);
+
+       v2s32 pos = pos_offset * spacing;
+       pos.X += stof(v_pos[0]) * (float) spacing.X;
+       pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+       v2s32 geom;
+
+       geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+
+       if (type == "textarea")
+       {
+               geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
+               pos.Y += m_btn_height;
+       }
+       else
+       {
+               pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+               pos.Y -= m_btn_height;
+               geom.Y = m_btn_height*2;
+       }
+
+       core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+       if(!data->explicit_size)
+               warningstream<<"invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
+
+       if(m_form_src)
+               default_val = m_form_src->resolveText(default_val);
+
+
+       std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
+
+       FieldSpec spec(
+               name,
+               wlabel,
+               utf8_to_wide(unescape_string(default_val)),
+               258+m_fields.size()
+       );
+
+       bool is_editable = !name.empty();
+
+       if (is_editable)
+               spec.send = true;
+
+       gui::IGUIEditBox *e = nullptr;
+       const wchar_t *text = spec.fdefault.empty() ?
+               wlabel.c_str() : spec.fdefault.c_str();
+
+#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
+       if (g_settings->getBool("freetype")) {
+               e = (gui::IGUIEditBox *) new gui::intlGUIEditBox(text,
+                       true, Environment, this, spec.fid, rect, is_editable, true);
+               e->drop();
+       } else {
+#else
+       {
+#endif
+               e = new GUIEditBoxWithScrollBar(text, true,
+                       Environment, this, spec.fid, rect, is_editable, true);
+       }
+
+       if (is_editable && spec.fname == data->focused_fieldname)
+               Environment->setFocus(e);
+
+       if (e) {
+               if (type == "textarea")
+               {
+                       e->setMultiLine(true);
+                       e->setWordWrap(true);
+                       e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
+               } else {
+                       irr::SEvent evt;
+                       evt.EventType            = EET_KEY_INPUT_EVENT;
+                       evt.KeyInput.Key         = KEY_END;
+                       evt.KeyInput.Char        = 0;
+                       evt.KeyInput.Control     = 0;
+                       evt.KeyInput.Shift       = 0;
+                       evt.KeyInput.PressedDown = true;
+                       e->OnEvent(evt);
+               }
+       }
+
+       if (is_editable && !label.empty()) {
+               int font_height = g_fontengine->getTextHeight();
+               rect.UpperLeftCorner.Y -= font_height;
+               rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
+               addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
+       }
+
+       if (parts.size() >= 6) {
+               // TODO: remove after 2016-11-03
+               warningstream << "field/textarea: use field_close_on_enter[name, enabled]" <<
+                               " instead of the 6th param" << std::endl;
+               field_close_on_enter[name] = is_yes(parts[5]);
+       }
+
+       m_fields.push_back(spec);
+}
+
+void GUIFormSpecMenu::parseField(parserData* data, const std::string &element,
+               const std::string &type)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if (parts.size() == 3 || parts.size() == 4) {
+               parseSimpleField(data,parts);
+               return;
+       }
+
+       if ((parts.size() == 5) || (parts.size() == 6) ||
+               ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               parseTextArea(data,parts,type);
+               return;
+       }
+       errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 2) ||
+               ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::string text = parts[1];
+
+               MY_CHECKPOS("label",0);
+
+               v2s32 pos = padding + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += (stof(v_pos[1]) + 7.0/30.0) * (float)spacing.Y;
+
+               if(!data->explicit_size)
+                       warningstream<<"invalid use of label without a size[] element"<<std::endl;
+
+               std::vector<std::string> lines = split(text, '\n');
+
+               for (unsigned int i = 0; i != lines.size(); i++) {
+                       // Lines are spaced at the nominal distance of
+                       // 2/5 inventory slot, even if the font doesn't
+                       // quite match that.  This provides consistent
+                       // form layout, at the expense of sometimes
+                       // having sub-optimal spacing for the font.
+                       // We multiply by 2 and then divide by 5, rather
+                       // than multiply by 0.4, to get exact results
+                       // in the integer cases: 0.4 is not exactly
+                       // representable in binary floating point.
+                       s32 posy = pos.Y + ((float)i) * spacing.Y * 2.0 / 5.0;
+                       std::wstring wlabel = utf8_to_wide(unescape_string(lines[i]));
+                       core::rect<s32> rect = core::rect<s32>(
+                               pos.X, posy - m_btn_height,
+                               pos.X + m_font->getDimension(wlabel.c_str()).Width,
+                               posy + m_btn_height);
+                       FieldSpec spec(
+                               "",
+                               wlabel,
+                               L"",
+                               258+m_fields.size()
+                       );
+                       gui::IGUIStaticText *e =
+                               addStaticText(Environment, spec.flabel.c_str(),
+                                       rect, false, false, this, spec.fid);
+                       e->setTextAlignment(gui::EGUIA_UPPERLEFT,
+                                               gui::EGUIA_CENTER);
+                       m_fields.push_back(spec);
+               }
+
+               return;
+       }
+       errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 2) ||
+               ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::wstring text = unescape_translate(
+                       unescape_string(utf8_to_wide(parts[1])));
+
+               MY_CHECKPOS("vertlabel",1);
+
+               v2s32 pos = padding + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+
+               core::rect<s32> rect = core::rect<s32>(
+                               pos.X, pos.Y+((imgsize.Y/2)- m_btn_height),
+                               pos.X+15, pos.Y +
+                                       font_line_height(m_font)
+                                       * (text.length()+1)
+                                       +((imgsize.Y/2)- m_btn_height));
+               //actually text.length() would be correct but adding +1 avoids to break all mods
+
+               if(!data->explicit_size)
+                       warningstream<<"invalid use of label without a size[] element"<<std::endl;
+
+               std::wstring label;
+
+               for (wchar_t i : text) {
+                       label += i;
+                       label += L"\n";
+               }
+
+               FieldSpec spec(
+                       "",
+                       label,
+                       L"",
+                       258+m_fields.size()
+               );
+               gui::IGUIStaticText *t =
+                               addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid);
+               t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &element,
+               const std::string &type)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if ((((parts.size() >= 5) && (parts.size() <= 8)) && (parts.size() != 6)) ||
+               ((parts.size() > 8) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string image_name = parts[2];
+               std::string name = parts[3];
+               std::string label = parts[4];
+
+               MY_CHECKPOS("imagebutton",0);
+               MY_CHECKGEOM("imagebutton",1);
+
+               v2s32 pos = padding + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+               v2s32 geom;
+               geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+               geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
+
+               bool noclip     = false;
+               bool drawborder = true;
+               std::string pressed_image_name;
+
+               if (parts.size() >= 7) {
+                       if (parts[5] == "true")
+                               noclip = true;
+                       if (parts[6] == "false")
+                               drawborder = false;
+               }
+
+               if (parts.size() >= 8) {
+                       pressed_image_name = parts[7];
+               }
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+               if(!data->explicit_size)
+                       warningstream<<"invalid use of image_button without a size[] element"<<std::endl;
+
+               image_name = unescape_string(image_name);
+               pressed_image_name = unescape_string(pressed_image_name);
+
+               std::wstring wlabel = utf8_to_wide(unescape_string(label));
+
+               FieldSpec spec(
+                       name,
+                       wlabel,
+                       utf8_to_wide(image_name),
+                       258+m_fields.size()
+               );
+               spec.ftype = f_Button;
+               if(type == "image_button_exit")
+                       spec.is_exit = true;
+
+               video::ITexture *texture = 0;
+               video::ITexture *pressed_texture = 0;
+               texture = m_tsrc->getTexture(image_name);
+               if (!pressed_image_name.empty())
+                       pressed_texture = m_tsrc->getTexture(pressed_image_name);
+               else
+                       pressed_texture = texture;
+
+               gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
+
+               if (spec.fname == data->focused_fieldname) {
+                       Environment->setFocus(e);
+               }
+
+               e->setUseAlphaChannel(true);
+               e->setImage(guiScalingImageButton(
+                       Environment->getVideoDriver(), texture, geom.X, geom.Y));
+               e->setPressedImage(guiScalingImageButton(
+                       Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
+               e->setScaleImage(true);
+               e->setNotClipped(noclip);
+               e->setDrawBorder(drawborder);
+
+               m_fields.push_back(spec);
+               return;
+       }
+
+       errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if (((parts.size() == 4) || (parts.size() == 6)) ||
+               ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::string name = parts[1];
+               std::vector<std::string> buttons = split(parts[2],',');
+               std::string str_index = parts[3];
+               bool show_background = true;
+               bool show_border = true;
+               int tab_index = stoi(str_index) -1;
+
+               MY_CHECKPOS("tabheader",0);
+
+               if (parts.size() == 6) {
+                       if (parts[4] == "true")
+                               show_background = false;
+                       if (parts[5] == "false")
+                               show_border = false;
+               }
+
+               FieldSpec spec(
+                       name,
+                       L"",
+                       L"",
+                       258+m_fields.size()
+               );
+
+               spec.ftype = f_TabHeader;
+
+               v2s32 pos = pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y - m_btn_height * 2;
+               v2s32 geom;
+               geom.X = DesiredRect.getWidth();
+               geom.Y = m_btn_height*2;
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
+                               pos.Y+geom.Y);
+
+               gui::IGUITabControl *e = Environment->addTabControl(rect, this,
+                               show_background, show_border, spec.fid);
+               e->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT,
+                               irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
+               e->setTabHeight(m_btn_height*2);
+
+               if (spec.fname == data->focused_fieldname) {
+                       Environment->setFocus(e);
+               }
+
+               e->setNotClipped(true);
+
+               for (const std::string &button : buttons) {
+                       e->addTab(unescape_translate(unescape_string(
+                               utf8_to_wide(button))).c_str(), -1);
+               }
+
+               if ((tab_index >= 0) &&
+                               (buttons.size() < INT_MAX) &&
+                               (tab_index < (int) buttons.size()))
+                       e->setActiveTab(tab_index);
+
+               m_fields.push_back(spec);
+               return;
+       }
+       errorstream << "Invalid TabHeader element(" << parts.size() << "): '"
+                       << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &element)
+{
+
+       if (m_client == 0) {
+               warningstream << "invalid use of item_image_button with m_client==0"
+                       << std::endl;
+               return;
+       }
+
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 5) ||
+               ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+               std::string item_name = parts[2];
+               std::string name = parts[3];
+               std::string label = parts[4];
+
+               label = unescape_string(label);
+               item_name = unescape_string(item_name);
+
+               MY_CHECKPOS("itemimagebutton",0);
+               MY_CHECKGEOM("itemimagebutton",1);
+
+               v2s32 pos = padding + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float)spacing.X;
+               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
+               v2s32 geom;
+               geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+               geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
+
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+
+               if(!data->explicit_size)
+                       warningstream<<"invalid use of item_image_button without a size[] element"<<std::endl;
+
+               IItemDefManager *idef = m_client->idef();
+               ItemStack item;
+               item.deSerialize(item_name, idef);
+
+               m_tooltips[name] =
+                       TooltipSpec(utf8_to_wide(item.getDefinition(idef).description),
+                                               m_default_tooltip_bgcolor,
+                                               m_default_tooltip_color);
+
+               FieldSpec spec(
+                       name,
+                       utf8_to_wide(label),
+                       utf8_to_wide(item_name),
+                       258 + m_fields.size()
+               );
+
+               gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, L"");
+
+               if (spec.fname == data->focused_fieldname) {
+                       Environment->setFocus(e);
+               }
+
+               spec.ftype = f_Button;
+               rect+=data->basepos-padding;
+               spec.rect=rect;
+               m_fields.push_back(spec);
+               pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float) spacing.X;
+               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+               m_itemimages.emplace_back("", item_name, e, pos, geom);
+               m_static_texts.emplace_back(utf8_to_wide(label), rect, e);
+               return;
+       }
+       errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if ((parts.size() == 3) ||
+               ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               std::vector<std::string> v_pos = split(parts[0],',');
+               std::vector<std::string> v_geom = split(parts[1],',');
+
+               MY_CHECKPOS("box",0);
+               MY_CHECKGEOM("box",1);
+
+               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
+               pos.X += stof(v_pos[0]) * (float) spacing.X;
+               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+
+               v2s32 geom;
+               geom.X = stof(v_geom[0]) * (float)spacing.X;
+               geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+
+               video::SColor tmp_color;
+
+               if (parseColorString(parts[2], tmp_color, false)) {
+                       BoxDrawSpec spec(pos, geom, tmp_color);
+
+                       m_boxes.push_back(spec);
+               }
+               else {
+                       errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'  INVALID COLOR"  << std::endl;
+               }
+               return;
+       }
+       errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if (((parts.size() == 1) || (parts.size() == 2)) ||
+                       ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION))) {
+               parseColorString(parts[0], m_bgcolor, false);
+
+               if (parts.size() == 2) {
+                       std::string fullscreen = parts[1];
+                       m_bgfullscreen = is_yes(fullscreen);
+               }
+
+               return;
+       }
+
+       errorstream << "Invalid bgcolor element(" << parts.size() << "): '" << element << "'"
+                       << std::endl;
+}
+
+void GUIFormSpecMenu::parseListColors(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+
+       if (((parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) ||
+               ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+       {
+               parseColorString(parts[0], m_slotbg_n, false);
+               parseColorString(parts[1], m_slotbg_h, false);
+
+               if (parts.size() >= 3) {
+                       if (parseColorString(parts[2], m_slotbordercolor, false)) {
+                               m_slotborder = true;
+                       }
+               }
+               if (parts.size() == 5) {
+                       video::SColor tmp_color;
+
+                       if (parseColorString(parts[3], tmp_color, false))
+                               m_default_tooltip_bgcolor = tmp_color;
+                       if (parseColorString(parts[4], tmp_color, false))
+                               m_default_tooltip_color = tmp_color;
+               }
+               return;
+       }
+       errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element,';');
+       if (parts.size() == 2) {
+               std::string name = parts[0];
+               m_tooltips[name] = TooltipSpec(utf8_to_wide(unescape_string(parts[1])),
+                       m_default_tooltip_bgcolor, m_default_tooltip_color);
+               return;
+       }
+
+       if (parts.size() == 4) {
+               std::string name = parts[0];
+               video::SColor tmp_color1, tmp_color2;
+               if ( parseColorString(parts[2], tmp_color1, false) && parseColorString(parts[3], tmp_color2, false) ) {
+                       m_tooltips[name] = TooltipSpec(utf8_to_wide(unescape_string(parts[1])),
+                               tmp_color1, tmp_color2);
+                       return;
+               }
+       }
+       errorstream<< "Invalid tooltip element(" << parts.size() << "): '" << element << "'"  << std::endl;
+}
+
+bool GUIFormSpecMenu::parseVersionDirect(const std::string &data)
+{
+       //some prechecks
+       if (data.empty())
+               return false;
+
+       std::vector<std::string> parts = split(data,'[');
+
+       if (parts.size() < 2) {
+               return false;
+       }
+
+       if (parts[0] != "formspec_version") {
+               return false;
+       }
+
+       if (is_number(parts[1])) {
+               m_formspec_version = mystoi(parts[1]);
+               return true;
+       }
+
+       return false;
+}
+
+bool GUIFormSpecMenu::parseSizeDirect(parserData* data, const std::string &element)
+{
+       if (element.empty())
+               return false;
+
+       std::vector<std::string> parts = split(element,'[');
+
+       if (parts.size() < 2)
+               return false;
+
+       std::string type = trim(parts[0]);
+       std::string description = trim(parts[1]);
+
+       if (type != "size" && type != "invsize")
+               return false;
+
+       if (type == "invsize")
+               log_deprecated("Deprecated formspec element \"invsize\" is used");
+
+       parseSize(data, description);
+
+       return true;
+}
+
+bool GUIFormSpecMenu::parsePositionDirect(parserData *data, const std::string &element)
+{
+       if (element.empty())
+               return false;
+
+       std::vector<std::string> parts = split(element, '[');
+
+       if (parts.size() != 2)
+               return false;
+
+       std::string type = trim(parts[0]);
+       std::string description = trim(parts[1]);
+
+       if (type != "position")
+               return false;
+
+       parsePosition(data, description);
+
+       return true;
+}
+
+void GUIFormSpecMenu::parsePosition(parserData *data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element, ',');
+
+       if (parts.size() == 2) {
+               data->offset.X = stof(parts[0]);
+               data->offset.Y = stof(parts[1]);
+               return;
+       }
+
+       errorstream << "Invalid position element (" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+bool GUIFormSpecMenu::parseAnchorDirect(parserData *data, const std::string &element)
+{
+       if (element.empty())
+               return false;
+
+       std::vector<std::string> parts = split(element, '[');
+
+       if (parts.size() != 2)
+               return false;
+
+       std::string type = trim(parts[0]);
+       std::string description = trim(parts[1]);
+
+       if (type != "anchor")
+               return false;
+
+       parseAnchor(data, description);
+
+       return true;
+}
+
+void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element)
+{
+       std::vector<std::string> parts = split(element, ',');
+
+       if (parts.size() == 2) {
+               data->anchor.X = stof(parts[0]);
+               data->anchor.Y = stof(parts[1]);
+               return;
+       }
+
+       errorstream << "Invalid anchor element (" << parts.size() << "): '" << element
+                       << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
+{
+       //some prechecks
+       if (element.empty())
+               return;
+
+       std::vector<std::string> parts = split(element,'[');
+
+       // ugly workaround to keep compatibility
+       if (parts.size() > 2) {
+               if (trim(parts[0]) == "image") {
+                       for (unsigned int i=2;i< parts.size(); i++) {
+                               parts[1] += "[" + parts[i];
+                       }
+               }
+               else { return; }
+       }
+
+       if (parts.size() < 2) {
+               return;
+       }
+
+       std::string type = trim(parts[0]);
+       std::string description = trim(parts[1]);
+
+       if (type == "container") {
+               parseContainer(data, description);
+               return;
+       }
+
+       if (type == "container_end") {
+               parseContainerEnd(data);
+               return;
+       }
+
+       if (type == "list") {
+               parseList(data, description);
+               return;
+       }
+
+       if (type == "listring") {
+               parseListRing(data, description);
+               return;
+       }
+
+       if (type == "checkbox") {
+               parseCheckbox(data, description);
+               return;
+       }
+
+       if (type == "image") {
+               parseImage(data, description);
+               return;
+       }
+
+       if (type == "item_image") {
+               parseItemImage(data, description);
+               return;
+       }
+
+       if (type == "button" || type == "button_exit") {
+               parseButton(data, description, type);
+               return;
+       }
+
+       if (type == "background") {
+               parseBackground(data,description);
+               return;
+       }
+
+       if (type == "tableoptions"){
+               parseTableOptions(data,description);
+               return;
+       }
+
+       if (type == "tablecolumns"){
+               parseTableColumns(data,description);
+               return;
+       }
+
+       if (type == "table"){
+               parseTable(data,description);
+               return;
+       }
+
+       if (type == "textlist"){
+               parseTextList(data,description);
+               return;
+       }
+
+       if (type == "dropdown"){
+               parseDropDown(data,description);
+               return;
+       }
+
+       if (type == "field_close_on_enter") {
+               parseFieldCloseOnEnter(data, description);
+               return;
+       }
+
+       if (type == "pwdfield") {
+               parsePwdField(data,description);
+               return;
+       }
+
+       if ((type == "field") || (type == "textarea")){
+               parseField(data,description,type);
+               return;
+       }
+
+       if (type == "label") {
+               parseLabel(data,description);
+               return;
+       }
+
+       if (type == "vertlabel") {
+               parseVertLabel(data,description);
+               return;
+       }
+
+       if (type == "item_image_button") {
+               parseItemImageButton(data,description);
+               return;
+       }
+
+       if ((type == "image_button") || (type == "image_button_exit")) {
+               parseImageButton(data,description,type);
+               return;
+       }
+
+       if (type == "tabheader") {
+               parseTabHeader(data,description);
+               return;
+       }
+
+       if (type == "box") {
+               parseBox(data,description);
+               return;
+       }
+
+       if (type == "bgcolor") {
+               parseBackgroundColor(data,description);
+               return;
+       }
+
+       if (type == "listcolors") {
+               parseListColors(data,description);
+               return;
+       }
+
+       if (type == "tooltip") {
+               parseTooltip(data,description);
+               return;
+       }
+
+       if (type == "scrollbar") {
+               parseScrollBar(data, description);
+               return;
+       }
+
+       // Ignore others
+       infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
+                       << std::endl;
+}
+
+void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
+{
+       /* useless to regenerate without a screensize */
+       if ((screensize.X <= 0) || (screensize.Y <= 0)) {
+               return;
+       }
+
+       parserData mydata;
+
+       //preserve tables
+       for (auto &m_table : m_tables) {
+               std::string tablename = m_table.first.fname;
+               GUITable *table = m_table.second;
+               mydata.table_dyndata[tablename] = table->getDynamicData();
+       }
+
+       //set focus
+       if (!m_focused_element.empty())
+               mydata.focused_fieldname = m_focused_element;
+
+       //preserve focus
+       gui::IGUIElement *focused_element = Environment->getFocus();
+       if (focused_element && focused_element->getParent() == this) {
+               s32 focused_id = focused_element->getID();
+               if (focused_id > 257) {
+                       for (const GUIFormSpecMenu::FieldSpec &field : m_fields) {
+                               if (field.fid == focused_id) {
+                                       mydata.focused_fieldname = field.fname;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       // Remove children
+       removeChildren();
+
+       for (auto &table_it : m_tables) {
+               table_it.second->drop();
+       }
+
+       mydata.size= v2s32(100,100);
+       mydata.screensize = screensize;
+       mydata.offset = v2f32(0.5f, 0.5f);
+       mydata.anchor = v2f32(0.5f, 0.5f);
+
+       // Base position of contents of form
+       mydata.basepos = getBasePos();
+
+       /* Convert m_init_draw_spec to m_inventorylists */
+
+       m_inventorylists.clear();
+       m_images.clear();
+       m_backgrounds.clear();
+       m_itemimages.clear();
+       m_tables.clear();
+       m_checkboxes.clear();
+       m_scrollbars.clear();
+       m_fields.clear();
+       m_boxes.clear();
+       m_tooltips.clear();
+       m_inventory_rings.clear();
+       m_static_texts.clear();
+       m_dropdowns.clear();
+
+       m_bgfullscreen = false;
+
+       {
+               v3f formspec_bgcolor = g_settings->getV3F("formspec_default_bg_color");
+               m_bgcolor = video::SColor(
+                       (u8) clamp_u8(g_settings->getS32("formspec_default_bg_opacity")),
+                       clamp_u8(myround(formspec_bgcolor.X)),
+                       clamp_u8(myround(formspec_bgcolor.Y)),
+                       clamp_u8(myround(formspec_bgcolor.Z))
+               );
+       }
+
+       {
+               v3f formspec_bgcolor = g_settings->getV3F("formspec_fullscreen_bg_color");
+               m_fullscreen_bgcolor = video::SColor(
+                       (u8) clamp_u8(g_settings->getS32("formspec_fullscreen_bg_opacity")),
+                       clamp_u8(myround(formspec_bgcolor.X)),
+                       clamp_u8(myround(formspec_bgcolor.Y)),
+                       clamp_u8(myround(formspec_bgcolor.Z))
+               );
+       }
+
+
+       m_slotbg_n = video::SColor(255,128,128,128);
+       m_slotbg_h = video::SColor(255,192,192,192);
+
+       m_default_tooltip_bgcolor = video::SColor(255,110,130,60);
+       m_default_tooltip_color = video::SColor(255,255,255,255);
+
+       m_slotbordercolor = video::SColor(200,0,0,0);
+       m_slotborder = false;
+
+       // Add tooltip
+       {
+               assert(!m_tooltip_element);
+               // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
+               m_tooltip_element = addStaticText(Environment, L"",core::rect<s32>(0,0,110,18));
+               m_tooltip_element->enableOverrideColor(true);
+               m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
+               m_tooltip_element->setDrawBackground(true);
+               m_tooltip_element->setDrawBorder(true);
+               m_tooltip_element->setOverrideColor(m_default_tooltip_color);
+               m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
+               m_tooltip_element->setWordWrap(false);
+               //we're not parent so no autograb for this one!
+               m_tooltip_element->grab();
+       }
+
+       std::vector<std::string> elements = split(m_formspec_string,']');
+       unsigned int i = 0;
+
+       /* try to read version from first element only */
+       if (!elements.empty()) {
+               if ( parseVersionDirect(elements[0]) ) {
+                       i++;
+               }
+       }
+
+       /* we need size first in order to calculate image scale */
+       mydata.explicit_size = false;
+       for (; i< elements.size(); i++) {
+               if (!parseSizeDirect(&mydata, elements[i])) {
+                       break;
+               }
+       }
+
+       /* "position" element is always after "size" element if it used */
+       for (; i< elements.size(); i++) {
+               if (!parsePositionDirect(&mydata, elements[i])) {
+                       break;
+               }
+       }
+
+       /* "anchor" element is always after "position" (or  "size" element) if it used */
+       for (; i< elements.size(); i++) {
+               if (!parseAnchorDirect(&mydata, elements[i])) {
+                       break;
+               }
+       }
+
+
+       if (mydata.explicit_size) {
+               // compute scaling for specified form size
+               if (m_lock) {
+                       v2u32 current_screensize = RenderingEngine::get_video_driver()->getScreenSize();
+                       v2u32 delta = current_screensize - m_lockscreensize;
+
+                       if (current_screensize.Y > m_lockscreensize.Y)
+                               delta.Y /= 2;
+                       else
+                               delta.Y = 0;
+
+                       if (current_screensize.X > m_lockscreensize.X)
+                               delta.X /= 2;
+                       else
+                               delta.X = 0;
+
+                       offset = v2s32(delta.X,delta.Y);
+
+                       mydata.screensize = m_lockscreensize;
+               } else {
+                       offset = v2s32(0,0);
+               }
+
+               double gui_scaling = g_settings->getFloat("gui_scaling");
+               double screen_dpi = RenderingEngine::getDisplayDensity() * 96;
+
+               double use_imgsize;
+               if (m_lock) {
+                       // In fixed-size mode, inventory image size
+                       // is 0.53 inch multiplied by the gui_scaling
+                       // config parameter.  This magic size is chosen
+                       // to make the main menu (15.5 inventory images
+                       // wide, including border) just fit into the
+                       // default window (800 pixels wide) at 96 DPI
+                       // and default scaling (1.00).
+                       use_imgsize = 0.5555 * screen_dpi * gui_scaling;
+               } else {
+                       // In variable-size mode, we prefer to make the
+                       // inventory image size 1/15 of screen height,
+                       // multiplied by the gui_scaling config parameter.
+                       // If the preferred size won't fit the whole
+                       // form on the screen, either horizontally or
+                       // vertically, then we scale it down to fit.
+                       // (The magic numbers in the computation of what
+                       // fits arise from the scaling factors in the
+                       // following stanza, including the form border,
+                       // help text space, and 0.1 inventory slot spare.)
+                       // However, a minimum size is also set, that
+                       // the image size can't be less than 0.3 inch
+                       // multiplied by gui_scaling, even if this means
+                       // the form doesn't fit the screen.
+                       double prefer_imgsize = mydata.screensize.Y / 15 *
+                                                       gui_scaling;
+                       double fitx_imgsize = mydata.screensize.X /
+                               ((5.0/4.0) * (0.5 + mydata.invsize.X));
+                       double fity_imgsize = mydata.screensize.Y /
+                               ((15.0/13.0) * (0.85 * mydata.invsize.Y));
+                       double screen_dpi = RenderingEngine::getDisplayDensity() * 96;
+                       double min_imgsize = 0.3 * screen_dpi * gui_scaling;
+                       use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize,
+                               MYMIN(fitx_imgsize, fity_imgsize)));
+               }
+
+               // Everything else is scaled in proportion to the
+               // inventory image size.  The inventory slot spacing
+               // is 5/4 image size horizontally and 15/13 image size
+               // vertically.  The padding around the form (incorporating
+               // the border of the outer inventory slots) is 3/8
+               // image size.  Font height (baseline to baseline)
+               // is 2/5 vertical inventory slot spacing, and button
+               // half-height is 7/8 of font height.
+               imgsize = v2s32(use_imgsize, use_imgsize);
+               spacing = v2s32(use_imgsize*5.0/4, use_imgsize*15.0/13);
+               padding = v2s32(use_imgsize*3.0/8, use_imgsize*3.0/8);
+               m_btn_height = use_imgsize*15.0/13 * 0.35;
+
+               m_font = g_fontengine->getFont();
+
+               mydata.size = v2s32(
+                       padding.X*2+spacing.X*(mydata.invsize.X-1.0)+imgsize.X,
+                       padding.Y*2+spacing.Y*(mydata.invsize.Y-1.0)+imgsize.Y + m_btn_height*2.0/3.0
+               );
+               DesiredRect = mydata.rect = core::rect<s32>(
+                               (s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * (f32)mydata.size.X) + offset.X,
+                               (s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * (f32)mydata.size.Y) + offset.Y,
+                               (s32)((f32)mydata.screensize.X * mydata.offset.X) + (s32)((1.0 - mydata.anchor.X) * (f32)mydata.size.X) + offset.X,
+                               (s32)((f32)mydata.screensize.Y * mydata.offset.Y) + (s32)((1.0 - mydata.anchor.Y) * (f32)mydata.size.Y) + offset.Y
+               );
+       } else {
+               // Non-size[] form must consist only of text fields and
+               // implicit "Proceed" button.  Use default font, and
+               // temporary form size which will be recalculated below.
+               m_font = g_fontengine->getFont();
+               m_btn_height = font_line_height(m_font) * 0.875;
+               DesiredRect = core::rect<s32>(
+                       (s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * 580.0),
+                       (s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * 300.0),
+                       (s32)((f32)mydata.screensize.X * mydata.offset.X) + (s32)((1.0 - mydata.anchor.X) * 580.0),
+                       (s32)((f32)mydata.screensize.Y * mydata.offset.Y) + (s32)((1.0 - mydata.anchor.Y) * 300.0)
+               );
+       }
+       recalculateAbsolutePosition(false);
+       mydata.basepos = getBasePos();
+       m_tooltip_element->setOverrideFont(m_font);
+
+       gui::IGUISkin* skin = Environment->getSkin();
+       sanity_check(skin);
+       gui::IGUIFont *old_font = skin->getFont();
+       skin->setFont(m_font);
+
+       pos_offset = v2s32();
+       for (; i< elements.size(); i++) {
+               parseElement(&mydata, elements[i]);
+       }
+
+       if (!container_stack.empty()) {
+               errorstream << "Invalid formspec string: container was never closed!"
+                       << std::endl;
+       }
+
+       // If there are fields without explicit size[], add a "Proceed"
+       // button and adjust size to fit all the fields.
+       if (!m_fields.empty() && !mydata.explicit_size) {
+               mydata.rect = core::rect<s32>(
+                               mydata.screensize.X/2 - 580/2,
+                               mydata.screensize.Y/2 - 300/2,
+                               mydata.screensize.X/2 + 580/2,
+                               mydata.screensize.Y/2 + 240/2+(m_fields.size()*60)
+               );
+               DesiredRect = mydata.rect;
+               recalculateAbsolutePosition(false);
+               mydata.basepos = getBasePos();
+
+               {
+                       v2s32 pos = mydata.basepos;
+                       pos.Y = ((m_fields.size()+2)*60);
+
+                       v2s32 size = DesiredRect.getSize();
+                       mydata.rect =
+                                       core::rect<s32>(size.X/2-70, pos.Y,
+                                                       (size.X/2-70)+140, pos.Y + (m_btn_height*2));
+                       const wchar_t *text = wgettext("Proceed");
+                       Environment->addButton(mydata.rect, this, 257, text);
+                       delete[] text;
+               }
+
+       }
+
+       //set initial focus if parser didn't set it
+       focused_element = Environment->getFocus();
+       if (!focused_element
+                       || !isMyChild(focused_element)
+                       || focused_element->getType() == gui::EGUIET_TAB_CONTROL)
+               setInitialFocus();
+
+       skin->setFont(old_font);
+}
+
+#ifdef __ANDROID__
+bool GUIFormSpecMenu::getAndroidUIInput()
+{
+       /* no dialog shown */
+       if (m_JavaDialogFieldName == "") {
+               return false;
+       }
+
+       /* still waiting */
+       if (porting::getInputDialogState() == -1) {
+               return true;
+       }
+
+       std::string fieldname = m_JavaDialogFieldName;
+       m_JavaDialogFieldName = "";
+
+       /* no value abort dialog processing */
+       if (porting::getInputDialogState() != 0) {
+               return false;
+       }
+
+       for(std::vector<FieldSpec>::iterator iter =  m_fields.begin();
+                       iter != m_fields.end(); ++iter) {
+
+               if (iter->fname != fieldname) {
+                       continue;
+               }
+               IGUIElement* tochange = getElementFromId(iter->fid);
+
+               if (tochange == 0) {
+                       return false;
+               }
+
+               if (tochange->getType() != irr::gui::EGUIET_EDIT_BOX) {
+                       return false;
+               }
+
+               std::string text = porting::getInputDialogValue();
+
+               ((gui::IGUIEditBox*) tochange)->
+                       setText(utf8_to_wide(text).c_str());
+       }
+       return false;
+}
+#endif
+
+GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
+{
+       core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
+
+       for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) {
+               for(s32 i=0; i<s.geom.X*s.geom.Y; i++) {
+                       s32 item_i = i + s.start_item_i;
+                       s32 x = (i%s.geom.X) * spacing.X;
+                       s32 y = (i/s.geom.X) * spacing.Y;
+                       v2s32 p0(x,y);
+                       core::rect<s32> rect = imgrect + s.pos + p0;
+                       if(rect.isPointInside(p))
+                       {
+                               return ItemSpec(s.inventoryloc, s.listname, item_i);
+                       }
+               }
+       }
+
+       return ItemSpec(InventoryLocation(), "", -1);
+}
+
+void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
+               bool &item_hovered)
+{
+       video::IVideoDriver* driver = Environment->getVideoDriver();
+
+       Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
+       if(!inv){
+               warningstream<<"GUIFormSpecMenu::drawList(): "
+                               <<"The inventory location "
+                               <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
+                               <<std::endl;
+               return;
+       }
+       InventoryList *ilist = inv->getList(s.listname);
+       if(!ilist){
+               warningstream<<"GUIFormSpecMenu::drawList(): "
+                               <<"The inventory list \""<<s.listname<<"\" @ \""
+                               <<s.inventoryloc.dump()<<"\" doesn't exist"
+                               <<std::endl;
+               return;
+       }
+
+       core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
+
+       for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
+       {
+               s32 item_i = i + s.start_item_i;
+               if(item_i >= (s32) ilist->getSize())
+                       break;
+               s32 x = (i%s.geom.X) * spacing.X;
+               s32 y = (i/s.geom.X) * spacing.Y;
+               v2s32 p(x,y);
+               core::rect<s32> rect = imgrect + s.pos + p;
+               ItemStack item;
+               if(ilist)
+                       item = ilist->getItem(item_i);
+
+               bool selected = m_selected_item
+                       && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
+                       && m_selected_item->listname == s.listname
+                       && m_selected_item->i == item_i;
+               bool hovering = rect.isPointInside(m_pointer);
+               ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED :
+                       (hovering ? IT_ROT_HOVERED : IT_ROT_NONE);
+
+               if (phase == 0) {
+                       if (hovering) {
+                               item_hovered = true;
+                               driver->draw2DRectangle(m_slotbg_h, rect, &AbsoluteClippingRect);
+                       } else {
+                               driver->draw2DRectangle(m_slotbg_n, rect, &AbsoluteClippingRect);
+                       }
+               }
+
+               //Draw inv slot borders
+               if (m_slotborder) {
+                       s32 x1 = rect.UpperLeftCorner.X;
+                       s32 y1 = rect.UpperLeftCorner.Y;
+                       s32 x2 = rect.LowerRightCorner.X;
+                       s32 y2 = rect.LowerRightCorner.Y;
+                       s32 border = 1;
+                       driver->draw2DRectangle(m_slotbordercolor,
+                               core::rect<s32>(v2s32(x1 - border, y1 - border),
+                                                               v2s32(x2 + border, y1)), NULL);
+                       driver->draw2DRectangle(m_slotbordercolor,
+                               core::rect<s32>(v2s32(x1 - border, y2),
+                                                               v2s32(x2 + border, y2 + border)), NULL);
+                       driver->draw2DRectangle(m_slotbordercolor,
+                               core::rect<s32>(v2s32(x1 - border, y1),
+                                                               v2s32(x1, y2)), NULL);
+                       driver->draw2DRectangle(m_slotbordercolor,
+                               core::rect<s32>(v2s32(x2, y1),
+                                                               v2s32(x2 + border, y2)), NULL);
+               }
+
+               if(phase == 1)
+               {
+                       // Draw item stack
+                       if(selected)
+                       {
+                               item.takeItem(m_selected_amount);
+                       }
+                       if(!item.empty())
+                       {
+                               drawItemStack(driver, m_font, item,
+                                       rect, &AbsoluteClippingRect, m_client,
+                                       rotation_kind);
+                       }
+
+                       // Draw tooltip
+                       std::wstring tooltip_text;
+                       if (hovering && !m_selected_item) {
+                               const std::string &desc = item.metadata.getString("description");
+                               if (desc.empty())
+                                       tooltip_text =
+                                               utf8_to_wide(item.getDefinition(m_client->idef()).description);
+                               else
+                                       tooltip_text = utf8_to_wide(desc);
+
+                               if (!item.name.empty()) {
+                                       if (tooltip_text.empty())
+                                               tooltip_text = utf8_to_wide(item.name);
+                                       if (m_tooltip_append_itemname)
+                                               tooltip_text += utf8_to_wide(" [" + item.name + "]");
+                               }
+                       }
+                       if (!tooltip_text.empty()) {
+                               showTooltip(tooltip_text, m_default_tooltip_color,
+                                       m_default_tooltip_bgcolor);
+                       }
+               }
+       }
+}
+
+void GUIFormSpecMenu::drawSelectedItem()
+{
+       video::IVideoDriver* driver = Environment->getVideoDriver();
+
+       if (!m_selected_item) {
+               drawItemStack(driver, m_font, ItemStack(),
+                       core::rect<s32>(v2s32(0, 0), v2s32(0, 0)),
+                       NULL, m_client, IT_ROT_DRAGGED);
+               return;
+       }
+
+       Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
+       sanity_check(inv);
+       InventoryList *list = inv->getList(m_selected_item->listname);
+       sanity_check(list);
+       ItemStack stack = list->getItem(m_selected_item->i);
+       stack.count = m_selected_amount;
+
+       core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
+       core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
+       rect.constrainTo(driver->getViewPort());
+       drawItemStack(driver, m_font, stack, rect, NULL, m_client, IT_ROT_DRAGGED);
+}
+
+void GUIFormSpecMenu::drawMenu()
+{
+       if (m_form_src) {
+               const std::string &newform = m_form_src->getForm();
+               if (newform != m_formspec_string) {
+                       m_formspec_string = newform;
+                       regenerateGui(m_screensize_old);
+               }
+       }
+
+       gui::IGUISkin* skin = Environment->getSkin();
+       sanity_check(skin != NULL);
+       gui::IGUIFont *old_font = skin->getFont();
+       skin->setFont(m_font);
+
+       updateSelectedItem();
+
+       video::IVideoDriver* driver = Environment->getVideoDriver();
+
+       v2u32 screenSize = driver->getScreenSize();
+       core::rect<s32> allbg(0, 0, screenSize.X, screenSize.Y);
+
+       if (m_bgfullscreen)
+               driver->draw2DRectangle(m_fullscreen_bgcolor, allbg, &allbg);
+       else
+               driver->draw2DRectangle(m_bgcolor, AbsoluteRect, &AbsoluteClippingRect);
+
+       m_tooltip_element->setVisible(false);
+
+       /*
+               Draw backgrounds
+       */
+       for (const GUIFormSpecMenu::ImageDrawSpec &spec : m_backgrounds) {
+               video::ITexture *texture = m_tsrc->getTexture(spec.name);
+
+               if (texture != 0) {
+                       // Image size on screen
+                       core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
+                       // Image rectangle on screen
+                       core::rect<s32> rect = imgrect + spec.pos;
+
+                       if (spec.clip) {
+                               core::dimension2d<s32> absrec_size = AbsoluteRect.getSize();
+                               rect = core::rect<s32>(AbsoluteRect.UpperLeftCorner.X - spec.pos.X,
+                                                                       AbsoluteRect.UpperLeftCorner.Y - spec.pos.Y,
+                                                                       AbsoluteRect.UpperLeftCorner.X + absrec_size.Width + spec.pos.X,
+                                                                       AbsoluteRect.UpperLeftCorner.Y + absrec_size.Height + spec.pos.Y);
+                       }
+
+                       const video::SColor color(255,255,255,255);
+                       const video::SColor colors[] = {color,color,color,color};
+                       draw2DImageFilterScaled(driver, texture, rect,
+                               core::rect<s32>(core::position2d<s32>(0,0),
+                                               core::dimension2di(texture->getOriginalSize())),
+                               NULL/*&AbsoluteClippingRect*/, colors, true);
+               } else {
+                       errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl;
+                       errorstream << "\t" << spec.name << std::endl;
+               }
+       }
+
+       /*
+               Draw Boxes
+       */
+       for (const GUIFormSpecMenu::BoxDrawSpec &spec : m_boxes) {
+               irr::video::SColor todraw = spec.color;
+
+               todraw.setAlpha(140);
+
+               core::rect<s32> rect(spec.pos.X,spec.pos.Y,
+                                                       spec.pos.X + spec.geom.X,spec.pos.Y + spec.geom.Y);
+
+               driver->draw2DRectangle(todraw, rect, 0);
+       }
+
+       /*
+               Call base class
+       */
+       gui::IGUIElement::draw();
+
+       /*
+               Draw images
+       */
+       for (const GUIFormSpecMenu::ImageDrawSpec &spec : m_images) {
+               video::ITexture *texture = m_tsrc->getTexture(spec.name);
+
+               if (texture != 0) {
+                       const core::dimension2d<u32>& img_origsize = texture->getOriginalSize();
+                       // Image size on screen
+                       core::rect<s32> imgrect;
+
+                       if (spec.scale)
+                               imgrect = core::rect<s32>(0,0,spec.geom.X, spec.geom.Y);
+                       else {
+
+                               imgrect = core::rect<s32>(0,0,img_origsize.Width,img_origsize.Height);
+                       }
+                       // Image rectangle on screen
+                       core::rect<s32> rect = imgrect + spec.pos;
+                       const video::SColor color(255,255,255,255);
+                       const video::SColor colors[] = {color,color,color,color};
+                       draw2DImageFilterScaled(driver, texture, rect,
+                               core::rect<s32>(core::position2d<s32>(0,0),img_origsize),
+                               NULL/*&AbsoluteClippingRect*/, colors, true);
+               }
+               else {
+                       errorstream << "GUIFormSpecMenu::drawMenu() Draw images unable to load texture:" << std::endl;
+                       errorstream << "\t" << spec.name << std::endl;
+               }
+       }
+
+       /*
+               Draw item images
+       */
+       for (const GUIFormSpecMenu::ImageDrawSpec &spec : m_itemimages) {
+               if (m_client == 0)
+                       break;
+
+               IItemDefManager *idef = m_client->idef();
+               ItemStack item;
+               item.deSerialize(spec.item_name, idef);
+               core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
+               // Viewport rectangle on screen
+               core::rect<s32> rect = imgrect + spec.pos;
+               if (spec.parent_button && spec.parent_button->isPressed()) {
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
+                       rect += core::dimension2d<s32>(
+                               0.05 * (float)rect.getWidth(), 0.05 * (float)rect.getHeight());
+#else
+                       rect += core::dimension2d<s32>(
+                               skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
+                               skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y));
+#endif
+               }
+               drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect,
+                               m_client, IT_ROT_NONE);
+       }
+
+       /*
+               Draw items
+               Phase 0: Item slot rectangles
+               Phase 1: Item images; prepare tooltip
+       */
+       bool item_hovered = false;
+       int start_phase = 0;
+       for (int phase = start_phase; phase <= 1; phase++) {
+               for (const GUIFormSpecMenu::ListDrawSpec &spec : m_inventorylists) {
+                       drawList(spec, phase, item_hovered);
+               }
+       }
+       if (!item_hovered) {
+               drawItemStack(driver, m_font, ItemStack(),
+                       core::rect<s32>(v2s32(0, 0), v2s32(0, 0)),
+                       NULL, m_client, IT_ROT_HOVERED);
+       }
+
+/* TODO find way to show tooltips on touchscreen */
+#ifndef HAVE_TOUCHSCREENGUI
+       m_pointer = RenderingEngine::get_raw_device()->getCursorControl()->getPosition();
+#endif
+
+       /*
+               Draw static text elements
+       */
+       for (const GUIFormSpecMenu::StaticTextSpec &spec : m_static_texts) {
+               core::rect<s32> rect = spec.rect;
+               if (spec.parent_button && spec.parent_button->isPressed()) {
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
+                       rect += core::dimension2d<s32>(
+                               0.05 * (float)rect.getWidth(), 0.05 * (float)rect.getHeight());
+#else
+                       // Use image offset instead of text's because its a bit smaller
+                       // and fits better, also TEXT_OFFSET_X is always 0
+                       rect += core::dimension2d<s32>(
+                               skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
+                               skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y));
+#endif
+               }
+               video::SColor color(255, 255, 255, 255);
+               m_font->draw(spec.text.c_str(), rect, color, true, true, &rect);
+       }
+
+       /*
+               Draw fields/buttons tooltips
+       */
+       gui::IGUIElement *hovered =
+                       Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
+
+       if (hovered != NULL) {
+               s32 id = hovered->getID();
+
+               u64 delta = 0;
+               if (id == -1) {
+                       m_old_tooltip_id = id;
+               } else {
+                       if (id == m_old_tooltip_id) {
+                               delta = porting::getDeltaMs(m_hovered_time, porting::getTimeMs());
+                       } else {
+                               m_hovered_time = porting::getTimeMs();
+                               m_old_tooltip_id = id;
+                       }
+               }
+
+               // Find and update the current tooltip
+               if (id != -1 && delta >= m_tooltip_show_delay) {
+                       for (const FieldSpec &field : m_fields) {
+
+                               if (field.fid != id)
+                                       continue;
+
+                               const std::wstring &text = m_tooltips[field.fname].tooltip;
+                               if (!text.empty())
+                                       showTooltip(text, m_tooltips[field.fname].color,
+                                               m_tooltips[field.fname].bgcolor);
+
+                               break;
+                       }
+               }
+       }
+
+       m_tooltip_element->draw();
+
+       /*
+               Draw dragged item stack
+       */
+       drawSelectedItem();
+
+       skin->setFont(old_font);
+}
+
+
+void GUIFormSpecMenu::showTooltip(const std::wstring &text,
+       const irr::video::SColor &color, const irr::video::SColor &bgcolor)
+{
+       const std::wstring ntext = translate_string(text);
+       m_tooltip_element->setOverrideColor(color);
+       m_tooltip_element->setBackgroundColor(bgcolor);
+       setStaticText(m_tooltip_element, ntext.c_str());
+
+       // Tooltip size and offset
+       s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
+#if (IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2) || USE_FREETYPE == 1
+       std::vector<std::wstring> text_rows = str_split(ntext, L'\n');
+       s32 tooltip_height = m_tooltip_element->getTextHeight() * text_rows.size() + 5;
+#else
+       s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
+#endif
+       v2u32 screenSize = Environment->getVideoDriver()->getScreenSize();
+       int tooltip_offset_x = m_btn_height;
+       int tooltip_offset_y = m_btn_height;
+#ifdef __ANDROID__
+       tooltip_offset_x *= 3;
+       tooltip_offset_y  = 0;
+       if (m_pointer.X > (s32)screenSize.X / 2)
+               tooltip_offset_x = -(tooltip_offset_x + tooltip_width);
+#endif
+
+       // Calculate and set the tooltip position
+       s32 tooltip_x = m_pointer.X + tooltip_offset_x;
+       s32 tooltip_y = m_pointer.Y + tooltip_offset_y;
+       if (tooltip_x + tooltip_width > (s32)screenSize.X)
+               tooltip_x = (s32)screenSize.X - tooltip_width  - m_btn_height;
+       if (tooltip_y + tooltip_height > (s32)screenSize.Y)
+               tooltip_y = (s32)screenSize.Y - tooltip_height - m_btn_height;
+
+       m_tooltip_element->setRelativePosition(
+               core::rect<s32>(
+                       core::position2d<s32>(tooltip_x, tooltip_y),
+                       core::dimension2d<s32>(tooltip_width, tooltip_height)
+               )
+       );
+
+       // Display the tooltip
+       m_tooltip_element->setVisible(true);
+       bringToFront(m_tooltip_element);
+}
+
+void GUIFormSpecMenu::updateSelectedItem()
+{
+       // If the selected stack has become empty for some reason, deselect it.
+       // If the selected stack has become inaccessible, deselect it.
+       // If the selected stack has become smaller, adjust m_selected_amount.
+       ItemStack selected = verifySelectedItem();
+
+       // WARNING: BLACK MAGIC
+       // See if there is a stack suited for our current guess.
+       // If such stack does not exist, clear the guess.
+       if (!m_selected_content_guess.name.empty() &&
+                       selected.name == m_selected_content_guess.name &&
+                       selected.count == m_selected_content_guess.count){
+               // Selected item fits the guess. Skip the black magic.
+       } else if (!m_selected_content_guess.name.empty()) {
+               bool found = false;
+               for(u32 i=0; i<m_inventorylists.size() && !found; i++){
+                       const ListDrawSpec &s = m_inventorylists[i];
+                       Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
+                       if(!inv)
+                               continue;
+                       InventoryList *list = inv->getList(s.listname);
+                       if(!list)
+                               continue;
+                       for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
+                               u32 item_i = i + s.start_item_i;
+                               if(item_i >= list->getSize())
+                                       continue;
+                               ItemStack stack = list->getItem(item_i);
+                               if(stack.name == m_selected_content_guess.name &&
+                                               stack.count == m_selected_content_guess.count){
+                                       found = true;
+                                       infostream<<"Client: Changing selected content guess to "
+                                                       <<s.inventoryloc.dump()<<" "<<s.listname
+                                                       <<" "<<item_i<<std::endl;
+                                       delete m_selected_item;
+                                       m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
+                                       m_selected_amount = stack.count;
+                               }
+                       }
+               }
+               if(!found){
+                       infostream<<"Client: Discarding selected content guess: "
+                                       <<m_selected_content_guess.getItemString()<<std::endl;
+                       m_selected_content_guess.name = "";
+               }
+       }
+
+       // If craftresult is nonempty and nothing else is selected, select it now.
+       if(!m_selected_item)
+       {
+               for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) {
+                       if(s.listname == "craftpreview")
+                       {
+                               Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
+                               InventoryList *list = inv->getList("craftresult");
+                               if(list && list->getSize() >= 1 && !list->getItem(0).empty())
+                               {
+                                       m_selected_item = new ItemSpec;
+                                       m_selected_item->inventoryloc = s.inventoryloc;
+                                       m_selected_item->listname = "craftresult";
+                                       m_selected_item->i = 0;
+                                       m_selected_amount = 0;
+                                       m_selected_dragging = false;
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       // If craftresult is selected, keep the whole stack selected
+       if(m_selected_item && m_selected_item->listname == "craftresult")
+       {
+               m_selected_amount = verifySelectedItem().count;
+       }
+}
+
+ItemStack GUIFormSpecMenu::verifySelectedItem()
+{
+       // If the selected stack has become empty for some reason, deselect it.
+       // If the selected stack has become inaccessible, deselect it.
+       // If the selected stack has become smaller, adjust m_selected_amount.
+       // Return the selected stack.
+
+       if(m_selected_item)
+       {
+               if(m_selected_item->isValid())
+               {
+                       Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
+                       if(inv)
+                       {
+                               InventoryList *list = inv->getList(m_selected_item->listname);
+                               if(list && (u32) m_selected_item->i < list->getSize())
+                               {
+                                       ItemStack stack = list->getItem(m_selected_item->i);
+                                       if(m_selected_amount > stack.count)
+                                               m_selected_amount = stack.count;
+                                       if(!stack.empty())
+                                               return stack;
+                               }
+                       }
+               }
+
+               // selection was not valid
+               delete m_selected_item;
+               m_selected_item = NULL;
+               m_selected_amount = 0;
+               m_selected_dragging = false;
+       }
+       return ItemStack();
+}
+
+void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
+{
+       if(m_text_dst)
+       {
+               StringMap fields;
+
+               if (quitmode == quit_mode_accept) {
+                       fields["quit"] = "true";
+               }
+
+               if (quitmode == quit_mode_cancel) {
+                       fields["quit"] = "true";
+                       m_text_dst->gotText(fields);
+                       return;
+               }
+
+               if (current_keys_pending.key_down) {
+                       fields["key_down"] = "true";
+                       current_keys_pending.key_down = false;
+               }
+
+               if (current_keys_pending.key_up) {
+                       fields["key_up"] = "true";
+                       current_keys_pending.key_up = false;
+               }
+
+               if (current_keys_pending.key_enter) {
+                       fields["key_enter"] = "true";
+                       current_keys_pending.key_enter = false;
+               }
+
+               if (!current_field_enter_pending.empty()) {
+                       fields["key_enter_field"] = current_field_enter_pending;
+                       current_field_enter_pending = "";
+               }
+
+               if (current_keys_pending.key_escape) {
+                       fields["key_escape"] = "true";
+                       current_keys_pending.key_escape = false;
+               }
+
+               for (const GUIFormSpecMenu::FieldSpec &s : m_fields) {
+                       if(s.send) {
+                               std::string name = s.fname;
+                               if (s.ftype == f_Button) {
+                                       fields[name] = wide_to_utf8(s.flabel);
+                               } else if (s.ftype == f_Table) {
+                                       GUITable *table = getTable(s.fname);
+                                       if (table) {
+                                               fields[name] = table->checkEvent();
+                                       }
+                               }
+                               else if(s.ftype == f_DropDown) {
+                                       // no dynamic cast possible due to some distributions shipped
+                                       // without rtti support in irrlicht
+                                       IGUIElement * element = getElementFromId(s.fid);
+                                       gui::IGUIComboBox *e = NULL;
+                                       if ((element) && (element->getType() == gui::EGUIET_COMBO_BOX)) {
+                                               e = static_cast<gui::IGUIComboBox*>(element);
+                                       }
+                                       s32 selected = e->getSelected();
+                                       if (selected >= 0) {
+                                               std::vector<std::string> *dropdown_values =
+                                                       getDropDownValues(s.fname);
+                                               if (dropdown_values && selected < (s32)dropdown_values->size()) {
+                                                       fields[name] = (*dropdown_values)[selected];
+                                               }
+                                       }
+                               }
+                               else if (s.ftype == f_TabHeader) {
+                                       // no dynamic cast possible due to some distributions shipped
+                                       // without rttzi support in irrlicht
+                                       IGUIElement * element = getElementFromId(s.fid);
+                                       gui::IGUITabControl *e = NULL;
+                                       if ((element) && (element->getType() == gui::EGUIET_TAB_CONTROL)) {
+                                               e = static_cast<gui::IGUITabControl *>(element);
+                                       }
+
+                                       if (e != 0) {
+                                               std::stringstream ss;
+                                               ss << (e->getActiveTab() +1);
+                                               fields[name] = ss.str();
+                                       }
+                               }
+                               else if (s.ftype == f_CheckBox) {
+                                       // no dynamic cast possible due to some distributions shipped
+                                       // without rtti support in irrlicht
+                                       IGUIElement * element = getElementFromId(s.fid);
+                                       gui::IGUICheckBox *e = NULL;
+                                       if ((element) && (element->getType() == gui::EGUIET_CHECK_BOX)) {
+                                               e = static_cast<gui::IGUICheckBox*>(element);
+                                       }
+
+                                       if (e != 0) {
+                                               if (e->isChecked())
+                                                       fields[name] = "true";
+                                               else
+                                                       fields[name] = "false";
+                                       }
+                               }
+                               else if (s.ftype == f_ScrollBar) {
+                                       // no dynamic cast possible due to some distributions shipped
+                                       // without rtti support in irrlicht
+                                       IGUIElement * element = getElementFromId(s.fid);
+                                       gui::IGUIScrollBar *e = NULL;
+                                       if ((element) && (element->getType() == gui::EGUIET_SCROLL_BAR)) {
+                                               e = static_cast<gui::IGUIScrollBar*>(element);
+                                       }
+
+                                       if (e != 0) {
+                                               std::stringstream os;
+                                               os << e->getPos();
+                                               if (s.fdefault == L"Changed")
+                                                       fields[name] = "CHG:" + os.str();
+                                               else
+                                                       fields[name] = "VAL:" + os.str();
+                                       }
+                               }
+                               else
+                               {
+                                       IGUIElement* e = getElementFromId(s.fid);
+                                       if(e != NULL) {
+                                               fields[name] = wide_to_utf8(e->getText());
+                                       }
+                               }
+                       }
+               }
+
+               m_text_dst->gotText(fields);
+       }
+}
+
+static bool isChild(gui::IGUIElement * tocheck, gui::IGUIElement * parent)
+{
+       while(tocheck != NULL) {
+               if (tocheck == parent) {
+                       return true;
+               }
+               tocheck = tocheck->getParent();
+       }
+       return false;
+}
+
+bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
+{
+       // The IGUITabControl renders visually using the skin's selected
+       // font, which we override for the duration of form drawing,
+       // but computes tab hotspots based on how it would have rendered
+       // using the font that is selected at the time of button release.
+       // To make these two consistent, temporarily override the skin's
+       // font while the IGUITabControl is processing the event.
+       if (event.EventType == EET_MOUSE_INPUT_EVENT &&
+                       event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
+               s32 x = event.MouseInput.X;
+               s32 y = event.MouseInput.Y;
+               gui::IGUIElement *hovered =
+                       Environment->getRootGUIElement()->getElementFromPoint(
+                               core::position2d<s32>(x, y));
+               if (hovered && isMyChild(hovered) &&
+                               hovered->getType() == gui::EGUIET_TAB_CONTROL) {
+                       gui::IGUISkin* skin = Environment->getSkin();
+                       sanity_check(skin != NULL);
+                       gui::IGUIFont *old_font = skin->getFont();
+                       skin->setFont(m_font);
+                       bool retval = hovered->OnEvent(event);
+                       skin->setFont(old_font);
+                       return retval;
+               }
+       }
+
+       // Fix Esc/Return key being eaten by checkboxen and tables
+       if(event.EventType==EET_KEY_INPUT_EVENT) {
+               KeyPress kp(event.KeyInput);
+               if (kp == EscapeKey || kp == CancelKey
+                               || kp == getKeySetting("keymap_inventory")
+                               || event.KeyInput.Key==KEY_RETURN) {
+                       gui::IGUIElement *focused = Environment->getFocus();
+                       if (focused && isMyChild(focused) &&
+                                       (focused->getType() == gui::EGUIET_LIST_BOX ||
+                                        focused->getType() == gui::EGUIET_CHECK_BOX)) {
+                               OnEvent(event);
+                               return true;
+                       }
+               }
+       }
+       // Mouse wheel events: send to hovered element instead of focused
+       if(event.EventType==EET_MOUSE_INPUT_EVENT
+                       && event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
+               s32 x = event.MouseInput.X;
+               s32 y = event.MouseInput.Y;
+               gui::IGUIElement *hovered =
+                       Environment->getRootGUIElement()->getElementFromPoint(
+                               core::position2d<s32>(x, y));
+               if (hovered && isMyChild(hovered)) {
+                       hovered->OnEvent(event);
+                       return true;
+               }
+       }
+
+       if (event.EventType == EET_MOUSE_INPUT_EVENT) {
+               s32 x = event.MouseInput.X;
+               s32 y = event.MouseInput.Y;
+               gui::IGUIElement *hovered =
+                       Environment->getRootGUIElement()->getElementFromPoint(
+                               core::position2d<s32>(x, y));
+               if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
+                       m_old_tooltip_id = -1;
+               }
+               if (!isChild(hovered,this)) {
+                       if (DoubleClickDetection(event)) {
+                               return true;
+                       }
+               }
+       }
+
+       #ifdef __ANDROID__
+       // display software keyboard when clicking edit boxes
+       if (event.EventType == EET_MOUSE_INPUT_EVENT
+                       && event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
+               gui::IGUIElement *hovered =
+                       Environment->getRootGUIElement()->getElementFromPoint(
+                               core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
+               if ((hovered) && (hovered->getType() == irr::gui::EGUIET_EDIT_BOX)) {
+                       bool retval = hovered->OnEvent(event);
+                       if (retval) {
+                               Environment->setFocus(hovered);
+                       }
+                       m_JavaDialogFieldName = getNameByID(hovered->getID());
+                       std::string message   = gettext("Enter ");
+                       std::string label     = wide_to_utf8(getLabelByID(hovered->getID()));
+                       if (label == "") {
+                               label = "text";
+                       }
+                       message += gettext(label) + ":";
+
+                       /* single line text input */
+                       int type = 2;
+
+                       /* multi line text input */
+                       if (((gui::IGUIEditBox*) hovered)->isMultiLineEnabled()) {
+                               type = 1;
+                       }
+
+                       /* passwords are always single line */
+                       if (((gui::IGUIEditBox*) hovered)->isPasswordBox()) {
+                               type = 3;
+                       }
+
+                       porting::showInputDialog(gettext("ok"), "",
+                                       wide_to_utf8(((gui::IGUIEditBox*) hovered)->getText()),
+                                       type);
+                       return retval;
+               }
+       }
+
+       if (event.EventType == EET_TOUCH_INPUT_EVENT)
+       {
+               SEvent translated;
+               memset(&translated, 0, sizeof(SEvent));
+               translated.EventType   = EET_MOUSE_INPUT_EVENT;
+               gui::IGUIElement* root = Environment->getRootGUIElement();
+
+               if (!root) {
+                       errorstream
+                       << "GUIFormSpecMenu::preprocessEvent unable to get root element"
+                       << std::endl;
+                       return false;
+               }
+               gui::IGUIElement* hovered = root->getElementFromPoint(
+                       core::position2d<s32>(
+                                       event.TouchInput.X,
+                                       event.TouchInput.Y));
+
+               translated.MouseInput.X = event.TouchInput.X;
+               translated.MouseInput.Y = event.TouchInput.Y;
+               translated.MouseInput.Control = false;
+
+               bool dont_send_event = false;
+
+               if (event.TouchInput.touchedCount == 1) {
+                       switch (event.TouchInput.Event) {
+                               case ETIE_PRESSED_DOWN:
+                                       m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
+                                       translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
+                                       translated.MouseInput.ButtonStates = EMBSM_LEFT;
+                                       m_down_pos = m_pointer;
+                                       break;
+                               case ETIE_MOVED:
+                                       m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
+                                       translated.MouseInput.Event = EMIE_MOUSE_MOVED;
+                                       translated.MouseInput.ButtonStates = EMBSM_LEFT;
+                                       break;
+                               case ETIE_LEFT_UP:
+                                       translated.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
+                                       translated.MouseInput.ButtonStates = 0;
+                                       hovered = root->getElementFromPoint(m_down_pos);
+                                       /* we don't have a valid pointer element use last
+                                        * known pointer pos */
+                                       translated.MouseInput.X = m_pointer.X;
+                                       translated.MouseInput.Y = m_pointer.Y;
+
+                                       /* reset down pos */
+                                       m_down_pos = v2s32(0,0);
+                                       break;
+                               default:
+                                       dont_send_event = true;
+                                       //this is not supposed to happen
+                                       errorstream
+                                       << "GUIFormSpecMenu::preprocessEvent unexpected usecase Event="
+                                       << event.TouchInput.Event << std::endl;
+                       }
+               } else if ( (event.TouchInput.touchedCount == 2) &&
+                               (event.TouchInput.Event == ETIE_PRESSED_DOWN) ) {
+                       hovered = root->getElementFromPoint(m_down_pos);
+
+                       translated.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
+                       translated.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT;
+                       translated.MouseInput.X = m_pointer.X;
+                       translated.MouseInput.Y = m_pointer.Y;
+
+                       if (hovered) {
+                               hovered->OnEvent(translated);
+                       }
+
+                       translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
+                       translated.MouseInput.ButtonStates = EMBSM_LEFT;
+
+
+                       if (hovered) {
+                               hovered->OnEvent(translated);
+                       }
+                       dont_send_event = true;
+               }
+               /* ignore unhandled 2 touch events ... accidental moving for example */
+               else if (event.TouchInput.touchedCount == 2) {
+                       dont_send_event = true;
+               }
+               else if (event.TouchInput.touchedCount > 2) {
+                       errorstream
+                       << "GUIFormSpecMenu::preprocessEvent to many multitouch events "
+                       << event.TouchInput.touchedCount << " ignoring them" << std::endl;
+               }
+
+               if (dont_send_event) {
+                       return true;
+               }
+
+               /* check if translated event needs to be preprocessed again */
+               if (preprocessEvent(translated)) {
+                       return true;
+               }
+               if (hovered) {
+                       grab();
+                       bool retval = hovered->OnEvent(translated);
+
+                       if (event.TouchInput.Event == ETIE_LEFT_UP) {
+                               /* reset pointer */
+                               m_pointer = v2s32(0,0);
+                       }
+                       drop();
+                       return retval;
+               }
+       }
+       #endif
+
+       if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
+               /* TODO add a check like:
+               if (event.JoystickEvent != joystick_we_listen_for)
+                       return false;
+               */
+               bool handled = m_joystick->handleEvent(event.JoystickEvent);
+               if (handled) {
+                       if (m_joystick->wasKeyDown(KeyType::ESC)) {
+                               tryClose();
+                       } else if (m_joystick->wasKeyDown(KeyType::JUMP)) {
+                               if (m_allowclose) {
+                                       acceptInput(quit_mode_accept);
+                                       quitMenu();
+                               }
+                       }
+               }
+               return handled;
+       }
+
+       return false;
+}
+
+/******************************************************************************/
+bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event)
+{
+       /* The following code is for capturing double-clicks of the mouse button
+        * and translating the double-click into an EET_KEY_INPUT_EVENT event
+        * -- which closes the form -- under some circumstances.
+        *
+        * There have been many github issues reporting this as a bug even though it
+        * was an intended feature.  For this reason, remapping the double-click as
+        * an ESC must be explicitly set when creating this class via the
+        * /p remap_dbl_click parameter of the constructor.
+        */
+
+       if (!m_remap_dbl_click)
+               return false;
+
+       if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
+               m_doubleclickdetect[0].pos  = m_doubleclickdetect[1].pos;
+               m_doubleclickdetect[0].time = m_doubleclickdetect[1].time;
+
+               m_doubleclickdetect[1].pos  = m_pointer;
+               m_doubleclickdetect[1].time = porting::getTimeMs();
+       }
+       else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
+               u64 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, porting::getTimeMs());
+               if (delta > 400) {
+                       return false;
+               }
+
+               double squaredistance =
+                               m_doubleclickdetect[0].pos
+                               .getDistanceFromSQ(m_doubleclickdetect[1].pos);
+
+               if (squaredistance > (30*30)) {
+                       return false;
+               }
+
+               SEvent* translated = new SEvent();
+               assert(translated != 0);
+               //translate doubleclick to escape
+               memset(translated, 0, sizeof(SEvent));
+               translated->EventType = irr::EET_KEY_INPUT_EVENT;
+               translated->KeyInput.Key         = KEY_ESCAPE;
+               translated->KeyInput.Control     = false;
+               translated->KeyInput.Shift       = false;
+               translated->KeyInput.PressedDown = true;
+               translated->KeyInput.Char        = 0;
+               OnEvent(*translated);
+
+               // no need to send the key up event as we're already deleted
+               // and no one else did notice this event
+               delete translated;
+               return true;
+       }
+
+       return false;
+}
+
+void GUIFormSpecMenu::tryClose()
+{
+       if (m_allowclose) {
+               doPause = false;
+               acceptInput(quit_mode_cancel);
+               quitMenu();
+       } else {
+               m_text_dst->gotText(L"MenuQuit");
+       }
+}
+
+bool GUIFormSpecMenu::OnEvent(const SEvent& event)
+{
+       if (event.EventType==EET_KEY_INPUT_EVENT) {
+               KeyPress kp(event.KeyInput);
+               if (event.KeyInput.PressedDown && (
+                               (kp == EscapeKey) || (kp == CancelKey) ||
+                               ((m_client != NULL) && (kp == getKeySetting("keymap_inventory"))))) {
+                       tryClose();
+                       return true;
+               }
+
+               if (m_client != NULL && event.KeyInput.PressedDown &&
+                               (kp == getKeySetting("keymap_screenshot"))) {
+                       m_client->makeScreenshot();
+               }
+               if (event.KeyInput.PressedDown &&
+                       (event.KeyInput.Key==KEY_RETURN ||
+                        event.KeyInput.Key==KEY_UP ||
+                        event.KeyInput.Key==KEY_DOWN)
+                       ) {
+                       switch (event.KeyInput.Key) {
+                               case KEY_RETURN:
+                                       current_keys_pending.key_enter = true;
+                                       break;
+                               case KEY_UP:
+                                       current_keys_pending.key_up = true;
+                                       break;
+                               case KEY_DOWN:
+                                       current_keys_pending.key_down = true;
+                                       break;
+                               break;
+                               default:
+                                       //can't happen at all!
+                                       FATAL_ERROR("Reached a source line that can't ever been reached");
+                                       break;
+                       }
+                       if (current_keys_pending.key_enter && m_allowclose) {
+                               acceptInput(quit_mode_accept);
+                               quitMenu();
+                       } else {
+                               acceptInput();
+                       }
+                       return true;
+               }
+
+       }
+
+       /* Mouse event other than movement, or crossing the border of inventory
+         field while holding right mouse button
+        */
+       if (event.EventType == EET_MOUSE_INPUT_EVENT &&
+                       (event.MouseInput.Event != EMIE_MOUSE_MOVED ||
+                        (event.MouseInput.Event == EMIE_MOUSE_MOVED &&
+                         event.MouseInput.isRightPressed() &&
+                         getItemAtPos(m_pointer).i != getItemAtPos(m_old_pointer).i))) {
+
+               // Get selected item and hovered/clicked item (s)
+
+               m_old_tooltip_id = -1;
+               updateSelectedItem();
+               ItemSpec s = getItemAtPos(m_pointer);
+
+               Inventory *inv_selected = NULL;
+               Inventory *inv_s = NULL;
+               InventoryList *list_s = NULL;
+
+               if (m_selected_item) {
+                       inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
+                       sanity_check(inv_selected);
+                       sanity_check(inv_selected->getList(m_selected_item->listname) != NULL);
+               }
+
+               u32 s_count = 0;
+
+               if (s.isValid())
+               do { // breakable
+                       inv_s = m_invmgr->getInventory(s.inventoryloc);
+
+                       if (!inv_s) {
+                               errorstream << "InventoryMenu: The selected inventory location "
+                                               << "\"" << s.inventoryloc.dump() << "\" doesn't exist"
+                                               << std::endl;
+                               s.i = -1;  // make it invalid again
+                               break;
+                       }
+
+                       list_s = inv_s->getList(s.listname);
+                       if (list_s == NULL) {
+                               verbosestream << "InventoryMenu: The selected inventory list \""
+                                               << s.listname << "\" does not exist" << std::endl;
+                               s.i = -1;  // make it invalid again
+                               break;
+                       }
+
+                       if ((u32)s.i >= list_s->getSize()) {
+                               infostream << "InventoryMenu: The selected inventory list \""
+                                               << s.listname << "\" is too small (i=" << s.i << ", size="
+                                               << list_s->getSize() << ")" << std::endl;
+                               s.i = -1;  // make it invalid again
+                               break;
+                       }
+
+                       s_count = list_s->getItem(s.i).count;
+               } while(0);
+
+               bool identical = (m_selected_item != NULL) && s.isValid() &&
+                       (inv_selected == inv_s) &&
+                       (m_selected_item->listname == s.listname) &&
+                       (m_selected_item->i == s.i);
+
+               // buttons: 0 = left, 1 = right, 2 = middle
+               // up/down: 0 = down (press), 1 = up (release), 2 = unknown event, -1 movement
+               int button = 0;
+               int updown = 2;
+               if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
+                       { button = 0; updown = 0; }
+               else if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
+                       { button = 1; updown = 0; }
+               else if (event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
+                       { button = 2; updown = 0; }
+               else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
+                       { button = 0; updown = 1; }
+               else if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
+                       { button = 1; updown = 1; }
+               else if (event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
+                       { button = 2; updown = 1; }
+               else if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
+                       { updown = -1;}
+
+               // Set this number to a positive value to generate a move action
+               // from m_selected_item to s.
+               u32 move_amount = 0;
+
+               // Set this number to a positive value to generate a move action
+               // from s to the next inventory ring.
+               u32 shift_move_amount = 0;
+
+               // Set this number to a positive value to generate a drop action
+               // from m_selected_item.
+               u32 drop_amount = 0;
+
+               // Set this number to a positive value to generate a craft action at s.
+               u32 craft_amount = 0;
+
+               if (updown == 0) {
+                       // Some mouse button has been pressed
+
+                       //infostream<<"Mouse button "<<button<<" pressed at p=("
+                       //      <<p.X<<","<<p.Y<<")"<<std::endl;
+
+                       m_selected_dragging = false;
+
+                       if (s.isValid() && s.listname == "craftpreview") {
+                               // Craft preview has been clicked: craft
+                               craft_amount = (button == 2 ? 10 : 1);
+                       } else if (m_selected_item == NULL) {
+                               if (s_count != 0) {
+                                       // Non-empty stack has been clicked: select or shift-move it
+                                       m_selected_item = new ItemSpec(s);
+
+                                       u32 count;
+                                       if (button == 1)  // right
+                                               count = (s_count + 1) / 2;
+                                       else if (button == 2)  // middle
+                                               count = MYMIN(s_count, 10);
+                                       else  // left
+                                               count = s_count;
+
+                                       if (!event.MouseInput.Shift) {
+                                               // no shift: select item
+                                               m_selected_amount = count;
+                                               m_selected_dragging = true;
+                                               m_auto_place = false;
+                                       } else {
+                                               // shift pressed: move item
+                                               if (button != 1)
+                                                       shift_move_amount = count;
+                                               else // count of 1 at left click like after drag & drop
+                                                       shift_move_amount = 1;
+                                       }
+                               }
+                       } else { // m_selected_item != NULL
+                               assert(m_selected_amount >= 1);
+
+                               if (s.isValid()) {
+                                       // Clicked a slot: move
+                                       if (button == 1)  // right
+                                               move_amount = 1;
+                                       else if (button == 2)  // middle
+                                               move_amount = MYMIN(m_selected_amount, 10);
+                                       else  // left
+                                               move_amount = m_selected_amount;
+
+                                       if (identical) {
+                                               if (move_amount >= m_selected_amount)
+                                                       m_selected_amount = 0;
+                                               else
+                                                       m_selected_amount -= move_amount;
+                                               move_amount = 0;
+                                       }
+                               }
+                               else if (!getAbsoluteClippingRect().isPointInside(m_pointer)) {
+                                       // Clicked outside of the window: drop
+                                       if (button == 1)  // right
+                                               drop_amount = 1;
+                                       else if (button == 2)  // middle
+                                               drop_amount = MYMIN(m_selected_amount, 10);
+                                       else  // left
+                                               drop_amount = m_selected_amount;
+                               }
+                       }
+               }
+               else if (updown == 1) {
+                       // Some mouse button has been released
+
+                       //infostream<<"Mouse button "<<button<<" released at p=("
+                       //      <<p.X<<","<<p.Y<<")"<<std::endl;
+
+                       if (m_selected_item != NULL && m_selected_dragging && s.isValid()) {
+                               if (!identical) {
+                                       // Dragged to different slot: move all selected
+                                       move_amount = m_selected_amount;
+                               }
+                       } else if (m_selected_item != NULL && m_selected_dragging &&
+                                       !(getAbsoluteClippingRect().isPointInside(m_pointer))) {
+                               // Dragged outside of window: drop all selected
+                               drop_amount = m_selected_amount;
+                       }
+
+                       m_selected_dragging = false;
+                       // Keep track of whether the mouse button be released
+                       // One click is drag without dropping. Click + release
+                       // + click changes to drop item when moved mode
+                       if (m_selected_item)
+                               m_auto_place = true;
+               } else if (updown == -1) {
+                       // Mouse has been moved and rmb is down and mouse pointer just
+                       // entered a new inventory field (checked in the entry-if, this
+                       // is the only action here that is generated by mouse movement)
+                       if (m_selected_item != NULL && s.isValid()) {
+                               // Move 1 item
+                               // TODO: middle mouse to move 10 items might be handy
+                               if (m_auto_place) {
+                                       // Only move an item if the destination slot is empty
+                                       // or contains the same item type as what is going to be
+                                       // moved
+                                       InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
+                                       InventoryList *list_to = list_s;
+                                       assert(list_from && list_to);
+                                       ItemStack stack_from = list_from->getItem(m_selected_item->i);
+                                       ItemStack stack_to = list_to->getItem(s.i);
+                                       if (stack_to.empty() || stack_to.name == stack_from.name)
+                                               move_amount = 1;
+                               }
+                       }
+               }
+
+               // Possibly send inventory action to server
+               if (move_amount > 0) {
+                       // Send IAction::Move
+
+                       assert(m_selected_item && m_selected_item->isValid());
+                       assert(s.isValid());
+
+                       assert(inv_selected && inv_s);
+                       InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
+                       InventoryList *list_to = list_s;
+                       assert(list_from && list_to);
+                       ItemStack stack_from = list_from->getItem(m_selected_item->i);
+                       ItemStack stack_to = list_to->getItem(s.i);
+
+                       // Check how many items can be moved
+                       move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
+                       ItemStack leftover = stack_to.addItem(stack_from, m_client->idef());
+                       // If source stack cannot be added to destination stack at all,
+                       // they are swapped
+                       if ((leftover.count == stack_from.count) &&
+                                       (leftover.name == stack_from.name)) {
+                               m_selected_amount = stack_to.count;
+                               // In case the server doesn't directly swap them but instead
+                               // moves stack_to somewhere else, set this
+                               m_selected_content_guess = stack_to;
+                               m_selected_content_guess_inventory = s.inventoryloc;
+                       }
+                       // Source stack goes fully into destination stack
+                       else if (leftover.empty()) {
+                               m_selected_amount -= move_amount;
+                               m_selected_content_guess = ItemStack(); // Clear
+                       }
+                       // Source stack goes partly into destination stack
+                       else {
+                               move_amount -= leftover.count;
+                               m_selected_amount -= move_amount;
+                               m_selected_content_guess = ItemStack(); // Clear
+                       }
+
+                       infostream << "Handing IAction::Move to manager" << std::endl;
+                       IMoveAction *a = new IMoveAction();
+                       a->count = move_amount;
+                       a->from_inv = m_selected_item->inventoryloc;
+                       a->from_list = m_selected_item->listname;
+                       a->from_i = m_selected_item->i;
+                       a->to_inv = s.inventoryloc;
+                       a->to_list = s.listname;
+                       a->to_i = s.i;
+                       m_invmgr->inventoryAction(a);
+               } else if (shift_move_amount > 0) {
+                       u32 mis = m_inventory_rings.size();
+                       u32 i = 0;
+                       for (; i < mis; i++) {
+                               const ListRingSpec &sp = m_inventory_rings[i];
+                               if (sp.inventoryloc == s.inventoryloc
+                                               && sp.listname == s.listname)
+                                       break;
+                       }
+                       do {
+                               if (i >= mis) // if not found
+                                       break;
+                               u32 to_inv_ind = (i + 1) % mis;
+                               const ListRingSpec &to_inv_sp = m_inventory_rings[to_inv_ind];
+                               InventoryList *list_from = list_s;
+                               if (!s.isValid())
+                                       break;
+                               Inventory *inv_to = m_invmgr->getInventory(to_inv_sp.inventoryloc);
+                               if (!inv_to)
+                                       break;
+                               InventoryList *list_to = inv_to->getList(to_inv_sp.listname);
+                               if (!list_to)
+                                       break;
+                               ItemStack stack_from = list_from->getItem(s.i);
+                               assert(shift_move_amount <= stack_from.count);
+                               if (m_client->getProtoVersion() >= 25) {
+                                       infostream << "Handing IAction::Move to manager" << std::endl;
+                                       IMoveAction *a = new IMoveAction();
+                                       a->count = shift_move_amount;
+                                       a->from_inv = s.inventoryloc;
+                                       a->from_list = s.listname;
+                                       a->from_i = s.i;
+                                       a->to_inv = to_inv_sp.inventoryloc;
+                                       a->to_list = to_inv_sp.listname;
+                                       a->move_somewhere = true;
+                                       m_invmgr->inventoryAction(a);
+                               } else {
+                                       // find a place (or more than one) to add the new item
+                                       u32 ilt_size = list_to->getSize();
+                                       ItemStack leftover;
+                                       for (u32 slot_to = 0; slot_to < ilt_size
+                                                       && shift_move_amount > 0; slot_to++) {
+                                               list_to->itemFits(slot_to, stack_from, &leftover);
+                                               if (leftover.count < stack_from.count) {
+                                                       infostream << "Handing IAction::Move to manager" << std::endl;
+                                                       IMoveAction *a = new IMoveAction();
+                                                       a->count = MYMIN(shift_move_amount,
+                                                               (u32) (stack_from.count - leftover.count));
+                                                       shift_move_amount -= a->count;
+                                                       a->from_inv = s.inventoryloc;
+                                                       a->from_list = s.listname;
+                                                       a->from_i = s.i;
+                                                       a->to_inv = to_inv_sp.inventoryloc;
+                                                       a->to_list = to_inv_sp.listname;
+                                                       a->to_i = slot_to;
+                                                       m_invmgr->inventoryAction(a);
+                                                       stack_from = leftover;
+                                               }
+                                       }
+                               }
+                       } while (0);
+               } else if (drop_amount > 0) {
+                       m_selected_content_guess = ItemStack(); // Clear
+
+                       // Send IAction::Drop
+
+                       assert(m_selected_item && m_selected_item->isValid());
+                       assert(inv_selected);
+                       InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
+                       assert(list_from);
+                       ItemStack stack_from = list_from->getItem(m_selected_item->i);
+
+                       // Check how many items can be dropped
+                       drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
+                       assert(drop_amount > 0 && drop_amount <= m_selected_amount);
+                       m_selected_amount -= drop_amount;
+
+                       infostream << "Handing IAction::Drop to manager" << std::endl;
+                       IDropAction *a = new IDropAction();
+                       a->count = drop_amount;
+                       a->from_inv = m_selected_item->inventoryloc;
+                       a->from_list = m_selected_item->listname;
+                       a->from_i = m_selected_item->i;
+                       m_invmgr->inventoryAction(a);
+               } else if (craft_amount > 0) {
+                       m_selected_content_guess = ItemStack(); // Clear
+
+                       // Send IAction::Craft
+
+                       assert(s.isValid());
+                       assert(inv_s);
+
+                       infostream << "Handing IAction::Craft to manager" << std::endl;
+                       ICraftAction *a = new ICraftAction();
+                       a->count = craft_amount;
+                       a->craft_inv = s.inventoryloc;
+                       m_invmgr->inventoryAction(a);
+               }
+
+               // If m_selected_amount has been decreased to zero, deselect
+               if (m_selected_amount == 0) {
+                       delete m_selected_item;
+                       m_selected_item = NULL;
+                       m_selected_amount = 0;
+                       m_selected_dragging = false;
+                       m_selected_content_guess = ItemStack();
+               }
+               m_old_pointer = m_pointer;
+       }
+       if (event.EventType == EET_GUI_EVENT) {
+
+               if (event.GUIEvent.EventType == gui::EGET_TAB_CHANGED
+                               && isVisible()) {
+                       // find the element that was clicked
+                       for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
+                               if ((s.ftype == f_TabHeader) &&
+                                               (s.fid == event.GUIEvent.Caller->getID())) {
+                                       s.send = true;
+                                       acceptInput();
+                                       s.send = false;
+                                       return true;
+                               }
+                       }
+               }
+               if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
+                               && isVisible()) {
+                       if (!canTakeFocus(event.GUIEvent.Element)) {
+                               infostream<<"GUIFormSpecMenu: Not allowing focus change."
+                                               <<std::endl;
+                               // Returning true disables focus change
+                               return true;
+                       }
+               }
+               if ((event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) ||
+                               (event.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED) ||
+                               (event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED) ||
+                               (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED)) {
+                       unsigned int btn_id = event.GUIEvent.Caller->getID();
+
+                       if (btn_id == 257) {
+                               if (m_allowclose) {
+                                       acceptInput(quit_mode_accept);
+                                       quitMenu();
+                               } else {
+                                       acceptInput();
+                                       m_text_dst->gotText(L"ExitButton");
+                               }
+                               // quitMenu deallocates menu
+                               return true;
+                       }
+
+                       // find the element that was clicked
+                       for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
+                               // if its a button, set the send field so
+                               // lua knows which button was pressed
+                               if ((s.ftype == f_Button || s.ftype == f_CheckBox) &&
+                                               s.fid == event.GUIEvent.Caller->getID()) {
+                                       s.send = true;
+                                       if (s.is_exit) {
+                                               if (m_allowclose) {
+                                                       acceptInput(quit_mode_accept);
+                                                       quitMenu();
+                                               } else {
+                                                       m_text_dst->gotText(L"ExitButton");
+                                               }
+                                               return true;
+                                       }
+
+                                       acceptInput(quit_mode_no);
+                                       s.send = false;
+                                       return true;
+
+                               } else if ((s.ftype == f_DropDown) &&
+                                               (s.fid == event.GUIEvent.Caller->getID())) {
+                                       // only send the changed dropdown
+                                       for (GUIFormSpecMenu::FieldSpec &s2 : m_fields) {
+                                               if (s2.ftype == f_DropDown) {
+                                                       s2.send = false;
+                                               }
+                                       }
+                                       s.send = true;
+                                       acceptInput(quit_mode_no);
+
+                                       // revert configuration to make sure dropdowns are sent on
+                                       // regular button click
+                                       for (GUIFormSpecMenu::FieldSpec &s2 : m_fields) {
+                                               if (s2.ftype == f_DropDown) {
+                                                       s2.send = true;
+                                               }
+                                       }
+                                       return true;
+                               } else if ((s.ftype == f_ScrollBar) &&
+                                               (s.fid == event.GUIEvent.Caller->getID())) {
+                                       s.fdefault = L"Changed";
+                                       acceptInput(quit_mode_no);
+                                       s.fdefault = L"";
+                               }
+                       }
+               }
+
+               if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
+                       if (event.GUIEvent.Caller->getID() > 257) {
+                               bool close_on_enter = true;
+                               for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
+                                       if (s.ftype == f_Unknown &&
+                                                       s.fid == event.GUIEvent.Caller->getID()) {
+                                               current_field_enter_pending = s.fname;
+                                               std::unordered_map<std::string, bool>::const_iterator it =
+                                                       field_close_on_enter.find(s.fname);
+                                               if (it != field_close_on_enter.end())
+                                                       close_on_enter = (*it).second;
+
+                                               break;
+                                       }
+                               }
+
+                               if (m_allowclose && close_on_enter) {
+                                       current_keys_pending.key_enter = true;
+                                       acceptInput(quit_mode_accept);
+                                       quitMenu();
+                               } else {
+                                       current_keys_pending.key_enter = true;
+                                       acceptInput();
+                               }
+                               // quitMenu deallocates menu
+                               return true;
+                       }
+               }
+
+               if (event.GUIEvent.EventType == gui::EGET_TABLE_CHANGED) {
+                       int current_id = event.GUIEvent.Caller->getID();
+                       if (current_id > 257) {
+                               // find the element that was clicked
+                               for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
+                                       // if it's a table, set the send field
+                                       // so lua knows which table was changed
+                                       if ((s.ftype == f_Table) && (s.fid == current_id)) {
+                                               s.send = true;
+                                               acceptInput();
+                                               s.send=false;
+                                       }
+                               }
+                               return true;
+                       }
+               }
+       }
+
+       return Parent ? Parent->OnEvent(event) : false;
+}
+
+/**
+ * get name of element by element id
+ * @param id of element
+ * @return name string or empty string
+ */
+std::string GUIFormSpecMenu::getNameByID(s32 id)
+{
+       for (FieldSpec &spec : m_fields) {
+               if (spec.fid == id) {
+                       return spec.fname;
+               }
+       }
+       return "";
+}
+
+/**
+ * get label of element by id
+ * @param id of element
+ * @return label string or empty string
+ */
+std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
+{
+       for (FieldSpec &spec : m_fields) {
+               if (spec.fid == id) {
+                       return spec.flabel;
+               }
+       }
+       return L"";
+}
diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h
new file mode 100644 (file)
index 0000000..071efb3
--- /dev/null
@@ -0,0 +1,565 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include <utility>
+#include <stack>
+
+#include "irrlichttypes_extrabloated.h"
+#include "inventorymanager.h"
+#include "modalMenu.h"
+#include "guiTable.h"
+#include "network/networkprotocol.h"
+#include "client/joystick_controller.h"
+#include "util/string.h"
+#include "util/enriched_string.h"
+
+class InventoryManager;
+class ISimpleTextureSource;
+class Client;
+
+typedef enum {
+       f_Button,
+       f_Table,
+       f_TabHeader,
+       f_CheckBox,
+       f_DropDown,
+       f_ScrollBar,
+       f_Unknown
+} FormspecFieldType;
+
+typedef enum {
+       quit_mode_no,
+       quit_mode_accept,
+       quit_mode_cancel
+} FormspecQuitMode;
+
+struct TextDest
+{
+       virtual ~TextDest() = default;
+
+       // This is deprecated I guess? -celeron55
+       virtual void gotText(const std::wstring &text) {}
+       virtual void gotText(const StringMap &fields) = 0;
+
+       std::string m_formname;
+};
+
+class IFormSource
+{
+public:
+       virtual ~IFormSource() = default;
+       virtual const std::string &getForm() const = 0;
+       // Fill in variables in field text
+       virtual std::string resolveText(const std::string &str) { return str; }
+};
+
+class GUIFormSpecMenu : public GUIModalMenu
+{
+       struct ItemSpec
+       {
+               ItemSpec() = default;
+
+               ItemSpec(const InventoryLocation &a_inventoryloc,
+                               const std::string &a_listname,
+                               s32 a_i) :
+                       inventoryloc(a_inventoryloc),
+                       listname(a_listname),
+                       i(a_i)
+               {
+               }
+
+               bool isValid() const { return i != -1; }
+
+               InventoryLocation inventoryloc;
+               std::string listname;
+               s32 i = -1;
+       };
+
+       struct ListDrawSpec
+       {
+               ListDrawSpec() = default;
+
+               ListDrawSpec(const InventoryLocation &a_inventoryloc,
+                               const std::string &a_listname,
+                               v2s32 a_pos, v2s32 a_geom, s32 a_start_item_i):
+                       inventoryloc(a_inventoryloc),
+                       listname(a_listname),
+                       pos(a_pos),
+                       geom(a_geom),
+                       start_item_i(a_start_item_i)
+               {
+               }
+
+               InventoryLocation inventoryloc;
+               std::string listname;
+               v2s32 pos;
+               v2s32 geom;
+               s32 start_item_i;
+       };
+
+       struct ListRingSpec
+       {
+               ListRingSpec() = default;
+
+               ListRingSpec(const InventoryLocation &a_inventoryloc,
+                               const std::string &a_listname):
+                       inventoryloc(a_inventoryloc),
+                       listname(a_listname)
+               {
+               }
+
+               InventoryLocation inventoryloc;
+               std::string listname;
+       };
+
+       struct ImageDrawSpec
+       {
+               ImageDrawSpec():
+                       parent_button(NULL),
+                       clip(false)
+               {
+               }
+
+               ImageDrawSpec(const std::string &a_name,
+                               const std::string &a_item_name,
+                               gui::IGUIButton *a_parent_button,
+                               const v2s32 &a_pos, const v2s32 &a_geom):
+                       name(a_name),
+                       item_name(a_item_name),
+                       parent_button(a_parent_button),
+                       pos(a_pos),
+                       geom(a_geom),
+                       scale(true),
+                       clip(false)
+               {
+               }
+
+               ImageDrawSpec(const std::string &a_name,
+                               const std::string &a_item_name,
+                               const v2s32 &a_pos, const v2s32 &a_geom):
+                       name(a_name),
+                       item_name(a_item_name),
+                       parent_button(NULL),
+                       pos(a_pos),
+                       geom(a_geom),
+                       scale(true),
+                       clip(false)
+               {
+               }
+
+               ImageDrawSpec(const std::string &a_name,
+                               const v2s32 &a_pos, const v2s32 &a_geom, bool clip=false):
+                       name(a_name),
+                       parent_button(NULL),
+                       pos(a_pos),
+                       geom(a_geom),
+                       scale(true),
+                       clip(clip)
+               {
+               }
+
+               ImageDrawSpec(const std::string &a_name,
+                               const v2s32 &a_pos):
+                       name(a_name),
+                       parent_button(NULL),
+                       pos(a_pos),
+                       scale(false),
+                       clip(false)
+               {
+               }
+
+               std::string name;
+               std::string item_name;
+               gui::IGUIButton *parent_button;
+               v2s32 pos;
+               v2s32 geom;
+               bool scale;
+               bool clip;
+       };
+
+       struct FieldSpec
+       {
+               FieldSpec() = default;
+
+               FieldSpec(const std::string &name, const std::wstring &label,
+                               const std::wstring &default_text, int id) :
+                       fname(name),
+                       flabel(label),
+                       fdefault(unescape_enriched(translate_string(default_text))),
+                       fid(id),
+                       send(false),
+                       ftype(f_Unknown),
+                       is_exit(false)
+               {
+               }
+
+               std::string fname;
+               std::wstring flabel;
+               std::wstring fdefault;
+               int fid;
+               bool send;
+               FormspecFieldType ftype;
+               bool is_exit;
+               core::rect<s32> rect;
+       };
+
+       struct BoxDrawSpec
+       {
+               BoxDrawSpec(v2s32 a_pos, v2s32 a_geom,irr::video::SColor a_color):
+                       pos(a_pos),
+                       geom(a_geom),
+                       color(a_color)
+               {
+               }
+               v2s32 pos;
+               v2s32 geom;
+               irr::video::SColor color;
+       };
+
+       struct TooltipSpec
+       {
+               TooltipSpec() = default;
+               TooltipSpec(const std::wstring &a_tooltip, irr::video::SColor a_bgcolor,
+                               irr::video::SColor a_color):
+                       tooltip(translate_string(a_tooltip)),
+                       bgcolor(a_bgcolor),
+                       color(a_color)
+               {
+               }
+
+               std::wstring tooltip;
+               irr::video::SColor bgcolor;
+               irr::video::SColor color;
+       };
+
+       struct StaticTextSpec
+       {
+               StaticTextSpec():
+                       parent_button(NULL)
+               {
+               }
+
+               StaticTextSpec(const std::wstring &a_text,
+                               const core::rect<s32> &a_rect):
+                       text(a_text),
+                       rect(a_rect),
+                       parent_button(NULL)
+               {
+               }
+
+               StaticTextSpec(const std::wstring &a_text,
+                               const core::rect<s32> &a_rect,
+                               gui::IGUIButton *a_parent_button):
+                       text(a_text),
+                       rect(a_rect),
+                       parent_button(a_parent_button)
+               {
+               }
+
+               std::wstring text;
+               core::rect<s32> rect;
+               gui::IGUIButton *parent_button;
+       };
+
+public:
+       GUIFormSpecMenu(JoystickController *joystick,
+                       gui::IGUIElement* parent, s32 id,
+                       IMenuManager *menumgr,
+                       Client *client,
+                       ISimpleTextureSource *tsrc,
+                       IFormSource* fs_src,
+                       TextDest* txt_dst,
+                       bool remap_dbl_click = true);
+
+       ~GUIFormSpecMenu();
+
+       void setFormSpec(const std::string &formspec_string,
+                       const InventoryLocation &current_inventory_location)
+       {
+               m_formspec_string = formspec_string;
+               m_current_inventory_location = current_inventory_location;
+               regenerateGui(m_screensize_old);
+       }
+
+       // form_src is deleted by this GUIFormSpecMenu
+       void setFormSource(IFormSource *form_src)
+       {
+               delete m_form_src;
+               m_form_src = form_src;
+       }
+
+       // text_dst is deleted by this GUIFormSpecMenu
+       void setTextDest(TextDest *text_dst)
+       {
+               delete m_text_dst;
+               m_text_dst = text_dst;
+       }
+
+       void allowClose(bool value)
+       {
+               m_allowclose = value;
+       }
+
+       void lockSize(bool lock,v2u32 basescreensize=v2u32(0,0))
+       {
+               m_lock = lock;
+               m_lockscreensize = basescreensize;
+       }
+
+       void removeChildren();
+       void setInitialFocus();
+
+       void setFocus(const std::string &elementname)
+       {
+               m_focused_element = elementname;
+       }
+
+       /*
+               Remove and re-add (or reposition) stuff
+       */
+       void regenerateGui(v2u32 screensize);
+
+       ItemSpec getItemAtPos(v2s32 p) const;
+       void drawList(const ListDrawSpec &s, int phase, bool &item_hovered);
+       void drawSelectedItem();
+       void drawMenu();
+       void updateSelectedItem();
+       ItemStack verifySelectedItem();
+
+       void acceptInput(FormspecQuitMode quitmode);
+       bool preprocessEvent(const SEvent& event);
+       bool OnEvent(const SEvent& event);
+       bool doPause;
+       bool pausesGame() { return doPause; }
+
+       GUITable* getTable(const std::string &tablename);
+       std::vector<std::string>* getDropDownValues(const std::string &name);
+
+#ifdef __ANDROID__
+       bool getAndroidUIInput();
+#endif
+
+protected:
+       v2s32 getBasePos() const
+       {
+                       return padding + offset + AbsoluteRect.UpperLeftCorner;
+       }
+
+       v2s32 padding;
+       v2s32 spacing;
+       v2s32 imgsize;
+       v2s32 offset;
+       v2s32 pos_offset;
+       std::stack<v2s32> container_stack;
+
+       InventoryManager *m_invmgr;
+       ISimpleTextureSource *m_tsrc;
+       Client *m_client;
+
+       std::string m_formspec_string;
+       InventoryLocation m_current_inventory_location;
+
+       std::vector<ListDrawSpec> m_inventorylists;
+       std::vector<ListRingSpec> m_inventory_rings;
+       std::vector<ImageDrawSpec> m_backgrounds;
+       std::vector<ImageDrawSpec> m_images;
+       std::vector<ImageDrawSpec> m_itemimages;
+       std::vector<BoxDrawSpec> m_boxes;
+       std::unordered_map<std::string, bool> field_close_on_enter;
+       std::vector<FieldSpec> m_fields;
+       std::vector<StaticTextSpec> m_static_texts;
+       std::vector<std::pair<FieldSpec,GUITable*> > m_tables;
+       std::vector<std::pair<FieldSpec,gui::IGUICheckBox*> > m_checkboxes;
+       std::map<std::string, TooltipSpec> m_tooltips;
+       std::vector<std::pair<FieldSpec,gui::IGUIScrollBar*> > m_scrollbars;
+       std::vector<std::pair<FieldSpec, std::vector<std::string> > > m_dropdowns;
+
+       ItemSpec *m_selected_item = nullptr;
+       u32 m_selected_amount = 0;
+       bool m_selected_dragging = false;
+
+       // WARNING: BLACK MAGIC
+       // Used to guess and keep up with some special things the server can do.
+       // If name is "", no guess exists.
+       ItemStack m_selected_content_guess;
+       InventoryLocation m_selected_content_guess_inventory;
+
+       v2s32 m_pointer;
+       v2s32 m_old_pointer;  // Mouse position after previous mouse event
+       gui::IGUIStaticText *m_tooltip_element = nullptr;
+
+       u64 m_tooltip_show_delay;
+       bool m_tooltip_append_itemname;
+       u64 m_hovered_time = 0;
+       s32 m_old_tooltip_id = -1;
+
+       bool m_auto_place = false;
+
+       bool m_allowclose = true;
+       bool m_lock = false;
+       v2u32 m_lockscreensize;
+
+       bool m_bgfullscreen;
+       bool m_slotborder;
+       video::SColor m_bgcolor;
+       video::SColor m_fullscreen_bgcolor;
+       video::SColor m_slotbg_n;
+       video::SColor m_slotbg_h;
+       video::SColor m_slotbordercolor;
+       video::SColor m_default_tooltip_bgcolor;
+       video::SColor m_default_tooltip_color;
+
+private:
+       IFormSource        *m_form_src;
+       TextDest           *m_text_dst;
+       u32                 m_formspec_version = 0;
+       std::string         m_focused_element = "";
+       JoystickController *m_joystick;
+
+       typedef struct {
+               bool explicit_size;
+               v2f invsize;
+               v2s32 size;
+               v2f32 offset;
+               v2f32 anchor;
+               core::rect<s32> rect;
+               v2s32 basepos;
+               v2u32 screensize;
+               std::string focused_fieldname;
+               GUITable::TableOptions table_options;
+               GUITable::TableColumns table_columns;
+               // used to restore table selection/scroll/treeview state
+               std::unordered_map<std::string, GUITable::DynamicData> table_dyndata;
+       } parserData;
+
+       typedef struct {
+               bool key_up;
+               bool key_down;
+               bool key_enter;
+               bool key_escape;
+       } fs_key_pendig;
+
+       fs_key_pendig current_keys_pending;
+       std::string current_field_enter_pending = "";
+
+       void parseElement(parserData* data, const std::string &element);
+
+       void parseSize(parserData* data, const std::string &element);
+       void parseContainer(parserData* data, const std::string &element);
+       void parseContainerEnd(parserData* data);
+       void parseList(parserData* data, const std::string &element);
+       void parseListRing(parserData* data, const std::string &element);
+       void parseCheckbox(parserData* data, const std::string &element);
+       void parseImage(parserData* data, const std::string &element);
+       void parseItemImage(parserData* data, const std::string &element);
+       void parseButton(parserData* data, const std::string &element,
+                       const std::string &typ);
+       void parseBackground(parserData* data, const std::string &element);
+       void parseTableOptions(parserData* data, const std::string &element);
+       void parseTableColumns(parserData* data, const std::string &element);
+       void parseTable(parserData* data, const std::string &element);
+       void parseTextList(parserData* data, const std::string &element);
+       void parseDropDown(parserData* data, const std::string &element);
+       void parseFieldCloseOnEnter(parserData *data, const std::string &element);
+       void parsePwdField(parserData* data, const std::string &element);
+       void parseField(parserData* data, const std::string &element, const std::string &type);
+       void parseSimpleField(parserData* data,std::vector<std::string> &parts);
+       void parseTextArea(parserData* data,std::vector<std::string>& parts,
+                       const std::string &type);
+       void parseLabel(parserData* data, const std::string &element);
+       void parseVertLabel(parserData* data, const std::string &element);
+       void parseImageButton(parserData* data, const std::string &element,
+                       const std::string &type);
+       void parseItemImageButton(parserData* data, const std::string &element);
+       void parseTabHeader(parserData* data, const std::string &element);
+       void parseBox(parserData* data, const std::string &element);
+       void parseBackgroundColor(parserData* data, const std::string &element);
+       void parseListColors(parserData* data, const std::string &element);
+       void parseTooltip(parserData* data, const std::string &element);
+       bool parseVersionDirect(const std::string &data);
+       bool parseSizeDirect(parserData* data, const std::string &element);
+       void parseScrollBar(parserData* data, const std::string &element);
+       bool parsePositionDirect(parserData *data, const std::string &element);
+       void parsePosition(parserData *data, const std::string &element);
+       bool parseAnchorDirect(parserData *data, const std::string &element);
+       void parseAnchor(parserData *data, const std::string &element);
+
+       void tryClose();
+
+       void showTooltip(const std::wstring &text, const irr::video::SColor &color,
+               const irr::video::SColor &bgcolor);
+
+       /**
+        * check if event is part of a double click
+        * @param event event to evaluate
+        * @return true/false if a doubleclick was detected
+        */
+       bool DoubleClickDetection(const SEvent event);
+
+       struct clickpos
+       {
+               v2s32 pos;
+               s64 time;
+       };
+       clickpos m_doubleclickdetect[2];
+
+       int m_btn_height;
+       gui::IGUIFont *m_font = nullptr;
+
+       std::wstring getLabelByID(s32 id);
+       std::string getNameByID(s32 id);
+#ifdef __ANDROID__
+       v2s32 m_down_pos;
+       std::string m_JavaDialogFieldName;
+#endif
+
+       /* If true, remap a double-click (or double-tap) action to ESC. This is so
+        * that, for example, Android users can double-tap to close a formspec.
+       *
+        * This value can (currently) only be set by the class constructor
+        * and the default value for the setting is true.
+        */
+       bool m_remap_dbl_click;
+
+};
+
+class FormspecFormSource: public IFormSource
+{
+public:
+       FormspecFormSource(const std::string &formspec):
+               m_formspec(formspec)
+       {
+       }
+
+       ~FormspecFormSource() = default;
+
+       void setForm(const std::string &formspec)
+       {
+               m_formspec = FORMSPEC_VERSION_STRING + formspec;
+       }
+
+       const std::string &getForm() const
+       {
+               return m_formspec;
+       }
+
+       std::string m_formspec;
+};
diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp
new file mode 100644 (file)
index 0000000..53677a5
--- /dev/null
@@ -0,0 +1,436 @@
+/*
+ Minetest
+ Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+ Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
+ Copyright (C) 2013 teddydestodes <derkomtur@schattengang.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser 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 <string>
+#include <IGUICheckBox.h>
+#include <IGUIEditBox.h>
+#include <IGUIButton.h>
+#include <IGUIStaticText.h>
+#include <IGUIFont.h>
+#include "settings.h"
+#include <algorithm>
+
+#include "mainmenumanager.h"  // for g_gamecallback
+
+#define KMaxButtonPerColumns 12
+
+extern MainGameCallback *g_gamecallback;
+
+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_NOCLIP_BUTTON,
+       GUI_ID_KEY_CINEMATIC_BUTTON,
+       GUI_ID_KEY_CHAT_BUTTON,
+       GUI_ID_KEY_CMD_BUTTON,
+       GUI_ID_KEY_CMD_LOCAL_BUTTON,
+       GUI_ID_KEY_CONSOLE_BUTTON,
+       GUI_ID_KEY_SNEAK_BUTTON,
+       GUI_ID_KEY_DROP_BUTTON,
+       GUI_ID_KEY_INVENTORY_BUTTON,
+       GUI_ID_KEY_HOTBAR_PREV_BUTTON,
+       GUI_ID_KEY_HOTBAR_NEXT_BUTTON,
+       GUI_ID_KEY_MUTE_BUTTON,
+       GUI_ID_KEY_DEC_VOLUME_BUTTON,
+       GUI_ID_KEY_INC_VOLUME_BUTTON,
+       GUI_ID_KEY_RANGE_BUTTON,
+       GUI_ID_KEY_ZOOM_BUTTON,
+       GUI_ID_KEY_CAMERA_BUTTON,
+       GUI_ID_KEY_MINIMAP_BUTTON,
+       GUI_ID_KEY_SCREENSHOT_BUTTON,
+       GUI_ID_KEY_CHATLOG_BUTTON,
+       GUI_ID_KEY_HUD_BUTTON,
+       GUI_ID_KEY_FOG_BUTTON,
+       GUI_ID_KEY_DEC_RANGE_BUTTON,
+       GUI_ID_KEY_INC_RANGE_BUTTON,
+       GUI_ID_KEY_AUTOFWD_BUTTON,
+       // other
+       GUI_ID_CB_AUX1_DESCENDS,
+       GUI_ID_CB_DOUBLETAP_JUMP,
+};
+
+GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
+                               gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
+GUIModalMenu(env, parent, id, menumgr)
+{
+       init_keys();
+       for (key_setting *ks : key_settings)
+               key_used.push_back(ks->key);
+}
+
+GUIKeyChangeMenu::~GUIKeyChangeMenu()
+{
+       removeChildren();
+
+       for (key_setting *ks : key_settings) {
+               delete[] ks->button_name;
+               delete ks;
+       }
+       key_settings.clear();
+}
+
+void GUIKeyChangeMenu::removeChildren()
+{
+       const core::list<gui::IGUIElement*> &children = getChildren();
+       core::list<gui::IGUIElement*> children_copy;
+       for (gui::IGUIElement*i : children) {
+               children_copy.push_back(i);
+       }
+
+       for (gui::IGUIElement *i : children_copy) {
+               i->remove();
+       }
+}
+
+void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
+{
+       removeChildren();
+       v2s32 size(745, 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, 600, 40);
+               rect += topleft + v2s32(25, 3);
+               //gui::IGUIStaticText *t =
+               const wchar_t *text = wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)");
+               Environment->addStaticText(text,
+                                                                  rect, false, true, this, -1);
+               delete[] text;
+               //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
+       }
+
+       // Build buttons
+
+       v2s32 offset(25, 60);
+
+       for(size_t i = 0; i < key_settings.size(); i++)
+       {
+               key_setting *k = key_settings.at(i);
+               {
+                       core::rect < s32 > rect(0, 0, 150, 20);
+                       rect += topleft + v2s32(offset.X, offset.Y);
+                       Environment->addStaticText(k->button_name, rect, false, true, this, -1);
+               }
+
+               {
+                       core::rect < s32 > rect(0, 0, 100, 30);
+                       rect += topleft + v2s32(offset.X + 120, offset.Y - 5);
+                       const wchar_t *text = wgettext(k->key.name());
+                       k->button = Environment->addButton(rect, this, k->id, text);
+                       delete[] text;
+               }
+               if ((i + 1) % KMaxButtonPerColumns == 0) {
+                       offset.X += 230;
+                       offset.Y = 60;
+               } else {
+                       offset += v2s32(0, 25);
+               }
+       }
+
+       {
+               s32 option_x = offset.X;
+               s32 option_y = offset.Y + 5;
+               u32 option_w = 180;
+               {
+                       core::rect<s32> rect(0, 0, option_w, 30);
+                       rect += topleft + v2s32(option_x, option_y);
+                       const wchar_t *text = wgettext("\"Special\" = climb down");
+                       Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
+                                       GUI_ID_CB_AUX1_DESCENDS, text);
+                       delete[] text;
+               }
+               offset += v2s32(0, 25);
+       }
+
+       {
+               s32 option_x = offset.X;
+               s32 option_y = offset.Y + 5;
+               u32 option_w = 280;
+               {
+                       core::rect<s32> rect(0, 0, option_w, 30);
+                       rect += topleft + v2s32(option_x, option_y);
+                       const wchar_t *text = wgettext("Double tap \"jump\" to toggle fly");
+                       Environment->addCheckBox(g_settings->getBool("doubletap_jump"), rect, this,
+                                       GUI_ID_CB_DOUBLETAP_JUMP, text);
+                       delete[] text;
+               }
+               offset += v2s32(0, 25);
+       }
+
+       {
+               core::rect < s32 > rect(0, 0, 100, 30);
+               rect += topleft + v2s32(size.X / 2 - 105, size.Y - 40);
+               const wchar_t *text =  wgettext("Save");
+               Environment->addButton(rect, this, GUI_ID_BACK_BUTTON,
+                                text);
+               delete[] text;
+       }
+       {
+               core::rect < s32 > rect(0, 0, 100, 30);
+               rect += topleft + v2s32(size.X / 2 + 5, size.Y - 40);
+               const wchar_t *text = wgettext("Cancel");
+               Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON,
+                               text);
+               delete[] text;
+       }
+}
+
+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, 745, 620);
+               rect += AbsoluteRect.UpperLeftCorner;
+               driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
+       }
+
+       gui::IGUIElement::draw();
+}
+
+bool GUIKeyChangeMenu::acceptInput()
+{
+       for (key_setting *k : key_settings) {
+               g_settings->set(k->setting_name, k->key.sym());
+       }
+
+       {
+               gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
+               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
+                       g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
+       }
+       {
+               gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
+               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
+                       g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
+       }
+
+       clearKeyCache();
+
+       g_gamecallback->signalKeyConfigChange();
+
+       return true;
+}
+
+bool GUIKeyChangeMenu::resetMenu()
+{
+       if (activeKey >= 0)
+       {
+               for (key_setting *k : key_settings) {
+                       if (k->id == activeKey) {
+                               const wchar_t *text = wgettext(k->key.name());
+                               k->button->setText(text);
+                               delete[] text;
+                               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) {
+
+               bool prefer_character = shift_down;
+               KeyPress kp(event.KeyInput, prefer_character);
+
+               bool shift_went_down = false;
+               if(!shift_down &&
+                               (event.KeyInput.Key == irr::KEY_SHIFT ||
+                               event.KeyInput.Key == irr::KEY_LSHIFT ||
+                               event.KeyInput.Key == irr::KEY_RSHIFT))
+                       shift_went_down = true;
+
+               // Remove Key already in use message
+               if(this->key_used_text)
+               {
+                       this->key_used_text->remove();
+                       this->key_used_text = NULL;
+               }
+               // Display Key already in use message
+               if (std::find(this->key_used.begin(), this->key_used.end(), kp) != this->key_used.end())
+               {
+                       core::rect < s32 > rect(0, 0, 600, 40);
+                       rect += v2s32(0, 0) + v2s32(25, 30);
+                       const wchar_t *text = wgettext("Key already in use");
+                       this->key_used_text = Environment->addStaticText(text,
+                                       rect, false, true, this, -1);
+                       delete[] text;
+                       //infostream << "Key already in use" << std::endl;
+               }
+
+               // But go on
+               {
+                       key_setting *k = NULL;
+                       for (key_setting *ks : key_settings) {
+                               if (ks->id == activeKey) {
+                                       k = ks;
+                                       break;
+                               }
+                       }
+                       FATAL_ERROR_IF(k == NULL, "Key setting not found");
+                       k->key = kp;
+                       const wchar_t *text = wgettext(k->key.name());
+                       k->button->setText(text);
+                       delete[] text;
+
+                       this->key_used.push_back(kp);
+
+                       // Allow characters made with shift
+                       if(shift_went_down){
+                               shift_down = true;
+                               return false;
+                       }
+
+                       activeKey = -1;
+                       return true;
+               }
+       } else if (event.EventType == EET_KEY_INPUT_EVENT && activeKey < 0
+                       && event.KeyInput.PressedDown
+                       && event.KeyInput.Key == irr::KEY_ESCAPE) {
+               quitMenu();
+               return true;
+       } else 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;
+                               default:
+                                       key_setting *k = NULL;
+
+                                       for (key_setting *ks : key_settings) {
+                                               if (ks->id == event.GUIEvent.Caller->getID()) {
+                                                       k = ks;
+                                                       break;
+                                               }
+                                       }
+                                       FATAL_ERROR_IF(k == NULL, "Key setting not found");
+
+                                       resetMenu();
+                                       shift_down = false;
+                                       activeKey = event.GUIEvent.Caller->getID();
+                                       const wchar_t *text = wgettext("press key");
+                                       k->button->setText(text);
+                                       delete[] text;
+                                       this->key_used.erase(std::remove(this->key_used.begin(),
+                                                       this->key_used.end(), k->key), this->key_used.end());
+                                       break;
+                       }
+                       Environment->setFocus(this);
+               }
+       }
+       return Parent ? Parent->OnEvent(event) : false;
+}
+
+void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::string &setting_name)
+{
+       key_setting *k = new key_setting;
+       k->id = id;
+
+       k->button_name = button_name;
+       k->setting_name = setting_name;
+       k->key = getKeySetting(k->setting_name.c_str());
+       key_settings.push_back(k);
+}
+
+void GUIKeyChangeMenu::init_keys()
+{
+       this->add_key(GUI_ID_KEY_FORWARD_BUTTON,   wgettext("Forward"),          "keymap_forward");
+       this->add_key(GUI_ID_KEY_BACKWARD_BUTTON,  wgettext("Backward"),         "keymap_backward");
+       this->add_key(GUI_ID_KEY_LEFT_BUTTON,      wgettext("Left"),             "keymap_left");
+       this->add_key(GUI_ID_KEY_RIGHT_BUTTON,     wgettext("Right"),            "keymap_right");
+       this->add_key(GUI_ID_KEY_USE_BUTTON,       wgettext("Special"),          "keymap_special1");
+       this->add_key(GUI_ID_KEY_JUMP_BUTTON,      wgettext("Jump"),             "keymap_jump");
+       this->add_key(GUI_ID_KEY_SNEAK_BUTTON,     wgettext("Sneak"),            "keymap_sneak");
+       this->add_key(GUI_ID_KEY_DROP_BUTTON,      wgettext("Drop"),             "keymap_drop");
+       this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"),        "keymap_inventory");
+       this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON,wgettext("Prev. item"),      "keymap_hotbar_previous");
+       this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON,wgettext("Next item"),       "keymap_hotbar_next");
+       this->add_key(GUI_ID_KEY_ZOOM_BUTTON,      wgettext("Zoom"),             "keymap_zoom");
+       this->add_key(GUI_ID_KEY_CAMERA_BUTTON,    wgettext("Change camera"),    "keymap_camera_mode");
+       this->add_key(GUI_ID_KEY_CINEMATIC_BUTTON, wgettext("Toggle Cinematic"), "keymap_cinematic");
+       this->add_key(GUI_ID_KEY_MINIMAP_BUTTON,   wgettext("Toggle minimap"),   "keymap_minimap");
+       this->add_key(GUI_ID_KEY_FLY_BUTTON,       wgettext("Toggle fly"),       "keymap_freemove");
+       this->add_key(GUI_ID_KEY_FAST_BUTTON,      wgettext("Toggle fast"),      "keymap_fastmove");
+       this->add_key(GUI_ID_KEY_NOCLIP_BUTTON,    wgettext("Toggle noclip"),    "keymap_noclip");
+       this->add_key(GUI_ID_KEY_MUTE_BUTTON,      wgettext("Mute"),             "keymap_mute");
+       this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON,wgettext("Dec. volume"),      "keymap_decrease_volume");
+       this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON,wgettext("Inc. volume"),      "keymap_increase_volume");
+       this->add_key(GUI_ID_KEY_AUTOFWD_BUTTON,   wgettext("Autoforward"),      "keymap_autoforward");
+       this->add_key(GUI_ID_KEY_CHAT_BUTTON,      wgettext("Chat"),             "keymap_chat");
+       this->add_key(GUI_ID_KEY_SCREENSHOT_BUTTON,wgettext("Screenshot"),       "keymap_screenshot");
+       this->add_key(GUI_ID_KEY_RANGE_BUTTON,     wgettext("Range select"),     "keymap_rangeselect");
+       this->add_key(GUI_ID_KEY_DEC_RANGE_BUTTON, wgettext("Dec. range"),       "keymap_decrease_viewing_range_min");
+       this->add_key(GUI_ID_KEY_INC_RANGE_BUTTON, wgettext("Inc. range"),       "keymap_increase_viewing_range_min");
+       this->add_key(GUI_ID_KEY_CONSOLE_BUTTON,   wgettext("Console"),          "keymap_console");
+       this->add_key(GUI_ID_KEY_CMD_BUTTON,       wgettext("Command"),          "keymap_cmd");
+       this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"),    "keymap_cmd_local");
+       this->add_key(GUI_ID_KEY_HUD_BUTTON,       wgettext("Toggle HUD"),       "keymap_toggle_hud");
+       this->add_key(GUI_ID_KEY_CHATLOG_BUTTON,   wgettext("Toggle chat log"),  "keymap_toggle_chat");
+       this->add_key(GUI_ID_KEY_FOG_BUTTON,       wgettext("Toggle fog"),       "keymap_toggle_force_fog_off");
+}
+
diff --git a/src/gui/guiKeyChangeMenu.h b/src/gui/guiKeyChangeMenu.h
new file mode 100644 (file)
index 0000000..7cf11d3
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ Minetest
+ Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+ Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
+ Copyright (C) 2013 teddydestodes <derkomtur@schattengang.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser 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.
+ */
+
+#pragma once
+
+#include "irrlichttypes_extrabloated.h"
+#include "modalMenu.h"
+#include "gettext.h"
+#include "keycode.h"
+#include <string>
+#include <vector>
+
+struct key_setting
+{
+       int id;
+       const wchar_t *button_name;
+       KeyPress key;
+       std::string setting_name;
+       gui::IGUIButton *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);
+
+       bool pausesGame() { return true; }
+
+private:
+       void init_keys();
+
+       bool resetMenu();
+
+       void add_key(int id, const wchar_t *button_name, const std::string &setting_name);
+
+       bool shift_down = false;
+       s32 activeKey = -1;
+
+       std::vector<KeyPress> key_used;
+       gui::IGUIStaticText *key_used_text = nullptr;
+       std::vector<key_setting *> key_settings;
+};
diff --git a/src/gui/guiMainMenu.h b/src/gui/guiMainMenu.h
new file mode 100644 (file)
index 0000000..43a3b1a
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+Minetest
+Copyright (C) 2010-2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "irrlichttypes_extrabloated.h"
+#include "modalMenu.h"
+#include <string>
+#include <list>
+
+struct MainMenuDataForScript {
+
+       MainMenuDataForScript() = default;
+
+       // Whether the server has requested a reconnect
+       bool reconnect_requested = false;
+       std::string errormessage = "";
+};
+
+struct MainMenuData {
+       // Client options
+       std::string servername;
+       std::string serverdescription;
+       std::string address;
+       std::string port;
+       std::string name;
+       std::string password;
+       // Whether to reconnect
+       bool do_reconnect = false;
+
+       // Server options
+       int selected_world = 0;
+       bool simple_singleplayer_mode = false;
+
+       // Data to be passed to the script
+       MainMenuDataForScript script_data;
+
+       MainMenuData() = default;
+};
diff --git a/src/gui/guiPasswordChange.cpp b/src/gui/guiPasswordChange.cpp
new file mode 100644 (file)
index 0000000..46de202
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+Part of Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "guiPasswordChange.h"
+#include "client.h"
+#include <IGUICheckBox.h>
+#include <IGUIEditBox.h>
+#include <IGUIButton.h>
+#include <IGUIStaticText.h>
+#include <IGUIFont.h>
+
+#include "gettext.h"
+
+const int ID_oldPassword = 256;
+const int ID_newPassword1 = 257;
+const int ID_newPassword2 = 258;
+const int ID_change = 259;
+const int ID_message = 260;
+const int ID_cancel = 261;
+
+GUIPasswordChange::GUIPasswordChange(gui::IGUIEnvironment* env,
+               gui::IGUIElement* parent, s32 id,
+               IMenuManager *menumgr,
+               Client* client
+):
+       GUIModalMenu(env, parent, id, menumgr),
+       m_client(client)
+{
+}
+
+GUIPasswordChange::~GUIPasswordChange()
+{
+       removeChildren();
+}
+
+void GUIPasswordChange::removeChildren()
+{
+       const core::list<gui::IGUIElement *> &children = getChildren();
+       core::list<gui::IGUIElement *> children_copy;
+       for (gui::IGUIElement *i : children) {
+               children_copy.push_back(i);
+       }
+
+       for (gui::IGUIElement *i : children_copy) {
+               i->remove();
+       }
+}
+void GUIPasswordChange::regenerateGui(v2u32 screensize)
+{
+       /*
+               save current input
+       */
+       acceptInput();
+
+       /*
+               Remove stuff
+       */
+       removeChildren();
+
+       /*
+               Calculate new sizes and positions
+       */
+       core::rect<s32> rect(
+                       screensize.X/2 - 580/2,
+                       screensize.Y/2 - 300/2,
+                       screensize.X/2 + 580/2,
+                       screensize.Y/2 + 300/2
+       );
+
+       DesiredRect = rect;
+       recalculateAbsolutePosition(false);
+
+       v2s32 size = rect.getSize();
+       v2s32 topleft_client(40, 0);
+
+       const wchar_t *text;
+
+       /*
+               Add stuff
+       */
+       s32 ypos = 50;
+       {
+               core::rect<s32> rect(0, 0, 150, 20);
+               rect += topleft_client + v2s32(25, ypos + 6);
+               text = wgettext("Old Password");
+               Environment->addStaticText(text, rect, false, true, this, -1);
+               delete[] text;
+       }
+       {
+               core::rect<s32> rect(0, 0, 230, 30);
+               rect += topleft_client + v2s32(160, ypos);
+               gui::IGUIEditBox *e = Environment->addEditBox(
+                               m_oldpass.c_str(), rect, true, this, ID_oldPassword);
+               Environment->setFocus(e);
+               e->setPasswordBox(true);
+       }
+       ypos += 50;
+       {
+               core::rect<s32> rect(0, 0, 150, 20);
+               rect += topleft_client + v2s32(25, ypos + 6);
+               text = wgettext("New Password");
+               Environment->addStaticText(text, rect, false, true, this, -1);
+               delete[] text;
+       }
+       {
+               core::rect<s32> rect(0, 0, 230, 30);
+               rect += topleft_client + v2s32(160, ypos);
+               gui::IGUIEditBox *e = Environment->addEditBox(
+                               m_newpass.c_str(), rect, true, this, ID_newPassword1);
+               e->setPasswordBox(true);
+       }
+       ypos += 50;
+       {
+               core::rect<s32> rect(0, 0, 150, 20);
+               rect += topleft_client + v2s32(25, ypos + 6);
+               text = wgettext("Confirm Password");
+               Environment->addStaticText(text, rect, false, true, this, -1);
+               delete[] text;
+       }
+       {
+               core::rect<s32> rect(0, 0, 230, 30);
+               rect += topleft_client + v2s32(160, ypos);
+               gui::IGUIEditBox *e = Environment->addEditBox(
+                               m_newpass_confirm.c_str(), rect, true, this, ID_newPassword2);
+               e->setPasswordBox(true);
+       }
+
+       ypos += 50;
+       {
+               core::rect<s32> rect(0, 0, 100, 30);
+               rect = rect + v2s32(size.X / 4 + 56, ypos);
+               text = wgettext("Change");
+               Environment->addButton(rect, this, ID_change, text);
+               delete[] text;
+       }
+       {
+               core::rect<s32> rect(0, 0, 100, 30);
+               rect = rect + v2s32(size.X / 4 + 185, ypos);
+               text = wgettext("Cancel");
+               Environment->addButton(rect, this, ID_cancel, text);
+               delete[] text;
+       }
+
+       ypos += 50;
+       {
+               core::rect<s32> rect(0, 0, 300, 20);
+               rect += topleft_client + v2s32(35, ypos);
+               text = wgettext("Passwords do not match!");
+               IGUIElement *e =
+                       Environment->addStaticText(
+                       text, rect, false, true, this, ID_message);
+               e->setVisible(false);
+               delete[] text;
+       }
+}
+
+void GUIPasswordChange::drawMenu()
+{
+       gui::IGUISkin *skin = Environment->getSkin();
+       if (!skin)
+               return;
+       video::IVideoDriver *driver = Environment->getVideoDriver();
+
+       video::SColor bgcolor(140, 0, 0, 0);
+       driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
+
+       gui::IGUIElement::draw();
+}
+
+void GUIPasswordChange::acceptInput()
+{
+       gui::IGUIElement *e;
+       e = getElementFromId(ID_oldPassword);
+       if (e != NULL)
+               m_oldpass = e->getText();
+       e = getElementFromId(ID_newPassword1);
+       if (e != NULL)
+               m_newpass = e->getText();
+       e = getElementFromId(ID_newPassword2);
+       if (e != NULL)
+               m_newpass_confirm = e->getText();
+}
+
+bool GUIPasswordChange::processInput()
+{
+       if (m_newpass != m_newpass_confirm) {
+               gui::IGUIElement *e = getElementFromId(ID_message);
+               if (e != NULL)
+                       e->setVisible(true);
+               return false;
+       }
+       m_client->sendChangePassword(wide_to_utf8(m_oldpass), wide_to_utf8(m_newpass));
+       return true;
+}
+
+bool GUIPasswordChange::OnEvent(const SEvent &event)
+{
+       if (event.EventType == EET_KEY_INPUT_EVENT) {
+               if (event.KeyInput.Key == KEY_ESCAPE && event.KeyInput.PressedDown) {
+                       quitMenu();
+                       return true;
+               }
+               if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
+                       acceptInput();
+                       if (processInput())
+                               quitMenu();
+                       return true;
+               }
+       }
+       if (event.EventType == EET_GUI_EVENT) {
+               if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST &&
+                               isVisible()) {
+                       if (!canTakeFocus(event.GUIEvent.Element)) {
+                               dstream << "GUIPasswordChange: 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 ID_change:
+                               acceptInput();
+                               if (processInput())
+                                       quitMenu();
+                               return true;
+                       case ID_cancel:
+                               quitMenu();
+                               return true;
+                       }
+               }
+               if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
+                       switch (event.GUIEvent.Caller->getID()) {
+                       case ID_oldPassword:
+                       case ID_newPassword1:
+                       case ID_newPassword2:
+                               acceptInput();
+                               if (processInput())
+                                       quitMenu();
+                               return true;
+                       }
+               }
+       }
+
+       return Parent ? Parent->OnEvent(event) : false;
+}
diff --git a/src/gui/guiPasswordChange.h b/src/gui/guiPasswordChange.h
new file mode 100644 (file)
index 0000000..59f3513
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+Part of Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#pragma once
+
+#include "irrlichttypes_extrabloated.h"
+#include "modalMenu.h"
+#include <string>
+
+class Client;
+
+class GUIPasswordChange : public GUIModalMenu
+{
+public:
+       GUIPasswordChange(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
+                       IMenuManager *menumgr, Client *client);
+       ~GUIPasswordChange();
+
+       void removeChildren();
+       /*
+               Remove and re-add (or reposition) stuff
+       */
+       void regenerateGui(v2u32 screensize);
+
+       void drawMenu();
+
+       void acceptInput();
+
+       bool processInput();
+
+       bool OnEvent(const SEvent &event);
+
+private:
+       Client *m_client;
+       std::wstring m_oldpass = L"";
+       std::wstring m_newpass = L"";
+       std::wstring m_newpass_confirm = L"";
+};
diff --git a/src/gui/guiPathSelectMenu.cpp b/src/gui/guiPathSelectMenu.cpp
new file mode 100644 (file)
index 0000000..b999f0a
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ Minetest
+ Copyright (C) 2013 sapier
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser 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 "guiPathSelectMenu.h"
+
+GUIFileSelectMenu::GUIFileSelectMenu(gui::IGUIEnvironment* env,
+               gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
+               const std::string &title, const std::string &formname,
+               bool is_file_select) :
+       GUIModalMenu(env, parent, id, menumgr),
+       m_title(utf8_to_wide(title)),
+       m_formname(formname),
+       m_file_select_dialog(is_file_select)
+{
+}
+
+GUIFileSelectMenu::~GUIFileSelectMenu()
+{
+       removeChildren();
+       setlocale(LC_NUMERIC, "C");
+}
+
+void GUIFileSelectMenu::regenerateGui(v2u32 screensize)
+{
+       removeChildren();
+       m_fileOpenDialog = 0;
+
+       core::dimension2du size(600, 400);
+       core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
+
+       DesiredRect = rect;
+       recalculateAbsolutePosition(false);
+
+       m_fileOpenDialog =
+                       Environment->addFileOpenDialog(m_title.c_str(), false, this, -1);
+
+       core::position2di pos = core::position2di(screensize.X / 2 - size.Width / 2,
+                       screensize.Y / 2 - size.Height / 2);
+       m_fileOpenDialog->setRelativePosition(pos);
+       m_fileOpenDialog->setMinSize(size);
+}
+
+void GUIFileSelectMenu::drawMenu()
+{
+       gui::IGUISkin *skin = Environment->getSkin();
+       if (!skin)
+               return;
+
+       gui::IGUIElement::draw();
+}
+
+void GUIFileSelectMenu::acceptInput()
+{
+       if (m_text_dst && !m_formname.empty()) {
+               StringMap fields;
+               if (m_accepted) {
+                       std::string path;
+                       if (!m_file_select_dialog) {
+                               core::string<fschar_t> string =
+                                               m_fileOpenDialog->getDirectoryName();
+                               path = std::string(string.c_str());
+                       } else {
+                               path = wide_to_utf8(m_fileOpenDialog->getFileName());
+                       }
+                       fields[m_formname + "_accepted"] = path;
+               } else {
+                       fields[m_formname + "_canceled"] = m_formname;
+               }
+               m_text_dst->gotText(fields);
+       }
+       quitMenu();
+}
+
+bool GUIFileSelectMenu::OnEvent(const SEvent &event)
+{
+       if (event.EventType == irr::EET_GUI_EVENT) {
+               switch (event.GUIEvent.EventType) {
+               case gui::EGET_ELEMENT_CLOSED:
+               case gui::EGET_FILE_CHOOSE_DIALOG_CANCELLED:
+                       m_accepted = false;
+                       acceptInput();
+                       return true;
+               case gui::EGET_DIRECTORY_SELECTED:
+                       m_accepted = !m_file_select_dialog;
+                       acceptInput();
+                       return true;
+               case gui::EGET_FILE_SELECTED:
+                       m_accepted = m_file_select_dialog;
+                       acceptInput();
+                       return true;
+               default:
+                       // ignore this event
+                       break;
+               }
+       }
+       return Parent ? Parent->OnEvent(event) : false;
+}
diff --git a/src/gui/guiPathSelectMenu.h b/src/gui/guiPathSelectMenu.h
new file mode 100644 (file)
index 0000000..f69d0ac
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ Minetest
+ Copyright (C) 2013 sapier
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser 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.
+ */
+
+#pragma once
+
+#include <string>
+
+#include "modalMenu.h"
+#include "IGUIFileOpenDialog.h"
+#include "guiFormSpecMenu.h" //required because of TextDest only !!!
+
+class GUIFileSelectMenu : public GUIModalMenu
+{
+public:
+       GUIFileSelectMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
+                       IMenuManager *menumgr, const std::string &title,
+                       const std::string &formid, bool is_file_select);
+       ~GUIFileSelectMenu();
+
+       /*
+        Remove and re-add (or reposition) stuff
+        */
+       void regenerateGui(v2u32 screensize);
+
+       void drawMenu();
+
+       bool OnEvent(const SEvent &event);
+
+       void setTextDest(TextDest *dest) { m_text_dst = dest; }
+
+private:
+       void acceptInput();
+
+       std::wstring m_title;
+       bool m_accepted = false;
+
+       gui::IGUIFileOpenDialog *m_fileOpenDialog = nullptr;
+
+       TextDest *m_text_dst = nullptr;
+
+       std::string m_formname;
+       bool m_file_select_dialog;
+};
diff --git a/src/gui/guiTable.cpp b/src/gui/guiTable.cpp
new file mode 100644 (file)
index 0000000..a2738af
--- /dev/null
@@ -0,0 +1,1261 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "guiTable.h"
+#include <queue>
+#include <sstream>
+#include <utility>
+#include <cstring>
+#include <IGUISkin.h>
+#include <IGUIFont.h>
+#include <IGUIScrollBar.h>
+#include "client/renderingengine.h"
+#include "debug.h"
+#include "log.h"
+#include "client/tile.h"
+#include "gettime.h"
+#include "util/string.h"
+#include "util/numeric.h"
+#include "util/string.h" // for parseColorString()
+#include "settings.h" // for settings
+#include "porting.h" // for dpi
+#include "guiscalingfilter.h"
+
+/*
+       GUITable
+*/
+
+GUITable::GUITable(gui::IGUIEnvironment *env,
+               gui::IGUIElement* parent, s32 id,
+               core::rect<s32> rectangle,
+               ISimpleTextureSource *tsrc
+):
+       gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle),
+       m_tsrc(tsrc)
+{
+       assert(tsrc != NULL);
+
+       gui::IGUISkin* skin = Environment->getSkin();
+
+       m_font = skin->getFont();
+       if (m_font) {
+               m_font->grab();
+               m_rowheight = m_font->getDimension(L"A").Height + 4;
+               m_rowheight = MYMAX(m_rowheight, 1);
+       }
+
+       const s32 s = skin->getSize(gui::EGDS_SCROLLBAR_SIZE);
+       m_scrollbar = Environment->addScrollBar(false,
+                       core::rect<s32>(RelativeRect.getWidth() - s,
+                                       0,
+                                       RelativeRect.getWidth(),
+                                       RelativeRect.getHeight()),
+                       this, -1);
+       m_scrollbar->setSubElement(true);
+       m_scrollbar->setTabStop(false);
+       m_scrollbar->setAlignment(gui::EGUIA_LOWERRIGHT, gui::EGUIA_LOWERRIGHT,
+                       gui::EGUIA_UPPERLEFT, gui::EGUIA_LOWERRIGHT);
+       m_scrollbar->setVisible(false);
+       m_scrollbar->setPos(0);
+
+       setTabStop(true);
+       setTabOrder(-1);
+       updateAbsolutePosition();
+
+       core::rect<s32> relative_rect = m_scrollbar->getRelativePosition();
+       s32 width = (relative_rect.getWidth()/(2.0/3.0)) *
+                       RenderingEngine::getDisplayDensity() *
+                       g_settings->getFloat("gui_scaling");
+       m_scrollbar->setRelativePosition(core::rect<s32>(
+                       relative_rect.LowerRightCorner.X-width,relative_rect.UpperLeftCorner.Y,
+                       relative_rect.LowerRightCorner.X,relative_rect.LowerRightCorner.Y
+                       ));
+}
+
+GUITable::~GUITable()
+{
+       for (GUITable::Row &row : m_rows)
+               delete[] row.cells;
+
+       if (m_font)
+               m_font->drop();
+
+       m_scrollbar->remove();
+}
+
+GUITable::Option GUITable::splitOption(const std::string &str)
+{
+       size_t equal_pos = str.find('=');
+       if (equal_pos == std::string::npos)
+               return GUITable::Option(str, "");
+
+       return GUITable::Option(str.substr(0, equal_pos),
+                       str.substr(equal_pos + 1));
+}
+
+void GUITable::setTextList(const std::vector<std::string> &content,
+               bool transparent)
+{
+       clear();
+
+       if (transparent) {
+               m_background.setAlpha(0);
+               m_border = false;
+       }
+
+       m_is_textlist = true;
+
+       s32 empty_string_index = allocString("");
+
+       m_rows.resize(content.size());
+       for (s32 i = 0; i < (s32) content.size(); ++i) {
+               Row *row = &m_rows[i];
+               row->cells = new Cell[1];
+               row->cellcount = 1;
+               row->indent = 0;
+               row->visible_index = i;
+               m_visible_rows.push_back(i);
+
+               Cell *cell = row->cells;
+               cell->xmin = 0;
+               cell->xmax = 0x7fff;  // something large enough
+               cell->xpos = 6;
+               cell->content_type = COLUMN_TYPE_TEXT;
+               cell->content_index = empty_string_index;
+               cell->tooltip_index = empty_string_index;
+               cell->color.set(255, 255, 255, 255);
+               cell->color_defined = false;
+               cell->reported_column = 1;
+
+               // parse row content (color)
+               const std::string &s = content[i];
+               if (s[0] == '#' && s[1] == '#') {
+                       // double # to escape
+                       cell->content_index = allocString(s.substr(2));
+               }
+               else if (s[0] == '#' && s.size() >= 7 &&
+                               parseColorString(
+                                       s.substr(0,7), cell->color, false)) {
+                       // single # for color
+                       cell->color_defined = true;
+                       cell->content_index = allocString(s.substr(7));
+               }
+               else {
+                       // no #, just text
+                       cell->content_index = allocString(s);
+               }
+
+       }
+
+       allocationComplete();
+
+       // Clamp scroll bar position
+       updateScrollBar();
+}
+
+void GUITable::setTable(const TableOptions &options,
+               const TableColumns &columns,
+               std::vector<std::string> &content)
+{
+       clear();
+
+       // Naming conventions:
+       // i is always a row index, 0-based
+       // j is always a column index, 0-based
+       // k is another index, for example an option index
+
+       // Handle a stupid error case... (issue #1187)
+       if (columns.empty()) {
+               TableColumn text_column;
+               text_column.type = "text";
+               TableColumns new_columns;
+               new_columns.push_back(text_column);
+               setTable(options, new_columns, content);
+               return;
+       }
+
+       // Handle table options
+       video::SColor default_color(255, 255, 255, 255);
+       s32 opendepth = 0;
+       for (const Option &option : options) {
+               const std::string &name = option.name;
+               const std::string &value = option.value;
+               if (name == "color")
+                       parseColorString(value, m_color, false);
+               else if (name == "background")
+                       parseColorString(value, m_background, false);
+               else if (name == "border")
+                       m_border = is_yes(value);
+               else if (name == "highlight")
+                       parseColorString(value, m_highlight, false);
+               else if (name == "highlight_text")
+                       parseColorString(value, m_highlight_text, false);
+               else if (name == "opendepth")
+                       opendepth = stoi(value);
+               else
+                       errorstream<<"Invalid table option: \""<<name<<"\""
+                               <<" (value=\""<<value<<"\")"<<std::endl;
+       }
+
+       // Get number of columns and rows
+       // note: error case columns.size() == 0 was handled above
+       s32 colcount = columns.size();
+       assert(colcount >= 1);
+       // rowcount = ceil(cellcount / colcount) but use integer arithmetic
+       s32 rowcount = (content.size() + colcount - 1) / colcount;
+       assert(rowcount >= 0);
+       // Append empty strings to content if there is an incomplete row
+       s32 cellcount = rowcount * colcount;
+       while (content.size() < (u32) cellcount)
+               content.emplace_back("");
+
+       // Create temporary rows (for processing columns)
+       struct TempRow {
+               // Current horizontal position (may different between rows due
+               // to indent/tree columns, or text/image columns with width<0)
+               s32 x;
+               // Tree indentation level
+               s32 indent;
+               // Next cell: Index into m_strings or m_images
+               s32 content_index;
+               // Next cell: Width in pixels
+               s32 content_width;
+               // Vector of completed cells in this row
+               std::vector<Cell> cells;
+               // Stores colors and how long they last (maximum column index)
+               std::vector<std::pair<video::SColor, s32> > colors;
+
+               TempRow(): x(0), indent(0), content_index(0), content_width(0) {}
+       };
+       TempRow *rows = new TempRow[rowcount];
+
+       // Get em width. Pedantically speaking, the width of "M" is not
+       // necessarily the same as the em width, but whatever, close enough.
+       s32 em = 6;
+       if (m_font)
+               em = m_font->getDimension(L"M").Width;
+
+       s32 default_tooltip_index = allocString("");
+
+       std::map<s32, s32> active_image_indices;
+
+       // Process content in column-major order
+       for (s32 j = 0; j < colcount; ++j) {
+               // Check column type
+               ColumnType columntype = COLUMN_TYPE_TEXT;
+               if (columns[j].type == "text")
+                       columntype = COLUMN_TYPE_TEXT;
+               else if (columns[j].type == "image")
+                       columntype = COLUMN_TYPE_IMAGE;
+               else if (columns[j].type == "color")
+                       columntype = COLUMN_TYPE_COLOR;
+               else if (columns[j].type == "indent")
+                       columntype = COLUMN_TYPE_INDENT;
+               else if (columns[j].type == "tree")
+                       columntype = COLUMN_TYPE_TREE;
+               else
+                       errorstream<<"Invalid table column type: \""
+                               <<columns[j].type<<"\""<<std::endl;
+
+               // Process column options
+               s32 padding = myround(0.5 * em);
+               s32 tooltip_index = default_tooltip_index;
+               s32 align = 0;
+               s32 width = 0;
+               s32 span = colcount;
+
+               if (columntype == COLUMN_TYPE_INDENT) {
+                       padding = 0; // default indent padding
+               }
+               if (columntype == COLUMN_TYPE_INDENT ||
+                               columntype == COLUMN_TYPE_TREE) {
+                       width = myround(em * 1.5); // default indent width
+               }
+
+               for (const Option &option : columns[j].options) {
+                       const std::string &name = option.name;
+                       const std::string &value = option.value;
+                       if (name == "padding")
+                               padding = myround(stof(value) * em);
+                       else if (name == "tooltip")
+                               tooltip_index = allocString(value);
+                       else if (name == "align" && value == "left")
+                               align = 0;
+                       else if (name == "align" && value == "center")
+                               align = 1;
+                       else if (name == "align" && value == "right")
+                               align = 2;
+                       else if (name == "align" && value == "inline")
+                               align = 3;
+                       else if (name == "width")
+                               width = myround(stof(value) * em);
+                       else if (name == "span" && columntype == COLUMN_TYPE_COLOR)
+                               span = stoi(value);
+                       else if (columntype == COLUMN_TYPE_IMAGE &&
+                                       !name.empty() &&
+                                       string_allowed(name, "0123456789")) {
+                               s32 content_index = allocImage(value);
+                               active_image_indices.insert(std::make_pair(
+                                                       stoi(name),
+                                                       content_index));
+                       }
+                       else {
+                               errorstream<<"Invalid table column option: \""<<name<<"\""
+                                       <<" (value=\""<<value<<"\")"<<std::endl;
+                       }
+               }
+
+               // If current column type can use information from "color" columns,
+               // find out which of those is currently active
+               if (columntype == COLUMN_TYPE_TEXT) {
+                       for (s32 i = 0; i < rowcount; ++i) {
+                               TempRow *row = &rows[i];
+                               while (!row->colors.empty() && row->colors.back().second < j)
+                                       row->colors.pop_back();
+                       }
+               }
+
+               // Make template for new cells
+               Cell newcell;
+               memset(&newcell, 0, sizeof newcell);
+               newcell.content_type = columntype;
+               newcell.tooltip_index = tooltip_index;
+               newcell.reported_column = j+1;
+
+               if (columntype == COLUMN_TYPE_TEXT) {
+                       // Find right edge of column
+                       s32 xmax = 0;
+                       for (s32 i = 0; i < rowcount; ++i) {
+                               TempRow *row = &rows[i];
+                               row->content_index = allocString(content[i * colcount + j]);
+                               const core::stringw &text = m_strings[row->content_index];
+                               row->content_width = m_font ?
+                                       m_font->getDimension(text.c_str()).Width : 0;
+                               row->content_width = MYMAX(row->content_width, width);
+                               s32 row_xmax = row->x + padding + row->content_width;
+                               xmax = MYMAX(xmax, row_xmax);
+                       }
+                       // Add a new cell (of text type) to each row
+                       for (s32 i = 0; i < rowcount; ++i) {
+                               newcell.xmin = rows[i].x + padding;
+                               alignContent(&newcell, xmax, rows[i].content_width, align);
+                               newcell.content_index = rows[i].content_index;
+                               newcell.color_defined = !rows[i].colors.empty();
+                               if (newcell.color_defined)
+                                       newcell.color = rows[i].colors.back().first;
+                               rows[i].cells.push_back(newcell);
+                               rows[i].x = newcell.xmax;
+                       }
+               }
+               else if (columntype == COLUMN_TYPE_IMAGE) {
+                       // Find right edge of column
+                       s32 xmax = 0;
+                       for (s32 i = 0; i < rowcount; ++i) {
+                               TempRow *row = &rows[i];
+                               row->content_index = -1;
+
+                               // Find content_index. Image indices are defined in
+                               // column options so check active_image_indices.
+                               s32 image_index = stoi(content[i * colcount + j]);
+                               std::map<s32, s32>::iterator image_iter =
+                                       active_image_indices.find(image_index);
+                               if (image_iter != active_image_indices.end())
+                                       row->content_index = image_iter->second;
+
+                               // Get texture object (might be NULL)
+                               video::ITexture *image = NULL;
+                               if (row->content_index >= 0)
+                                       image = m_images[row->content_index];
+
+                               // Get content width and update xmax
+                               row->content_width = image ? image->getOriginalSize().Width : 0;
+                               row->content_width = MYMAX(row->content_width, width);
+                               s32 row_xmax = row->x + padding + row->content_width;
+                               xmax = MYMAX(xmax, row_xmax);
+                       }
+                       // Add a new cell (of image type) to each row
+                       for (s32 i = 0; i < rowcount; ++i) {
+                               newcell.xmin = rows[i].x + padding;
+                               alignContent(&newcell, xmax, rows[i].content_width, align);
+                               newcell.content_index = rows[i].content_index;
+                               rows[i].cells.push_back(newcell);
+                               rows[i].x = newcell.xmax;
+                       }
+                       active_image_indices.clear();
+               }
+               else if (columntype == COLUMN_TYPE_COLOR) {
+                       for (s32 i = 0; i < rowcount; ++i) {
+                               video::SColor cellcolor(255, 255, 255, 255);
+                               if (parseColorString(content[i * colcount + j], cellcolor, true))
+                                       rows[i].colors.emplace_back(cellcolor, j+span);
+                       }
+               }
+               else if (columntype == COLUMN_TYPE_INDENT ||
+                               columntype == COLUMN_TYPE_TREE) {
+                       // For column type "tree", reserve additional space for +/-
+                       // Also enable special processing for treeview-type tables
+                       s32 content_width = 0;
+                       if (columntype == COLUMN_TYPE_TREE) {
+                               content_width = m_font ? m_font->getDimension(L"+").Width : 0;
+                               m_has_tree_column = true;
+                       }
+                       // Add a new cell (of indent or tree type) to each row
+                       for (s32 i = 0; i < rowcount; ++i) {
+                               TempRow *row = &rows[i];
+
+                               s32 indentlevel = stoi(content[i * colcount + j]);
+                               indentlevel = MYMAX(indentlevel, 0);
+                               if (columntype == COLUMN_TYPE_TREE)
+                                       row->indent = indentlevel;
+
+                               newcell.xmin = row->x + padding;
+                               newcell.xpos = newcell.xmin + indentlevel * width;
+                               newcell.xmax = newcell.xpos + content_width;
+                               newcell.content_index = 0;
+                               newcell.color_defined = !rows[i].colors.empty();
+                               if (newcell.color_defined)
+                                       newcell.color = rows[i].colors.back().first;
+                               row->cells.push_back(newcell);
+                               row->x = newcell.xmax;
+                       }
+               }
+       }
+
+       // Copy temporary rows to not so temporary rows
+       if (rowcount >= 1) {
+               m_rows.resize(rowcount);
+               for (s32 i = 0; i < rowcount; ++i) {
+                       Row *row = &m_rows[i];
+                       row->cellcount = rows[i].cells.size();
+                       row->cells = new Cell[row->cellcount];
+                       memcpy((void*) row->cells, (void*) &rows[i].cells[0],
+                                       row->cellcount * sizeof(Cell));
+                       row->indent = rows[i].indent;
+                       row->visible_index = i;
+                       m_visible_rows.push_back(i);
+               }
+       }
+
+       if (m_has_tree_column) {
+               // Treeview: convert tree to indent cells on leaf rows
+               for (s32 i = 0; i < rowcount; ++i) {
+                       if (i == rowcount-1 || m_rows[i].indent >= m_rows[i+1].indent)
+                               for (s32 j = 0; j < m_rows[i].cellcount; ++j)
+                                       if (m_rows[i].cells[j].content_type == COLUMN_TYPE_TREE)
+                                               m_rows[i].cells[j].content_type = COLUMN_TYPE_INDENT;
+               }
+
+               // Treeview: close rows according to opendepth option
+               std::set<s32> opened_trees;
+               for (s32 i = 0; i < rowcount; ++i)
+                       if (m_rows[i].indent < opendepth)
+                               opened_trees.insert(i);
+               setOpenedTrees(opened_trees);
+       }
+
+       // Delete temporary information used only during setTable()
+       delete[] rows;
+       allocationComplete();
+
+       // Clamp scroll bar position
+       updateScrollBar();
+}
+
+void GUITable::clear()
+{
+       // Clean up cells and rows
+       for (GUITable::Row &row : m_rows)
+               delete[] row.cells;
+       m_rows.clear();
+       m_visible_rows.clear();
+
+       // Get colors from skin
+       gui::IGUISkin *skin = Environment->getSkin();
+       m_color          = skin->getColor(gui::EGDC_BUTTON_TEXT);
+       m_background     = skin->getColor(gui::EGDC_3D_HIGH_LIGHT);
+       m_highlight      = skin->getColor(gui::EGDC_HIGH_LIGHT);
+       m_highlight_text = skin->getColor(gui::EGDC_HIGH_LIGHT_TEXT);
+
+       // Reset members
+       m_is_textlist = false;
+       m_has_tree_column = false;
+       m_selected = -1;
+       m_sel_column = 0;
+       m_sel_doubleclick = false;
+       m_keynav_time = 0;
+       m_keynav_buffer = L"";
+       m_border = true;
+       m_strings.clear();
+       m_images.clear();
+       m_alloc_strings.clear();
+       m_alloc_images.clear();
+}
+
+std::string GUITable::checkEvent()
+{
+       s32 sel = getSelected();
+       assert(sel >= 0);
+
+       if (sel == 0) {
+               return "INV";
+       }
+
+       std::ostringstream os(std::ios::binary);
+       if (m_sel_doubleclick) {
+               os<<"DCL:";
+               m_sel_doubleclick = false;
+       }
+       else {
+               os<<"CHG:";
+       }
+       os<<sel;
+       if (!m_is_textlist) {
+               os<<":"<<m_sel_column;
+       }
+       return os.str();
+}
+
+s32 GUITable::getSelected() const
+{
+       if (m_selected < 0)
+               return 0;
+
+       assert(m_selected >= 0 && m_selected < (s32) m_visible_rows.size());
+       return m_visible_rows[m_selected] + 1;
+}
+
+void GUITable::setSelected(s32 index)
+{
+       s32 old_selected = m_selected;
+
+       m_selected = -1;
+       m_sel_column = 0;
+       m_sel_doubleclick = false;
+
+       --index; // Switch from 1-based indexing to 0-based indexing
+
+       s32 rowcount = m_rows.size();
+       if (rowcount == 0 || index < 0) {
+               return;
+       }
+
+       if (index >= rowcount) {
+               index = rowcount - 1;
+       }
+
+       // If the selected row is not visible, open its ancestors to make it visible
+       bool selection_invisible = m_rows[index].visible_index < 0;
+       if (selection_invisible) {
+               std::set<s32> opened_trees;
+               getOpenedTrees(opened_trees);
+               s32 indent = m_rows[index].indent;
+               for (s32 j = index - 1; j >= 0; --j) {
+                       if (m_rows[j].indent < indent) {
+                               opened_trees.insert(j);
+                               indent = m_rows[j].indent;
+                       }
+               }
+               setOpenedTrees(opened_trees);
+       }
+
+       if (index >= 0) {
+               m_selected = m_rows[index].visible_index;
+               assert(m_selected >= 0 && m_selected < (s32) m_visible_rows.size());
+       }
+
+       if (m_selected != old_selected || selection_invisible) {
+               autoScroll();
+       }
+}
+
+GUITable::DynamicData GUITable::getDynamicData() const
+{
+       DynamicData dyndata;
+       dyndata.selected = getSelected();
+       dyndata.scrollpos = m_scrollbar->getPos();
+       dyndata.keynav_time = m_keynav_time;
+       dyndata.keynav_buffer = m_keynav_buffer;
+       if (m_has_tree_column)
+               getOpenedTrees(dyndata.opened_trees);
+       return dyndata;
+}
+
+void GUITable::setDynamicData(const DynamicData &dyndata)
+{
+       if (m_has_tree_column)
+               setOpenedTrees(dyndata.opened_trees);
+
+       m_keynav_time = dyndata.keynav_time;
+       m_keynav_buffer = dyndata.keynav_buffer;
+
+       setSelected(dyndata.selected);
+       m_sel_column = 0;
+       m_sel_doubleclick = false;
+
+       m_scrollbar->setPos(dyndata.scrollpos);
+}
+
+const c8* GUITable::getTypeName() const
+{
+       return "GUITable";
+}
+
+void GUITable::updateAbsolutePosition()
+{
+       IGUIElement::updateAbsolutePosition();
+       updateScrollBar();
+}
+
+void GUITable::draw()
+{
+       if (!IsVisible)
+               return;
+
+       gui::IGUISkin *skin = Environment->getSkin();
+
+       // draw background
+
+       bool draw_background = m_background.getAlpha() > 0;
+       if (m_border)
+               skin->draw3DSunkenPane(this, m_background,
+                               true, draw_background,
+                               AbsoluteRect, &AbsoluteClippingRect);
+       else if (draw_background)
+               skin->draw2DRectangle(this, m_background,
+                               AbsoluteRect, &AbsoluteClippingRect);
+
+       // get clipping rect
+
+       core::rect<s32> client_clip(AbsoluteRect);
+       client_clip.UpperLeftCorner.Y += 1;
+       client_clip.UpperLeftCorner.X += 1;
+       client_clip.LowerRightCorner.Y -= 1;
+       client_clip.LowerRightCorner.X -= 1;
+       if (m_scrollbar->isVisible()) {
+               client_clip.LowerRightCorner.X =
+                               m_scrollbar->getAbsolutePosition().UpperLeftCorner.X;
+       }
+       client_clip.clipAgainst(AbsoluteClippingRect);
+
+       // draw visible rows
+
+       s32 scrollpos = m_scrollbar->getPos();
+       s32 row_min = scrollpos / m_rowheight;
+       s32 row_max = (scrollpos + AbsoluteRect.getHeight() - 1)
+                       / m_rowheight + 1;
+       row_max = MYMIN(row_max, (s32) m_visible_rows.size());
+
+       core::rect<s32> row_rect(AbsoluteRect);
+       if (m_scrollbar->isVisible())
+               row_rect.LowerRightCorner.X -=
+                       skin->getSize(gui::EGDS_SCROLLBAR_SIZE);
+       row_rect.UpperLeftCorner.Y += row_min * m_rowheight - scrollpos;
+       row_rect.LowerRightCorner.Y = row_rect.UpperLeftCorner.Y + m_rowheight;
+
+       for (s32 i = row_min; i < row_max; ++i) {
+               Row *row = &m_rows[m_visible_rows[i]];
+               bool is_sel = i == m_selected;
+               video::SColor color = m_color;
+
+               if (is_sel) {
+                       skin->draw2DRectangle(this, m_highlight, row_rect, &client_clip);
+                       color = m_highlight_text;
+               }
+
+               for (s32 j = 0; j < row->cellcount; ++j)
+                       drawCell(&row->cells[j], color, row_rect, client_clip);
+
+               row_rect.UpperLeftCorner.Y += m_rowheight;
+               row_rect.LowerRightCorner.Y += m_rowheight;
+       }
+
+       // Draw children
+       IGUIElement::draw();
+}
+
+void GUITable::drawCell(const Cell *cell, video::SColor color,
+               const core::rect<s32> &row_rect,
+               const core::rect<s32> &client_clip)
+{
+       if ((cell->content_type == COLUMN_TYPE_TEXT)
+                       || (cell->content_type == COLUMN_TYPE_TREE)) {
+
+               core::rect<s32> text_rect = row_rect;
+               text_rect.UpperLeftCorner.X = row_rect.UpperLeftCorner.X
+                               + cell->xpos;
+               text_rect.LowerRightCorner.X = row_rect.UpperLeftCorner.X
+                               + cell->xmax;
+
+               if (cell->color_defined)
+                       color = cell->color;
+
+               if (m_font) {
+                       if (cell->content_type == COLUMN_TYPE_TEXT)
+                               m_font->draw(m_strings[cell->content_index],
+                                               text_rect, color,
+                                               false, true, &client_clip);
+                       else // tree
+                               m_font->draw(cell->content_index ? L"+" : L"-",
+                                               text_rect, color,
+                                               false, true, &client_clip);
+               }
+       }
+       else if (cell->content_type == COLUMN_TYPE_IMAGE) {
+
+               if (cell->content_index < 0)
+                       return;
+
+               video::IVideoDriver *driver = Environment->getVideoDriver();
+               video::ITexture *image = m_images[cell->content_index];
+
+               if (image) {
+                       core::position2d<s32> dest_pos =
+                                       row_rect.UpperLeftCorner;
+                       dest_pos.X += cell->xpos;
+                       core::rect<s32> source_rect(
+                                       core::position2d<s32>(0, 0),
+                                       image->getOriginalSize());
+                       s32 imgh = source_rect.LowerRightCorner.Y;
+                       s32 rowh = row_rect.getHeight();
+                       if (imgh < rowh)
+                               dest_pos.Y += (rowh - imgh) / 2;
+                       else
+                               source_rect.LowerRightCorner.Y = rowh;
+
+                       video::SColor color(255, 255, 255, 255);
+
+                       driver->draw2DImage(image, dest_pos, source_rect,
+                                       &client_clip, color, true);
+               }
+       }
+}
+
+bool GUITable::OnEvent(const SEvent &event)
+{
+       if (!isEnabled())
+               return IGUIElement::OnEvent(event);
+
+       if (event.EventType == EET_KEY_INPUT_EVENT) {
+               if (event.KeyInput.PressedDown && (
+                               event.KeyInput.Key == KEY_DOWN ||
+                               event.KeyInput.Key == KEY_UP   ||
+                               event.KeyInput.Key == KEY_HOME ||
+                               event.KeyInput.Key == KEY_END  ||
+                               event.KeyInput.Key == KEY_NEXT ||
+                               event.KeyInput.Key == KEY_PRIOR)) {
+                       s32 offset = 0;
+                       switch (event.KeyInput.Key) {
+                               case KEY_DOWN:
+                                       offset = 1;
+                                       break;
+                               case KEY_UP:
+                                       offset = -1;
+                                       break;
+                               case KEY_HOME:
+                                       offset = - (s32) m_visible_rows.size();
+                                       break;
+                               case KEY_END:
+                                       offset = m_visible_rows.size();
+                                       break;
+                               case KEY_NEXT:
+                                       offset = AbsoluteRect.getHeight() / m_rowheight;
+                                       break;
+                               case KEY_PRIOR:
+                                       offset = - (s32) (AbsoluteRect.getHeight() / m_rowheight);
+                                       break;
+                               default:
+                                       break;
+                       }
+                       s32 old_selected = m_selected;
+                       s32 rowcount = m_visible_rows.size();
+                       if (rowcount != 0) {
+                               m_selected = rangelim(m_selected + offset, 0, rowcount-1);
+                               autoScroll();
+                       }
+
+                       if (m_selected != old_selected)
+                               sendTableEvent(0, false);
+
+                       return true;
+               }
+
+               if (event.KeyInput.PressedDown && (
+                               event.KeyInput.Key == KEY_LEFT ||
+                               event.KeyInput.Key == KEY_RIGHT)) {
+                       // Open/close subtree via keyboard
+                       if (m_selected >= 0) {
+                               int dir = event.KeyInput.Key == KEY_LEFT ? -1 : 1;
+                               toggleVisibleTree(m_selected, dir, true);
+                       }
+                       return true;
+               }
+               else if (!event.KeyInput.PressedDown && (
+                               event.KeyInput.Key == KEY_RETURN ||
+                               event.KeyInput.Key == KEY_SPACE)) {
+                       sendTableEvent(0, true);
+                       return true;
+               }
+               else if (event.KeyInput.Key == KEY_ESCAPE ||
+                               event.KeyInput.Key == KEY_SPACE) {
+                       // pass to parent
+               }
+               else if (event.KeyInput.PressedDown && event.KeyInput.Char) {
+                       // change selection based on text as it is typed
+                       u64 now = porting::getTimeMs();
+                       if (now - m_keynav_time >= 500)
+                               m_keynav_buffer = L"";
+                       m_keynav_time = now;
+
+                       // add to key buffer if not a key repeat
+                       if (!(m_keynav_buffer.size() == 1 &&
+                                       m_keynav_buffer[0] == event.KeyInput.Char)) {
+                               m_keynav_buffer.append(event.KeyInput.Char);
+                       }
+
+                       // find the selected item, starting at the current selection
+                       // don't change selection if the key buffer matches the current item
+                       s32 old_selected = m_selected;
+                       s32 start = MYMAX(m_selected, 0);
+                       s32 rowcount = m_visible_rows.size();
+                       for (s32 k = 1; k < rowcount; ++k) {
+                               s32 current = start + k;
+                               if (current >= rowcount)
+                                       current -= rowcount;
+                               if (doesRowStartWith(getRow(current), m_keynav_buffer)) {
+                                       m_selected = current;
+                                       break;
+                               }
+                       }
+                       autoScroll();
+                       if (m_selected != old_selected)
+                               sendTableEvent(0, false);
+
+                       return true;
+               }
+       }
+       if (event.EventType == EET_MOUSE_INPUT_EVENT) {
+               core::position2d<s32> p(event.MouseInput.X, event.MouseInput.Y);
+
+               if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
+                       m_scrollbar->setPos(m_scrollbar->getPos() +
+                                       (event.MouseInput.Wheel < 0 ? -3 : 3) *
+                                       - (s32) m_rowheight / 2);
+                       return true;
+               }
+
+               // Find hovered row and cell
+               bool really_hovering = false;
+               s32 row_i = getRowAt(p.Y, really_hovering);
+               const Cell *cell = NULL;
+               if (really_hovering) {
+                       s32 cell_j = getCellAt(p.X, row_i);
+                       if (cell_j >= 0)
+                               cell = &(getRow(row_i)->cells[cell_j]);
+               }
+
+               // Update tooltip
+               setToolTipText(cell ? m_strings[cell->tooltip_index].c_str() : L"");
+
+               // Fix for #1567/#1806:
+               // IGUIScrollBar passes double click events to its parent,
+               // which we don't want. Detect this case and discard the event
+               if (event.MouseInput.Event != EMIE_MOUSE_MOVED &&
+                               m_scrollbar->isVisible() &&
+                               m_scrollbar->isPointInside(p))
+                       return true;
+
+               if (event.MouseInput.isLeftPressed() &&
+                               (isPointInside(p) ||
+                                event.MouseInput.Event == EMIE_MOUSE_MOVED)) {
+                       s32 sel_column = 0;
+                       bool sel_doubleclick = (event.MouseInput.Event
+                                       == EMIE_LMOUSE_DOUBLE_CLICK);
+                       bool plusminus_clicked = false;
+
+                       // For certain events (left click), report column
+                       // Also open/close subtrees when the +/- is clicked
+                       if (cell && (
+                                       event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN ||
+                                       event.MouseInput.Event == EMIE_LMOUSE_DOUBLE_CLICK ||
+                                       event.MouseInput.Event == EMIE_LMOUSE_TRIPLE_CLICK)) {
+                               sel_column = cell->reported_column;
+                               if (cell->content_type == COLUMN_TYPE_TREE)
+                                       plusminus_clicked = true;
+                       }
+
+                       if (plusminus_clicked) {
+                               if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
+                                       toggleVisibleTree(row_i, 0, false);
+                               }
+                       }
+                       else {
+                               // Normal selection
+                               s32 old_selected = m_selected;
+                               m_selected = row_i;
+                               autoScroll();
+
+                               if (m_selected != old_selected ||
+                                               sel_column >= 1 ||
+                                               sel_doubleclick) {
+                                       sendTableEvent(sel_column, sel_doubleclick);
+                               }
+
+                               // Treeview: double click opens/closes trees
+                               if (m_has_tree_column && sel_doubleclick) {
+                                       toggleVisibleTree(m_selected, 0, false);
+                               }
+                       }
+               }
+               return true;
+       }
+       if (event.EventType == EET_GUI_EVENT &&
+                       event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED &&
+                       event.GUIEvent.Caller == m_scrollbar) {
+               // Don't pass events from our scrollbar to the parent
+               return true;
+       }
+
+       return IGUIElement::OnEvent(event);
+}
+
+/******************************************************************************/
+/* GUITable helper functions                                                  */
+/******************************************************************************/
+
+s32 GUITable::allocString(const std::string &text)
+{
+       std::map<std::string, s32>::iterator it = m_alloc_strings.find(text);
+       if (it == m_alloc_strings.end()) {
+               s32 id = m_strings.size();
+               std::wstring wtext = utf8_to_wide(text);
+               m_strings.emplace_back(wtext.c_str());
+               m_alloc_strings.insert(std::make_pair(text, id));
+               return id;
+       }
+
+       return it->second;
+}
+
+s32 GUITable::allocImage(const std::string &imagename)
+{
+       std::map<std::string, s32>::iterator it = m_alloc_images.find(imagename);
+       if (it == m_alloc_images.end()) {
+               s32 id = m_images.size();
+               m_images.push_back(m_tsrc->getTexture(imagename));
+               m_alloc_images.insert(std::make_pair(imagename, id));
+               return id;
+       }
+
+       return it->second;
+}
+
+void GUITable::allocationComplete()
+{
+       // Called when done with creating rows and cells from table data,
+       // i.e. when allocString and allocImage won't be called anymore
+       m_alloc_strings.clear();
+       m_alloc_images.clear();
+}
+
+const GUITable::Row* GUITable::getRow(s32 i) const
+{
+       if (i >= 0 && i < (s32) m_visible_rows.size())
+               return &m_rows[m_visible_rows[i]];
+
+       return NULL;
+}
+
+bool GUITable::doesRowStartWith(const Row *row, const core::stringw &str) const
+{
+       if (row == NULL)
+               return false;
+
+       for (s32 j = 0; j < row->cellcount; ++j) {
+               Cell *cell = &row->cells[j];
+               if (cell->content_type == COLUMN_TYPE_TEXT) {
+                       const core::stringw &cellstr = m_strings[cell->content_index];
+                       if (cellstr.size() >= str.size() &&
+                                       str.equals_ignore_case(cellstr.subString(0, str.size())))
+                               return true;
+               }
+       }
+       return false;
+}
+
+s32 GUITable::getRowAt(s32 y, bool &really_hovering) const
+{
+       really_hovering = false;
+
+       s32 rowcount = m_visible_rows.size();
+       if (rowcount == 0)
+               return -1;
+
+       // Use arithmetic to find row
+       s32 rel_y = y - AbsoluteRect.UpperLeftCorner.Y - 1;
+       s32 i = (rel_y + m_scrollbar->getPos()) / m_rowheight;
+
+       if (i >= 0 && i < rowcount) {
+               really_hovering = true;
+               return i;
+       }
+       if (i < 0)
+               return 0;
+
+       return rowcount - 1;
+}
+
+s32 GUITable::getCellAt(s32 x, s32 row_i) const
+{
+       const Row *row = getRow(row_i);
+       if (row == NULL)
+               return -1;
+
+       // Use binary search to find cell in row
+       s32 rel_x = x - AbsoluteRect.UpperLeftCorner.X - 1;
+       s32 jmin = 0;
+       s32 jmax = row->cellcount - 1;
+       while (jmin < jmax) {
+               s32 pivot = jmin + (jmax - jmin) / 2;
+               assert(pivot >= 0 && pivot < row->cellcount);
+               const Cell *cell = &row->cells[pivot];
+
+               if (rel_x >= cell->xmin && rel_x <= cell->xmax)
+                       return pivot;
+
+               if (rel_x < cell->xmin)
+                       jmax = pivot - 1;
+               else
+                       jmin = pivot + 1;
+       }
+
+       if (jmin >= 0 && jmin < row->cellcount &&
+                       rel_x >= row->cells[jmin].xmin &&
+                       rel_x <= row->cells[jmin].xmax)
+               return jmin;
+
+       return -1;
+}
+
+void GUITable::autoScroll()
+{
+       if (m_selected >= 0) {
+               s32 pos = m_scrollbar->getPos();
+               s32 maxpos = m_selected * m_rowheight;
+               s32 minpos = maxpos - (AbsoluteRect.getHeight() - m_rowheight);
+               if (pos > maxpos)
+                       m_scrollbar->setPos(maxpos);
+               else if (pos < minpos)
+                       m_scrollbar->setPos(minpos);
+       }
+}
+
+void GUITable::updateScrollBar()
+{
+       s32 totalheight = m_rowheight * m_visible_rows.size();
+       s32 scrollmax = MYMAX(0, totalheight - AbsoluteRect.getHeight());
+       m_scrollbar->setVisible(scrollmax > 0);
+       m_scrollbar->setMax(scrollmax);
+       m_scrollbar->setSmallStep(m_rowheight);
+       m_scrollbar->setLargeStep(2 * m_rowheight);
+}
+
+void GUITable::sendTableEvent(s32 column, bool doubleclick)
+{
+       m_sel_column = column;
+       m_sel_doubleclick = doubleclick;
+       if (Parent) {
+               SEvent e;
+               memset(&e, 0, sizeof e);
+               e.EventType = EET_GUI_EVENT;
+               e.GUIEvent.Caller = this;
+               e.GUIEvent.Element = 0;
+               e.GUIEvent.EventType = gui::EGET_TABLE_CHANGED;
+               Parent->OnEvent(e);
+       }
+}
+
+void GUITable::getOpenedTrees(std::set<s32> &opened_trees) const
+{
+       opened_trees.clear();
+       s32 rowcount = m_rows.size();
+       for (s32 i = 0; i < rowcount - 1; ++i) {
+               if (m_rows[i].indent < m_rows[i+1].indent &&
+                               m_rows[i+1].visible_index != -2)
+                       opened_trees.insert(i);
+       }
+}
+
+void GUITable::setOpenedTrees(const std::set<s32> &opened_trees)
+{
+       s32 old_selected = -1;
+       if (m_selected >= 0)
+               old_selected = m_visible_rows[m_selected];
+
+       std::vector<s32> parents;
+       std::vector<s32> closed_parents;
+
+       m_visible_rows.clear();
+
+       for (size_t i = 0; i < m_rows.size(); ++i) {
+               Row *row = &m_rows[i];
+
+               // Update list of ancestors
+               while (!parents.empty() && m_rows[parents.back()].indent >= row->indent)
+                       parents.pop_back();
+               while (!closed_parents.empty() &&
+                               m_rows[closed_parents.back()].indent >= row->indent)
+                       closed_parents.pop_back();
+
+               assert(closed_parents.size() <= parents.size());
+
+               if (closed_parents.empty()) {
+                       // Visible row
+                       row->visible_index = m_visible_rows.size();
+                       m_visible_rows.push_back(i);
+               }
+               else if (parents.back() == closed_parents.back()) {
+                       // Invisible row, direct parent is closed
+                       row->visible_index = -2;
+               }
+               else {
+                       // Invisible row, direct parent is open, some ancestor is closed
+                       row->visible_index = -1;
+               }
+
+               // If not a leaf, add to parents list
+               if (i < m_rows.size()-1 && row->indent < m_rows[i+1].indent) {
+                       parents.push_back(i);
+
+                       s32 content_index = 0; // "-", open
+                       if (opened_trees.count(i) == 0) {
+                               closed_parents.push_back(i);
+                               content_index = 1; // "+", closed
+                       }
+
+                       // Update all cells of type "tree"
+                       for (s32 j = 0; j < row->cellcount; ++j)
+                               if (row->cells[j].content_type == COLUMN_TYPE_TREE)
+                                       row->cells[j].content_index = content_index;
+               }
+       }
+
+       updateScrollBar();
+
+       // m_selected must be updated since it is a visible row index
+       if (old_selected >= 0)
+               m_selected = m_rows[old_selected].visible_index;
+}
+
+void GUITable::openTree(s32 to_open)
+{
+       std::set<s32> opened_trees;
+       getOpenedTrees(opened_trees);
+       opened_trees.insert(to_open);
+       setOpenedTrees(opened_trees);
+}
+
+void GUITable::closeTree(s32 to_close)
+{
+       std::set<s32> opened_trees;
+       getOpenedTrees(opened_trees);
+       opened_trees.erase(to_close);
+       setOpenedTrees(opened_trees);
+}
+
+// The following function takes a visible row index (hidden rows skipped)
+// dir: -1 = left (close), 0 = auto (toggle), 1 = right (open)
+void GUITable::toggleVisibleTree(s32 row_i, int dir, bool move_selection)
+{
+       // Check if the chosen tree is currently open
+       const Row *row = getRow(row_i);
+       if (row == NULL)
+               return;
+
+       bool was_open = false;
+       for (s32 j = 0; j < row->cellcount; ++j) {
+               if (row->cells[j].content_type == COLUMN_TYPE_TREE) {
+                       was_open = row->cells[j].content_index == 0;
+                       break;
+               }
+       }
+
+       // Check if the chosen tree should be opened
+       bool do_open = !was_open;
+       if (dir < 0)
+               do_open = false;
+       else if (dir > 0)
+               do_open = true;
+
+       // Close or open the tree; the heavy lifting is done by setOpenedTrees
+       if (was_open && !do_open)
+               closeTree(m_visible_rows[row_i]);
+       else if (!was_open && do_open)
+               openTree(m_visible_rows[row_i]);
+
+       // Change selected row if requested by caller,
+       // this is useful for keyboard navigation
+       if (move_selection) {
+               s32 sel = row_i;
+               if (was_open && do_open) {
+                       // Move selection to first child
+                       const Row *maybe_child = getRow(sel + 1);
+                       if (maybe_child && maybe_child->indent > row->indent)
+                               sel++;
+               }
+               else if (!was_open && !do_open) {
+                       // Move selection to parent
+                       assert(getRow(sel) != NULL);
+                       while (sel > 0 && getRow(sel - 1)->indent >= row->indent)
+                               sel--;
+                       sel--;
+                       if (sel < 0)  // was root already selected?
+                               sel = row_i;
+               }
+               if (sel != m_selected) {
+                       m_selected = sel;
+                       autoScroll();
+                       sendTableEvent(0, false);
+               }
+       }
+}
+
+void GUITable::alignContent(Cell *cell, s32 xmax, s32 content_width, s32 align)
+{
+       // requires that cell.xmin, cell.xmax are properly set
+       // align = 0: left aligned, 1: centered, 2: right aligned, 3: inline
+       if (align == 0) {
+               cell->xpos = cell->xmin;
+               cell->xmax = xmax;
+       }
+       else if (align == 1) {
+               cell->xpos = (cell->xmin + xmax - content_width) / 2;
+               cell->xmax = xmax;
+       }
+       else if (align == 2) {
+               cell->xpos = xmax - content_width;
+               cell->xmax = xmax;
+       }
+       else {
+               // inline alignment: the cells of the column don't have an aligned
+               // right border, the right border of each cell depends on the content
+               cell->xpos = cell->xmin;
+               cell->xmax = cell->xmin + content_width;
+       }
+}
diff --git a/src/gui/guiTable.h b/src/gui/guiTable.h
new file mode 100644 (file)
index 0000000..f9337ff
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+#include <iostream>
+
+#include "irrlichttypes_extrabloated.h"
+
+class ISimpleTextureSource;
+
+/*
+       A table GUI element for GUIFormSpecMenu.
+
+       Sends a EGET_TABLE_CHANGED event to the parent when
+       an item is selected or double-clicked.
+       Call checkEvent() to get info.
+
+       Credits: The interface and implementation of this class are (very)
+       loosely based on the Irrlicht classes CGUITable and CGUIListBox.
+       CGUITable and CGUIListBox are licensed under the Irrlicht license;
+       they are Copyright (C) 2002-2012 Nikolaus Gebhardt
+*/
+class GUITable : public gui::IGUIElement
+{
+public:
+       /*
+               Stores dynamic data that should be preserved
+               when updating a formspec
+       */
+       struct DynamicData
+       {
+               s32 selected = 0;
+               s32 scrollpos = 0;
+               s32 keynav_time = 0;
+               core::stringw keynav_buffer;
+               std::set<s32> opened_trees;
+       };
+
+       /*
+               An option of the form <name>=<value>
+       */
+       struct Option
+       {
+               std::string name;
+               std::string value;
+
+               Option(const std::string &name_, const std::string &value_) :
+                       name(name_),
+                       value(value_)
+               {}
+       };
+
+       /*
+               A list of options that concern the entire table
+       */
+       typedef std::vector<Option> TableOptions;
+
+       /*
+               A column with options
+       */
+       struct TableColumn
+       {
+               std::string type;
+               std::vector<Option> options;
+       };
+       typedef std::vector<TableColumn> TableColumns;
+
+
+       GUITable(gui::IGUIEnvironment *env,
+                       gui::IGUIElement *parent, s32 id,
+                       core::rect<s32> rectangle,
+                       ISimpleTextureSource *tsrc);
+
+       virtual ~GUITable();
+
+       /* Split a string of the form "name=value" into name and value */
+       static Option splitOption(const std::string &str);
+
+       /* Set textlist-like options, columns and data */
+       void setTextList(const std::vector<std::string> &content,
+                       bool transparent);
+
+       /* Set generic table options, columns and content */
+       // Adds empty strings to end of content if there is an incomplete row
+       void setTable(const TableOptions &options,
+                       const TableColumns &columns,
+                       std::vector<std::string> &content);
+
+       /* Clear the table */
+       void clear();
+
+       /* Get info about last event (string such as "CHG:1:2") */
+       // Call this after EGET_TABLE_CHANGED
+       std::string checkEvent();
+
+       /* Get index of currently selected row (first=1; 0 if none selected) */
+       s32 getSelected() const;
+
+       /* Set currently selected row (first=1; 0 if none selected) */
+       // If given index is not visible at the moment, select its parent
+       // Autoscroll to make the selected row fully visible
+       void setSelected(s32 index);
+
+       /* Get selection, scroll position and opened (sub)trees */
+       DynamicData getDynamicData() const;
+
+       /* Set selection, scroll position and opened (sub)trees */
+       void setDynamicData(const DynamicData &dyndata);
+
+       /* Returns "GUITable" */
+       virtual const c8* getTypeName() const;
+
+       /* Must be called when position or size changes */
+       virtual void updateAbsolutePosition();
+
+       /* Irrlicht draw method */
+       virtual void draw();
+
+       /* Irrlicht event handler */
+       virtual bool OnEvent(const SEvent &event);
+
+protected:
+       enum ColumnType {
+               COLUMN_TYPE_TEXT,
+               COLUMN_TYPE_IMAGE,
+               COLUMN_TYPE_COLOR,
+               COLUMN_TYPE_INDENT,
+               COLUMN_TYPE_TREE,
+       };
+
+       struct Cell {
+               s32 xmin;
+               s32 xmax;
+               s32 xpos;
+               ColumnType content_type;
+               s32 content_index;
+               s32 tooltip_index;
+               video::SColor color;
+               bool color_defined;
+               s32 reported_column;
+       };
+
+       struct Row {
+               Cell *cells;
+               s32 cellcount;
+               s32 indent;
+               // visible_index >= 0: is index of row in m_visible_rows
+               // visible_index == -1: parent open but other ancestor closed
+               // visible_index == -2: parent closed
+               s32 visible_index;
+       };
+
+       // Texture source
+       ISimpleTextureSource *m_tsrc;
+
+       // Table content (including hidden rows)
+       std::vector<Row> m_rows;
+       // Table content (only visible; indices into m_rows)
+       std::vector<s32> m_visible_rows;
+       bool m_is_textlist = false;
+       bool m_has_tree_column = false;
+
+       // Selection status
+       s32 m_selected = -1; // index of row (1...n), or 0 if none selected
+       s32 m_sel_column = 0;
+       bool m_sel_doubleclick = false;
+
+       // Keyboard navigation stuff
+       u64 m_keynav_time = 0;
+       core::stringw m_keynav_buffer = L"";
+
+       // Drawing and geometry information
+       bool m_border = true;
+       video::SColor m_color = video::SColor(255, 255, 255, 255);
+       video::SColor m_background = video::SColor(255, 0, 0, 0);
+       video::SColor m_highlight = video::SColor(255, 70, 100, 50);
+       video::SColor m_highlight_text = video::SColor(255, 255, 255, 255);
+       s32 m_rowheight = 1;
+       gui::IGUIFont *m_font = nullptr;
+       gui::IGUIScrollBar *m_scrollbar = nullptr;
+
+       // Allocated strings and images
+       std::vector<core::stringw> m_strings;
+       std::vector<video::ITexture*> m_images;
+       std::map<std::string, s32> m_alloc_strings;
+       std::map<std::string, s32> m_alloc_images;
+
+       s32 allocString(const std::string &text);
+       s32 allocImage(const std::string &imagename);
+       void allocationComplete();
+
+       // Helper for draw() that draws a single cell
+       void drawCell(const Cell *cell, video::SColor color,
+                       const core::rect<s32> &rowrect,
+                       const core::rect<s32> &client_clip);
+
+       // Returns the i-th visible row (NULL if i is invalid)
+       const Row *getRow(s32 i) const;
+
+       // Key navigation helper
+       bool doesRowStartWith(const Row *row, const core::stringw &str) const;
+
+       // Returns the row at a given screen Y coordinate
+       // Returns index i such that m_rows[i] is valid (or -1 on error)
+       s32 getRowAt(s32 y, bool &really_hovering) const;
+
+       // Returns the cell at a given screen X coordinate within m_rows[row_i]
+       // Returns index j such that m_rows[row_i].cells[j] is valid
+       // (or -1 on error)
+       s32 getCellAt(s32 x, s32 row_i) const;
+
+       // Make the selected row fully visible
+       void autoScroll();
+
+       // Should be called when m_rowcount or m_rowheight changes
+       void updateScrollBar();
+
+       // Sends EET_GUI_EVENT / EGET_TABLE_CHANGED to parent
+       void sendTableEvent(s32 column, bool doubleclick);
+
+       // Functions that help deal with hidden rows
+       // The following functions take raw row indices (hidden rows not skipped)
+       void getOpenedTrees(std::set<s32> &opened_trees) const;
+       void setOpenedTrees(const std::set<s32> &opened_trees);
+       void openTree(s32 to_open);
+       void closeTree(s32 to_close);
+       // The following function takes a visible row index (hidden rows skipped)
+       // dir: -1 = left (close), 0 = auto (toggle), 1 = right (open)
+       void toggleVisibleTree(s32 row_i, int dir, bool move_selection);
+
+       // Aligns cell content in column according to alignment specification
+       // align = 0: left aligned, 1: centered, 2: right aligned, 3: inline
+       static void alignContent(Cell *cell, s32 xmax, s32 content_width,
+                       s32 align);
+};
diff --git a/src/gui/guiVolumeChange.cpp b/src/gui/guiVolumeChange.cpp
new file mode 100644 (file)
index 0000000..8c46231
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+Part of Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
+Copyright (C) 2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "guiVolumeChange.h"
+#include "debug.h"
+#include "serialization.h"
+#include <string>
+#include <IGUICheckBox.h>
+#include <IGUIButton.h>
+#include <IGUIScrollBar.h>
+#include <IGUIStaticText.h>
+#include <IGUIFont.h>
+#include "settings.h"
+
+#include "gettext.h"
+
+const int ID_soundText = 263;
+const int ID_soundExitButton = 264;
+const int ID_soundSlider = 265;
+const int ID_soundMuteButton = 266;
+
+GUIVolumeChange::GUIVolumeChange(gui::IGUIEnvironment* env,
+               gui::IGUIElement* parent, s32 id,
+               IMenuManager *menumgr
+):
+       GUIModalMenu(env, parent, id, menumgr)
+{
+}
+
+GUIVolumeChange::~GUIVolumeChange()
+{
+       removeChildren();
+}
+
+void GUIVolumeChange::removeChildren()
+{
+       if (gui::IGUIElement *e = getElementFromId(ID_soundText))
+               e->remove();
+
+       if (gui::IGUIElement *e = getElementFromId(ID_soundExitButton))
+               e->remove();
+
+       if (gui::IGUIElement *e = getElementFromId(ID_soundSlider))
+               e->remove();
+}
+
+void GUIVolumeChange::regenerateGui(v2u32 screensize)
+{
+       /*
+               Remove stuff
+       */
+       removeChildren();
+
+       /*
+               Calculate new sizes and positions
+       */
+       DesiredRect = core::rect<s32>(
+               screensize.X/2 - 380/2,
+               screensize.Y/2 - 200/2,
+               screensize.X/2 + 380/2,
+               screensize.Y/2 + 200/2
+       );
+       recalculateAbsolutePosition(false);
+
+       v2s32 size = DesiredRect.getSize();
+       int volume = (int)(g_settings->getFloat("sound_volume") * 100);
+
+       /*
+               Add stuff
+       */
+       {
+               core::rect<s32> rect(0, 0, 160, 20);
+               rect = rect + v2s32(size.X / 2 - 80, size.Y / 2 - 70);
+
+               const wchar_t *text = wgettext("Sound Volume: ");
+               core::stringw volume_text = text;
+               delete [] text;
+
+               volume_text += core::stringw(volume) + core::stringw("%");
+               Environment->addStaticText(volume_text.c_str(), rect, false,
+                               true, this, ID_soundText);
+       }
+       {
+               core::rect<s32> rect(0, 0, 80, 30);
+               rect = rect + v2s32(size.X/2-80/2, size.Y/2+55);
+               const wchar_t *text = wgettext("Exit");
+               Environment->addButton(rect, this, ID_soundExitButton,
+                       text);
+               delete[] text;
+       }
+       {
+               core::rect<s32> rect(0, 0, 300, 20);
+               rect = rect + v2s32(size.X / 2 - 150, size.Y / 2);
+               gui::IGUIScrollBar *e = Environment->addScrollBar(true,
+                       rect, this, ID_soundSlider);
+               e->setMax(100);
+               e->setPos(volume);
+       }
+       {
+               core::rect<s32> rect(0, 0, 160, 20);
+               rect = rect + v2s32(size.X / 2 - 80, size.Y / 2 - 35);
+               const wchar_t *text = wgettext("Muted");
+               Environment->addCheckBox(g_settings->getBool("mute_sound"), rect, this,
+                               ID_soundMuteButton, text);
+               delete[] text;
+       }
+}
+
+void GUIVolumeChange::drawMenu()
+{
+       gui::IGUISkin* skin = Environment->getSkin();
+       if (!skin)
+               return;
+       video::IVideoDriver* driver = Environment->getVideoDriver();
+       video::SColor bgcolor(140, 0, 0, 0);
+       driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
+       gui::IGUIElement::draw();
+}
+
+bool GUIVolumeChange::OnEvent(const SEvent& event)
+{
+       if (event.EventType == EET_KEY_INPUT_EVENT) {
+               if (event.KeyInput.Key == KEY_ESCAPE && event.KeyInput.PressedDown) {
+                       quitMenu();
+                       return true;
+               }
+
+               if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
+                       quitMenu();
+                       return true;
+               }
+       } else if (event.EventType == EET_GUI_EVENT) {
+               if (event.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED) {
+                       gui::IGUIElement *e = getElementFromId(ID_soundMuteButton);
+                       if (e != NULL && e->getType() == gui::EGUIET_CHECK_BOX) {
+                               g_settings->setBool("mute_sound", ((gui::IGUICheckBox*)e)->isChecked());
+                       }
+
+                       Environment->setFocus(this);
+                       return true;
+               }
+
+               if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) {
+                       if (event.GUIEvent.Caller->getID() == ID_soundExitButton) {
+                               quitMenu();
+                               return true;
+                       }
+                       Environment->setFocus(this);
+               }
+
+               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_SCROLL_BAR_CHANGED) {
+                       if (event.GUIEvent.Caller->getID() == ID_soundSlider) {
+                               s32 pos = ((gui::IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
+                               g_settings->setFloat("sound_volume", (float) pos / 100);
+
+                               gui::IGUIElement *e = getElementFromId(ID_soundText);
+                               const wchar_t *text = wgettext("Sound Volume: ");
+                               core::stringw volume_text = text;
+                               delete [] text;
+
+                               volume_text += core::stringw(pos) + core::stringw("%");
+                               e->setText(volume_text.c_str());
+                               return true;
+                       }
+               }
+
+       }
+
+       return Parent ? Parent->OnEvent(event) : false;
+}
+
diff --git a/src/gui/guiVolumeChange.h b/src/gui/guiVolumeChange.h
new file mode 100644 (file)
index 0000000..7c7e19a
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+Part of Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
+Copyright (C) 2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#pragma once
+
+#include "irrlichttypes_extrabloated.h"
+#include "modalMenu.h"
+#include <string>
+
+class GUIVolumeChange : public GUIModalMenu
+{
+public:
+       GUIVolumeChange(gui::IGUIEnvironment* env,
+                       gui::IGUIElement* parent, s32 id,
+                       IMenuManager *menumgr);
+       ~GUIVolumeChange();
+
+       void removeChildren();
+       /*
+               Remove and re-add (or reposition) stuff
+       */
+       void regenerateGui(v2u32 screensize);
+
+       void drawMenu();
+
+       bool OnEvent(const SEvent& event);
+
+       bool pausesGame() { return true; }
+};
diff --git a/src/gui/intlGUIEditBox.cpp b/src/gui/intlGUIEditBox.cpp
new file mode 100644 (file)
index 0000000..279e7a4
--- /dev/null
@@ -0,0 +1,1601 @@
+// 11.11.2011 11:11 ValkaTR
+//
+// This is a copy of intlGUIEditBox from the irrlicht, but with a
+// fix in the OnEvent function, which doesn't allowed input of
+// other keyboard layouts than latin-1
+//
+// Characters like: ä ö ü õ ы й ю я ъ № € ° ...
+//
+// This fix is only needed for linux, because of a bug
+// in the CIrrDeviceLinux.cpp:1014-1015 of the irrlicht
+//
+// Also locale in the programm should not be changed to
+// a "C", "POSIX" or whatever, it should be set to "",
+// or XLookupString will return nothing for the international
+// characters.
+//
+// From the "man setlocale":
+//
+// On startup of the main program, the portable "C" locale
+// is selected as default.  A  program  may  be  made
+// portable to all locales by calling:
+//
+//           setlocale(LC_ALL, "");
+//
+//       after  program initialization....
+//
+
+// Copyright (C) 2002-2013 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#include <util/numeric.h>
+#include "intlGUIEditBox.h"
+
+#if defined(_IRR_COMPILE_WITH_GUI_) && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
+
+#include "IGUISkin.h"
+#include "IGUIEnvironment.h"
+#include "IGUIFont.h"
+#include "IVideoDriver.h"
+//#include "rect.h"
+//#include "irrlicht/os.cpp"
+#include "porting.h"
+//#include "Keycodes.h"
+#include "log.h"
+
+/*
+       todo:
+       optional scrollbars
+       ctrl+left/right to select word
+       double click/ctrl click: word select + drag to select whole words, triple click to select line
+       optional? dragging selected text
+       numerical
+*/
+
+namespace irr
+{
+namespace gui
+{
+
+//! constructor
+intlGUIEditBox::intlGUIEditBox(const wchar_t* text, bool border,
+               IGUIEnvironment* environment, IGUIElement* parent, s32 id,
+               const core::rect<s32>& rectangle, bool writable, bool has_vscrollbar)
+       : IGUIEditBox(environment, parent, id, rectangle),
+       Border(border), FrameRect(rectangle),
+       m_scrollbar_width(0), m_vscrollbar(NULL), m_writable(writable)
+{
+       #ifdef _DEBUG
+       setDebugName("intlintlGUIEditBox");
+       #endif
+
+       Text = text;
+
+       if (Environment)
+               Operator = Environment->getOSOperator();
+
+       if (Operator)
+               Operator->grab();
+
+       // this element can be tabbed to
+       setTabStop(true);
+       setTabOrder(-1);
+
+       IGUISkin *skin = 0;
+       if (Environment)
+               skin = Environment->getSkin();
+       if (Border && skin)
+       {
+               FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+               FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+               FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+               FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+       }
+
+       if (skin && has_vscrollbar) {
+               m_scrollbar_width = skin->getSize(gui::EGDS_SCROLLBAR_SIZE);
+
+               if (m_scrollbar_width > 0) {
+                       createVScrollBar();
+               }
+       }
+
+       breakText();
+
+       calculateScrollPos();
+       setWritable(writable);
+}
+
+
+//! destructor
+intlGUIEditBox::~intlGUIEditBox()
+{
+       if (OverrideFont)
+               OverrideFont->drop();
+
+       if (Operator)
+               Operator->drop();
+}
+
+
+//! Sets another skin independent font.
+void intlGUIEditBox::setOverrideFont(IGUIFont* font)
+{
+       if (OverrideFont == font)
+               return;
+
+       if (OverrideFont)
+               OverrideFont->drop();
+
+       OverrideFont = font;
+
+       if (OverrideFont)
+               OverrideFont->grab();
+
+       breakText();
+}
+
+IGUIFont * intlGUIEditBox::getOverrideFont() const
+{
+       return OverrideFont;
+}
+
+//! Get the font which is used right now for drawing
+IGUIFont* intlGUIEditBox::getActiveFont() const
+{
+       if ( OverrideFont )
+               return OverrideFont;
+       IGUISkin* skin = Environment->getSkin();
+       if (skin)
+               return skin->getFont();
+       return 0;
+}
+
+//! Sets another color for the text.
+void intlGUIEditBox::setOverrideColor(video::SColor color)
+{
+       OverrideColor = color;
+       OverrideColorEnabled = true;
+}
+
+video::SColor intlGUIEditBox::getOverrideColor() const
+{
+       return OverrideColor;
+}
+
+//! Turns the border on or off
+void intlGUIEditBox::setDrawBorder(bool border)
+{
+       Border = border;
+}
+
+//! Sets whether to draw the background
+void intlGUIEditBox::setDrawBackground(bool draw)
+{
+}
+
+//! Sets if the text should use the overide color or the color in the gui skin.
+void intlGUIEditBox::enableOverrideColor(bool enable)
+{
+       OverrideColorEnabled = enable;
+}
+
+bool intlGUIEditBox::isOverrideColorEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return OverrideColorEnabled;
+}
+
+//! Enables or disables word wrap
+void intlGUIEditBox::setWordWrap(bool enable)
+{
+       WordWrap = enable;
+       breakText();
+}
+
+
+void intlGUIEditBox::updateAbsolutePosition()
+{
+    core::rect<s32> oldAbsoluteRect(AbsoluteRect);
+       IGUIElement::updateAbsolutePosition();
+       if ( oldAbsoluteRect != AbsoluteRect )
+       {
+        breakText();
+       }
+}
+
+
+//! Checks if word wrap is enabled
+bool intlGUIEditBox::isWordWrapEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return WordWrap;
+}
+
+
+//! Enables or disables newlines.
+void intlGUIEditBox::setMultiLine(bool enable)
+{
+       MultiLine = enable;
+}
+
+
+//! Checks if multi line editing is enabled
+bool intlGUIEditBox::isMultiLineEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return MultiLine;
+}
+
+
+void intlGUIEditBox::setPasswordBox(bool passwordBox, wchar_t passwordChar)
+{
+       PasswordBox = passwordBox;
+       if (PasswordBox)
+       {
+               PasswordChar = passwordChar;
+               setMultiLine(false);
+               setWordWrap(false);
+               BrokenText.clear();
+       }
+}
+
+
+bool intlGUIEditBox::isPasswordBox() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return PasswordBox;
+}
+
+
+//! Sets text justification
+void intlGUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
+{
+       HAlign = horizontal;
+       VAlign = vertical;
+}
+
+
+//! called if an event happened.
+bool intlGUIEditBox::OnEvent(const SEvent& event)
+{
+       if (IsEnabled)
+       {
+
+               switch(event.EventType)
+               {
+               case EET_GUI_EVENT:
+                       if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
+                       {
+                               if (event.GUIEvent.Caller == this)
+                               {
+                                       MouseMarking = false;
+                                       setTextMarkers(0,0);
+                               }
+                       }
+                       break;
+               case EET_KEY_INPUT_EVENT:
+        {
+#if (defined(__linux__) || defined(__FreeBSD__))
+            // ################################################################
+                       // ValkaTR:
+            // This part is the difference from the original intlGUIEditBox
+            // It converts UTF-8 character into a UCS-2 (wchar_t)
+            wchar_t wc = L'_';
+            mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
+
+            //printf( "char: %lc (%u)  \r\n", wc, wc );
+
+            SEvent irrevent(event);
+            irrevent.KeyInput.Char = wc;
+            // ################################################################
+
+                       if (processKey(irrevent))
+                               return true;
+#else
+                       if (processKey(event))
+                               return true;
+#endif // defined(linux)
+
+                       break;
+        }
+               case EET_MOUSE_INPUT_EVENT:
+                       if (processMouse(event))
+                               return true;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return IGUIElement::OnEvent(event);
+}
+
+
+bool intlGUIEditBox::processKey(const SEvent& event)
+{
+       if (!event.KeyInput.PressedDown)
+               return false;
+
+       bool textChanged = false;
+       s32 newMarkBegin = MarkBegin;
+       s32 newMarkEnd = MarkEnd;
+
+       // control shortcut handling
+
+       if (event.KeyInput.Control)
+       {
+               // german backlash '\' entered with control + '?'
+               if ( event.KeyInput.Char == '\\' )
+               {
+                       inputChar(event.KeyInput.Char);
+                       return true;
+               }
+
+               switch(event.KeyInput.Key)
+               {
+               case KEY_KEY_A:
+                       // select all
+                       newMarkBegin = 0;
+                       newMarkEnd = Text.size();
+                       break;
+               case KEY_KEY_C:
+                       // copy to clipboard
+                       if (!PasswordBox && Operator && MarkBegin != MarkEnd)
+                       {
+                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+                               core::stringc s;
+                               s = Text.subString(realmbgn, realmend - realmbgn).c_str();
+                               Operator->copyToClipboard(s.c_str());
+                       }
+                       break;
+               case KEY_KEY_X:
+                       // cut to the clipboard
+                       if (!PasswordBox && Operator && MarkBegin != MarkEnd)
+                       {
+                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+                               // copy
+                               core::stringc sc;
+                               sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
+                               Operator->copyToClipboard(sc.c_str());
+
+                               if (IsEnabled)
+                               {
+                                       // delete
+                                       core::stringw s;
+                                       s = Text.subString(0, realmbgn);
+                                       s.append( Text.subString(realmend, Text.size()-realmend) );
+                                       Text = s;
+
+                                       CursorPos = realmbgn;
+                                       newMarkBegin = 0;
+                                       newMarkEnd = 0;
+                                       textChanged = true;
+                               }
+                       }
+                       break;
+               case KEY_KEY_V:
+                       if ( !IsEnabled )
+                               break;
+
+                       // paste from the clipboard
+                       if (Operator)
+                       {
+                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+                               // add new character
+                               const c8* p = Operator->getTextFromClipboard();
+                               if (p)
+                               {
+                                       if (MarkBegin == MarkEnd)
+                                       {
+                                               // insert text
+                                               core::stringw s = Text.subString(0, CursorPos);
+                                               s.append(p);
+                                               s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+
+                                               if (!Max || s.size()<=Max) // thx to Fish FH for fix
+                                               {
+                                                       Text = s;
+                                                       s = p;
+                                                       CursorPos += s.size();
+                                               }
+                                       }
+                                       else
+                                       {
+                                               // replace text
+
+                                               core::stringw s = Text.subString(0, realmbgn);
+                                               s.append(p);
+                                               s.append( Text.subString(realmend, Text.size()-realmend) );
+
+                                               if (!Max || s.size()<=Max)  // thx to Fish FH for fix
+                                               {
+                                                       Text = s;
+                                                       s = p;
+                                                       CursorPos = realmbgn + s.size();
+                                               }
+                                       }
+                               }
+
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                               textChanged = true;
+                       }
+                       break;
+               case KEY_HOME:
+                       // move/highlight to start of text
+                       if (event.KeyInput.Shift)
+                       {
+                               newMarkEnd = CursorPos;
+                               newMarkBegin = 0;
+                               CursorPos = 0;
+                       }
+                       else
+                       {
+                               CursorPos = 0;
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                       }
+                       break;
+               case KEY_END:
+                       // move/highlight to end of text
+                       if (event.KeyInput.Shift)
+                       {
+                               newMarkBegin = CursorPos;
+                               newMarkEnd = Text.size();
+                               CursorPos = 0;
+                       }
+                       else
+                       {
+                               CursorPos = Text.size();
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                       }
+                       break;
+               default:
+                       return false;
+               }
+       }
+       // default keyboard handling
+       else
+       switch(event.KeyInput.Key)
+       {
+       case KEY_END:
+               {
+                       s32 p = Text.size();
+                       if (WordWrap || MultiLine)
+                       {
+                               p = getLineFromPos(CursorPos);
+                               p = BrokenTextPositions[p] + (s32)BrokenText[p].size();
+                               if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' ))
+                                       p-=1;
+                       }
+
+                       if (event.KeyInput.Shift)
+                       {
+                               if (MarkBegin == MarkEnd)
+                                       newMarkBegin = CursorPos;
+
+                               newMarkEnd = p;
+                       }
+                       else
+                       {
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                       }
+                       CursorPos = p;
+                       BlinkStartTime = porting::getTimeMs();
+               }
+               break;
+       case KEY_HOME:
+               {
+
+                       s32 p = 0;
+                       if (WordWrap || MultiLine)
+                       {
+                               p = getLineFromPos(CursorPos);
+                               p = BrokenTextPositions[p];
+                       }
+
+                       if (event.KeyInput.Shift)
+                       {
+                               if (MarkBegin == MarkEnd)
+                                       newMarkBegin = CursorPos;
+                               newMarkEnd = p;
+                       }
+                       else
+                       {
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                       }
+                       CursorPos = p;
+                       BlinkStartTime = porting::getTimeMs();
+               }
+               break;
+       case KEY_RETURN:
+               if (MultiLine)
+               {
+                       inputChar(L'\n');
+                       return true;
+               }
+               else
+               {
+                   sendGuiEvent( EGET_EDITBOX_ENTER );
+               }
+               break;
+       case KEY_LEFT:
+
+               if (event.KeyInput.Shift)
+               {
+                       if (CursorPos > 0)
+                       {
+                               if (MarkBegin == MarkEnd)
+                                       newMarkBegin = CursorPos;
+
+                               newMarkEnd = CursorPos-1;
+                       }
+               }
+               else
+               {
+                       newMarkBegin = 0;
+                       newMarkEnd = 0;
+               }
+
+               if (CursorPos > 0) CursorPos--;
+               BlinkStartTime = porting::getTimeMs();
+               break;
+
+       case KEY_RIGHT:
+               if (event.KeyInput.Shift)
+               {
+                       if (Text.size() > (u32)CursorPos)
+                       {
+                               if (MarkBegin == MarkEnd)
+                                       newMarkBegin = CursorPos;
+
+                               newMarkEnd = CursorPos+1;
+                       }
+               }
+               else
+               {
+                       newMarkBegin = 0;
+                       newMarkEnd = 0;
+               }
+
+               if (Text.size() > (u32)CursorPos) CursorPos++;
+               BlinkStartTime = porting::getTimeMs();
+               break;
+       case KEY_UP:
+               if (MultiLine || (WordWrap && BrokenText.size() > 1) )
+               {
+                       s32 lineNo = getLineFromPos(CursorPos);
+                       s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd);
+                       if (lineNo > 0)
+                       {
+                               s32 cp = CursorPos - BrokenTextPositions[lineNo];
+                               if ((s32)BrokenText[lineNo-1].size() < cp)
+                                       CursorPos = BrokenTextPositions[lineNo-1] + (s32)BrokenText[lineNo-1].size()-1;
+                               else
+                                       CursorPos = BrokenTextPositions[lineNo-1] + cp;
+                       }
+
+                       if (event.KeyInput.Shift)
+                       {
+                               newMarkBegin = mb;
+                               newMarkEnd = CursorPos;
+                       }
+                       else
+                       {
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                       }
+
+               }
+               else
+               {
+                       return false;
+               }
+               break;
+       case KEY_DOWN:
+               if (MultiLine || (WordWrap && BrokenText.size() > 1) )
+               {
+                       s32 lineNo = getLineFromPos(CursorPos);
+                       s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd);
+                       if (lineNo < (s32)BrokenText.size()-1)
+                       {
+                               s32 cp = CursorPos - BrokenTextPositions[lineNo];
+                               if ((s32)BrokenText[lineNo+1].size() < cp)
+                                       CursorPos = BrokenTextPositions[lineNo+1] + BrokenText[lineNo+1].size()-1;
+                               else
+                                       CursorPos = BrokenTextPositions[lineNo+1] + cp;
+                       }
+
+                       if (event.KeyInput.Shift)
+                       {
+                               newMarkBegin = mb;
+                               newMarkEnd = CursorPos;
+                       }
+                       else
+                       {
+                               newMarkBegin = 0;
+                               newMarkEnd = 0;
+                       }
+
+               }
+               else
+               {
+                       return false;
+               }
+               break;
+
+       case KEY_BACK:
+               if ( !this->IsEnabled )
+                       break;
+
+               if (!Text.empty()) {
+                       core::stringw s;
+
+                       if (MarkBegin != MarkEnd)
+                       {
+                               // delete marked text
+                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+                               s = Text.subString(0, realmbgn);
+                               s.append( Text.subString(realmend, Text.size()-realmend) );
+                               Text = s;
+
+                               CursorPos = realmbgn;
+                       }
+                       else
+                       {
+                               // delete text behind cursor
+                               if (CursorPos>0)
+                                       s = Text.subString(0, CursorPos-1);
+                               else
+                                       s = L"";
+                               s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+                               Text = s;
+                               --CursorPos;
+                       }
+
+                       if (CursorPos < 0)
+                               CursorPos = 0;
+                       BlinkStartTime = porting::getTimeMs();
+                       newMarkBegin = 0;
+                       newMarkEnd = 0;
+                       textChanged = true;
+               }
+               break;
+       case KEY_DELETE:
+               if ( !this->IsEnabled )
+                       break;
+
+               if (!Text.empty()) {
+                       core::stringw s;
+
+                       if (MarkBegin != MarkEnd)
+                       {
+                               // delete marked text
+                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+                               s = Text.subString(0, realmbgn);
+                               s.append( Text.subString(realmend, Text.size()-realmend) );
+                               Text = s;
+
+                               CursorPos = realmbgn;
+                       }
+                       else
+                       {
+                               // delete text before cursor
+                               s = Text.subString(0, CursorPos);
+                               s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) );
+                               Text = s;
+                       }
+
+                       if (CursorPos > (s32)Text.size())
+                               CursorPos = (s32)Text.size();
+
+                       BlinkStartTime = porting::getTimeMs();
+                       newMarkBegin = 0;
+                       newMarkEnd = 0;
+                       textChanged = true;
+               }
+               break;
+
+       case KEY_ESCAPE:
+       case KEY_TAB:
+       case KEY_SHIFT:
+       case KEY_F1:
+       case KEY_F2:
+       case KEY_F3:
+       case KEY_F4:
+       case KEY_F5:
+       case KEY_F6:
+       case KEY_F7:
+       case KEY_F8:
+       case KEY_F9:
+       case KEY_F10:
+       case KEY_F11:
+       case KEY_F12:
+       case KEY_F13:
+       case KEY_F14:
+       case KEY_F15:
+       case KEY_F16:
+       case KEY_F17:
+       case KEY_F18:
+       case KEY_F19:
+       case KEY_F20:
+       case KEY_F21:
+       case KEY_F22:
+       case KEY_F23:
+       case KEY_F24:
+               // ignore these keys
+               return false;
+
+       default:
+               inputChar(event.KeyInput.Char);
+               return true;
+       }
+
+    // Set new text markers
+    setTextMarkers( newMarkBegin, newMarkEnd );
+
+       // break the text if it has changed
+       if (textChanged)
+       {
+               breakText();
+               sendGuiEvent(EGET_EDITBOX_CHANGED);
+       }
+
+       calculateScrollPos();
+
+       return true;
+}
+
+
+//! draws the element and its children
+void intlGUIEditBox::draw()
+{
+       if (!IsVisible)
+               return;
+
+       const bool focus = Environment->hasFocus(this);
+
+       IGUISkin* skin = Environment->getSkin();
+       if (!skin)
+               return;
+
+       FrameRect = AbsoluteRect;
+
+       // draw the border
+
+       if (Border)
+       {
+               if (m_writable) {
+                       skin->draw3DSunkenPane(this, skin->getColor(EGDC_WINDOW),
+                               false, true, FrameRect, &AbsoluteClippingRect);
+               }
+
+               FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+               FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+               FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
+               FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
+       }
+
+       updateVScrollBar();
+       core::rect<s32> localClipRect = FrameRect;
+       localClipRect.clipAgainst(AbsoluteClippingRect);
+
+       // draw the text
+
+       IGUIFont* font = OverrideFont;
+       if (!OverrideFont)
+               font = skin->getFont();
+
+       s32 cursorLine = 0;
+       s32 charcursorpos = 0;
+
+       if (font)
+       {
+               if (LastBreakFont != font)
+               {
+                       breakText();
+               }
+
+               // calculate cursor pos
+
+               core::stringw *txtLine = &Text;
+               s32 startPos = 0;
+
+               core::stringw s, s2;
+
+               // get mark position
+               const bool ml = (!PasswordBox && (WordWrap || MultiLine));
+               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+               const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0;
+               const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1;
+               const s32 lineCount = ml ? BrokenText.size() : 1;
+
+               // Save the override color information.
+               // Then, alter it if the edit box is disabled.
+               const bool prevOver = OverrideColorEnabled;
+               const video::SColor prevColor = OverrideColor;
+
+               if (!Text.empty()) {
+                       if (!IsEnabled && !OverrideColorEnabled)
+                       {
+                               OverrideColorEnabled = true;
+                               OverrideColor = skin->getColor(EGDC_GRAY_TEXT);
+                       }
+
+                       for (s32 i=0; i < lineCount; ++i)
+                       {
+                               setTextRect(i);
+
+                               // clipping test - don't draw anything outside the visible area
+                               core::rect<s32> c = localClipRect;
+                               c.clipAgainst(CurrentTextRect);
+                               if (!c.isValid())
+                                       continue;
+
+                               // get current line
+                               if (PasswordBox)
+                               {
+                                       if (BrokenText.size() != 1)
+                                       {
+                                               BrokenText.clear();
+                                               BrokenText.push_back(core::stringw());
+                                       }
+                                       if (BrokenText[0].size() != Text.size())
+                                       {
+                                               BrokenText[0] = Text;
+                                               for (u32 q = 0; q < Text.size(); ++q)
+                                               {
+                                                       BrokenText[0] [q] = PasswordChar;
+                                               }
+                                       }
+                                       txtLine = &BrokenText[0];
+                                       startPos = 0;
+                               }
+                               else
+                               {
+                                       txtLine = ml ? &BrokenText[i] : &Text;
+                                       startPos = ml ? BrokenTextPositions[i] : 0;
+                               }
+
+
+                               // draw normal text
+                               font->draw(txtLine->c_str(), CurrentTextRect,
+                                       OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
+                                       false, true, &localClipRect);
+
+                               // draw mark and marked text
+                               if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)
+                               {
+
+                                       s32 mbegin = 0, mend = 0;
+                                       s32 lineStartPos = 0, lineEndPos = txtLine->size();
+
+                                       if (i == hlineStart)
+                                       {
+                                               // highlight start is on this line
+                                               s = txtLine->subString(0, realmbgn - startPos);
+                                               mbegin = font->getDimension(s.c_str()).Width;
+
+                                               // deal with kerning
+                                               mbegin += font->getKerningWidth(
+                                                       &((*txtLine)[realmbgn - startPos]),
+                                                       realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0);
+
+                                               lineStartPos = realmbgn - startPos;
+                                       }
+                                       if (i == hlineStart + hlineCount - 1)
+                                       {
+                                               // highlight end is on this line
+                                               s2 = txtLine->subString(0, realmend - startPos);
+                                               mend = font->getDimension(s2.c_str()).Width;
+                                               lineEndPos = (s32)s2.size();
+                                       }
+                                       else
+                                               mend = font->getDimension(txtLine->c_str()).Width;
+
+                                       CurrentTextRect.UpperLeftCorner.X += mbegin;
+                                       CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin;
+
+                                       // draw mark
+                                       skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect);
+
+                                       // draw marked text
+                                       s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos);
+
+                                       if (!s.empty())
+                                               font->draw(s.c_str(), CurrentTextRect,
+                                                       OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
+                                                       false, true, &localClipRect);
+
+                               }
+                       }
+
+                       // Return the override color information to its previous settings.
+                       OverrideColorEnabled = prevOver;
+                       OverrideColor = prevColor;
+               }
+
+               // draw cursor
+
+               if (WordWrap || MultiLine)
+               {
+                       cursorLine = getLineFromPos(CursorPos);
+                       txtLine = &BrokenText[cursorLine];
+                       startPos = BrokenTextPositions[cursorLine];
+               }
+               s = txtLine->subString(0,CursorPos-startPos);
+               charcursorpos = font->getDimension(s.c_str()).Width +
+                       font->getKerningWidth(L"_", CursorPos-startPos > 0 ? &((*txtLine)[CursorPos-startPos-1]) : 0);
+
+               if (m_writable) {
+                       if (focus && (porting::getTimeMs() - BlinkStartTime) % 700 < 350) {
+                               setTextRect(cursorLine);
+                               CurrentTextRect.UpperLeftCorner.X += charcursorpos;
+
+                               font->draw(L"_", CurrentTextRect,
+                                       OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
+                                       false, true, &localClipRect);
+                       }
+               }
+       }
+
+       // draw children
+       IGUIElement::draw();
+}
+
+
+//! Sets the new caption of this element.
+void intlGUIEditBox::setText(const wchar_t* text)
+{
+       Text = text;
+       if (u32(CursorPos) > Text.size())
+               CursorPos = Text.size();
+       HScrollPos = 0;
+       breakText();
+}
+
+
+//! Enables or disables automatic scrolling with cursor position
+//! \param enable: If set to true, the text will move around with the cursor position
+void intlGUIEditBox::setAutoScroll(bool enable)
+{
+       AutoScroll = enable;
+}
+
+
+//! Checks to see if automatic scrolling is enabled
+//! \return true if automatic scrolling is enabled, false if not
+bool intlGUIEditBox::isAutoScrollEnabled() const
+{
+       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
+       return AutoScroll;
+}
+
+
+//! Gets the area of the text in the edit box
+//! \return Returns the size in pixels of the text
+core::dimension2du intlGUIEditBox::getTextDimension()
+{
+       core::rect<s32> ret;
+
+       setTextRect(0);
+       ret = CurrentTextRect;
+
+       for (u32 i=1; i < BrokenText.size(); ++i)
+       {
+               setTextRect(i);
+               ret.addInternalPoint(CurrentTextRect.UpperLeftCorner);
+               ret.addInternalPoint(CurrentTextRect.LowerRightCorner);
+       }
+
+       return core::dimension2du(ret.getSize());
+}
+
+
+//! Sets the maximum amount of characters which may be entered in the box.
+//! \param max: Maximum amount of characters. If 0, the character amount is
+//! infinity.
+void intlGUIEditBox::setMax(u32 max)
+{
+       Max = max;
+
+       if (Text.size() > Max && Max != 0)
+               Text = Text.subString(0, Max);
+}
+
+
+//! Returns maximum amount of characters, previously set by setMax();
+u32 intlGUIEditBox::getMax() const
+{
+       return Max;
+}
+
+
+bool intlGUIEditBox::processMouse(const SEvent& event)
+{
+       switch(event.MouseInput.Event)
+       {
+       case irr::EMIE_LMOUSE_LEFT_UP:
+               if (Environment->hasFocus(this))
+               {
+                       CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+                       if (MouseMarking)
+                       {
+                           setTextMarkers( MarkBegin, CursorPos );
+                       }
+                       MouseMarking = false;
+                       calculateScrollPos();
+                       return true;
+               }
+               break;
+       case irr::EMIE_MOUSE_MOVED:
+               {
+                       if (MouseMarking)
+                       {
+                               CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+                               setTextMarkers( MarkBegin, CursorPos );
+                               calculateScrollPos();
+                               return true;
+                       }
+               }
+               break;
+       case EMIE_LMOUSE_PRESSED_DOWN:
+               if (!Environment->hasFocus(this))
+               {
+                       BlinkStartTime = porting::getTimeMs();
+                       MouseMarking = true;
+                       CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+                       setTextMarkers(CursorPos, CursorPos );
+                       calculateScrollPos();
+                       return true;
+               }
+               else
+               {
+                       if (!AbsoluteClippingRect.isPointInside(
+                               core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y))) {
+                               return false;
+                       }
+
+
+                       // move cursor
+                       CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
+
+                       s32 newMarkBegin = MarkBegin;
+                       if (!MouseMarking)
+                               newMarkBegin = CursorPos;
+
+                       MouseMarking = true;
+                       setTextMarkers( newMarkBegin, CursorPos);
+                       calculateScrollPos();
+                       return true;
+               }
+               break;
+       case EMIE_MOUSE_WHEEL:
+               if (m_vscrollbar) {
+                       s32 pos = m_vscrollbar->getPos();
+                       s32 step = m_vscrollbar->getSmallStep();
+                       m_vscrollbar->setPos(pos - event.MouseInput.Wheel * step);
+               }
+               break;
+       default:
+               break;
+       }
+
+       return false;
+}
+
+
+s32 intlGUIEditBox::getCursorPos(s32 x, s32 y)
+{
+       IGUIFont* font = OverrideFont;
+       IGUISkin* skin = Environment->getSkin();
+       if (!OverrideFont)
+               font = skin->getFont();
+
+       const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
+
+       core::stringw *txtLine = NULL;
+       s32 startPos = 0;
+       u32 curr_line_idx = 0;
+       x += 3;
+
+       for (; curr_line_idx < lineCount; ++curr_line_idx) {
+               setTextRect(curr_line_idx);
+               if (curr_line_idx == 0 && y < CurrentTextRect.UpperLeftCorner.Y)
+                       y = CurrentTextRect.UpperLeftCorner.Y;
+               if (curr_line_idx == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y)
+                       y = CurrentTextRect.LowerRightCorner.Y;
+
+               // is it inside this region?
+               if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y) {
+                       // we've found the clicked line
+                       txtLine = (WordWrap || MultiLine) ? &BrokenText[curr_line_idx] : &Text;
+                       startPos = (WordWrap || MultiLine) ? BrokenTextPositions[curr_line_idx] : 0;
+                       break;
+               }
+       }
+
+       if (x < CurrentTextRect.UpperLeftCorner.X)
+               x = CurrentTextRect.UpperLeftCorner.X;
+       else if (x > CurrentTextRect.LowerRightCorner.X)
+               x = CurrentTextRect.LowerRightCorner.X;
+
+       s32 idx = font->getCharacterFromPos(txtLine->c_str(), x - CurrentTextRect.UpperLeftCorner.X);
+       // Special handling for last line, if we are on limits, add 1 extra shift because idx
+       // will be the last char, not null char of the wstring
+       if (curr_line_idx == lineCount - 1 && x == CurrentTextRect.LowerRightCorner.X)
+               idx++;
+
+       return rangelim(idx + startPos, 0, S32_MAX);
+}
+
+
+//! Breaks the single text line.
+void intlGUIEditBox::breakText()
+{
+       IGUISkin* skin = Environment->getSkin();
+
+       if ((!WordWrap && !MultiLine) || !skin)
+               return;
+
+       BrokenText.clear(); // need to reallocate :/
+       BrokenTextPositions.set_used(0);
+
+       IGUIFont* font = OverrideFont;
+       if (!OverrideFont)
+               font = skin->getFont();
+
+       if (!font)
+               return;
+
+       LastBreakFont = font;
+
+       core::stringw line;
+       core::stringw word;
+       core::stringw whitespace;
+       s32 lastLineStart = 0;
+       s32 size = Text.size();
+       s32 length = 0;
+       s32 elWidth = RelativeRect.getWidth() - 6;
+       wchar_t c;
+
+       for (s32 i=0; i<size; ++i)
+       {
+               c = Text[i];
+               bool lineBreak = false;
+
+               if (c == L'\r') // Mac or Windows breaks
+               {
+                       lineBreak = true;
+                       c = ' ';
+                       if (Text[i+1] == L'\n') // Windows breaks
+                       {
+                               Text.erase(i+1);
+                               --size;
+                       }
+               }
+               else if (c == L'\n') // Unix breaks
+               {
+                       lineBreak = true;
+                       c = ' ';
+               }
+
+               // don't break if we're not a multi-line edit box
+               if (!MultiLine)
+                       lineBreak = false;
+
+               if (c == L' ' || c == 0 || i == (size-1))
+               {
+                       if (!word.empty()) {
+                               // here comes the next whitespace, look if
+                               // we can break the last word to the next line.
+                               s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
+                               s32 worldlgth = font->getDimension(word.c_str()).Width;
+
+                               if (WordWrap && length + worldlgth + whitelgth > elWidth)
+                               {
+                                       // break to next line
+                                       length = worldlgth;
+                                       BrokenText.push_back(line);
+                                       BrokenTextPositions.push_back(lastLineStart);
+                                       lastLineStart = i - (s32)word.size();
+                                       line = word;
+                               }
+                               else
+                               {
+                                       // add word to line
+                                       line += whitespace;
+                                       line += word;
+                                       length += whitelgth + worldlgth;
+                               }
+
+                               word = L"";
+                               whitespace = L"";
+                       }
+
+                       whitespace += c;
+
+                       // compute line break
+                       if (lineBreak)
+                       {
+                               line += whitespace;
+                               line += word;
+                               BrokenText.push_back(line);
+                               BrokenTextPositions.push_back(lastLineStart);
+                               lastLineStart = i+1;
+                               line = L"";
+                               word = L"";
+                               whitespace = L"";
+                               length = 0;
+                       }
+               }
+               else
+               {
+                       // yippee this is a word..
+                       word += c;
+               }
+       }
+
+       line += whitespace;
+       line += word;
+       BrokenText.push_back(line);
+       BrokenTextPositions.push_back(lastLineStart);
+}
+
+
+void intlGUIEditBox::setTextRect(s32 line)
+{
+       core::dimension2du d;
+
+       IGUISkin* skin = Environment->getSkin();
+       if (!skin)
+               return;
+
+       IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
+
+       if (!font)
+               return;
+
+       // get text dimension
+       const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
+       if (WordWrap || MultiLine)
+       {
+               d = font->getDimension(BrokenText[line].c_str());
+       }
+       else
+       {
+               d = font->getDimension(Text.c_str());
+               d.Height = AbsoluteRect.getHeight();
+       }
+       d.Height += font->getKerningHeight();
+
+       // justification
+       switch (HAlign)
+       {
+       case EGUIA_CENTER:
+               // align to h centre
+               CurrentTextRect.UpperLeftCorner.X = (FrameRect.getWidth()/2) - (d.Width/2);
+               CurrentTextRect.LowerRightCorner.X = (FrameRect.getWidth()/2) + (d.Width/2);
+               break;
+       case EGUIA_LOWERRIGHT:
+               // align to right edge
+               CurrentTextRect.UpperLeftCorner.X = FrameRect.getWidth() - d.Width;
+               CurrentTextRect.LowerRightCorner.X = FrameRect.getWidth();
+               break;
+       default:
+               // align to left edge
+               CurrentTextRect.UpperLeftCorner.X = 0;
+               CurrentTextRect.LowerRightCorner.X = d.Width;
+
+       }
+
+       switch (VAlign)
+       {
+       case EGUIA_CENTER:
+               // align to v centre
+               CurrentTextRect.UpperLeftCorner.Y =
+                       (FrameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line;
+               break;
+       case EGUIA_LOWERRIGHT:
+               // align to bottom edge
+               CurrentTextRect.UpperLeftCorner.Y =
+                       FrameRect.getHeight() - lineCount*d.Height + d.Height*line;
+               break;
+       default:
+               // align to top edge
+               CurrentTextRect.UpperLeftCorner.Y = d.Height*line;
+               break;
+       }
+
+       CurrentTextRect.UpperLeftCorner.X  -= HScrollPos;
+       CurrentTextRect.LowerRightCorner.X -= HScrollPos;
+       CurrentTextRect.UpperLeftCorner.Y  -= VScrollPos;
+       CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height;
+
+       CurrentTextRect += FrameRect.UpperLeftCorner;
+
+}
+
+
+s32 intlGUIEditBox::getLineFromPos(s32 pos)
+{
+       if (!WordWrap && !MultiLine)
+               return 0;
+
+       s32 i=0;
+       while (i < (s32)BrokenTextPositions.size())
+       {
+               if (BrokenTextPositions[i] > pos)
+                       return i-1;
+               ++i;
+       }
+       return (s32)BrokenTextPositions.size() - 1;
+}
+
+
+void intlGUIEditBox::inputChar(wchar_t c)
+{
+       if (!IsEnabled)
+               return;
+
+       if (c != 0)
+       {
+               if (Text.size() < Max || Max == 0)
+               {
+                       core::stringw s;
+
+                       if (MarkBegin != MarkEnd)
+                       {
+                               // replace marked text
+                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
+                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
+
+                               s = Text.subString(0, realmbgn);
+                               s.append(c);
+                               s.append( Text.subString(realmend, Text.size()-realmend) );
+                               Text = s;
+                               CursorPos = realmbgn+1;
+                       }
+                       else
+                       {
+                               // add new character
+                               s = Text.subString(0, CursorPos);
+                               s.append(c);
+                               s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
+                               Text = s;
+                               ++CursorPos;
+                       }
+
+                       BlinkStartTime = porting::getTimeMs();
+                       setTextMarkers(0, 0);
+               }
+       }
+       breakText();
+       sendGuiEvent(EGET_EDITBOX_CHANGED);
+       calculateScrollPos();
+}
+
+
+void intlGUIEditBox::calculateScrollPos()
+{
+       if (!AutoScroll)
+               return;
+
+       // calculate horizontal scroll position
+       s32 cursLine = getLineFromPos(CursorPos);
+       setTextRect(cursLine);
+
+       // don't do horizontal scrolling when wordwrap is enabled.
+       if (!WordWrap)
+       {
+               // get cursor position
+               IGUISkin* skin = Environment->getSkin();
+               if (!skin)
+                       return;
+               IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
+               if (!font)
+                       return;
+
+               core::stringw *txtLine = MultiLine ? &BrokenText[cursLine] : &Text;
+               s32 cPos = MultiLine ? CursorPos - BrokenTextPositions[cursLine] : CursorPos;
+
+               s32 cStart = CurrentTextRect.UpperLeftCorner.X + HScrollPos +
+                       font->getDimension(txtLine->subString(0, cPos).c_str()).Width;
+
+               s32 cEnd = cStart + font->getDimension(L"_ ").Width;
+
+               if (FrameRect.LowerRightCorner.X < cEnd)
+                       HScrollPos = cEnd - FrameRect.LowerRightCorner.X;
+               else if (FrameRect.UpperLeftCorner.X > cStart)
+                       HScrollPos = cStart - FrameRect.UpperLeftCorner.X;
+               else
+                       HScrollPos = 0;
+
+               // todo: adjust scrollbar
+       }
+
+       // vertical scroll position
+       if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos)
+               VScrollPos = CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y + VScrollPos;
+
+       else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos)
+               VScrollPos = CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y + VScrollPos;
+       else
+               VScrollPos = 0;
+
+       // todo: adjust scrollbar
+       if (m_vscrollbar)
+               m_vscrollbar->setPos(VScrollPos);
+}
+
+//! set text markers
+void intlGUIEditBox::setTextMarkers(s32 begin, s32 end)
+{
+    if ( begin != MarkBegin || end != MarkEnd )
+    {
+        MarkBegin = begin;
+        MarkEnd = end;
+        sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
+    }
+}
+
+//! send some gui event to parent
+void intlGUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type)
+{
+       if ( Parent )
+       {
+        SEvent e;
+        e.EventType = EET_GUI_EVENT;
+        e.GUIEvent.Caller = this;
+        e.GUIEvent.Element = 0;
+        e.GUIEvent.EventType = type;
+
+        Parent->OnEvent(e);
+       }
+}
+
+//! Create a vertical scrollbar
+void intlGUIEditBox::createVScrollBar()
+{
+       s32 fontHeight = 1;
+
+       if (OverrideFont) {
+               fontHeight = OverrideFont->getDimension(L"").Height;
+       } else {
+               if (IGUISkin* skin = Environment->getSkin()) {
+                       if (IGUIFont* font = skin->getFont()) {
+                               fontHeight = font->getDimension(L"").Height;
+                       }
+               }
+       }
+
+       irr::core::rect<s32> scrollbarrect = FrameRect;
+       scrollbarrect.UpperLeftCorner.X += FrameRect.getWidth() - m_scrollbar_width;
+       m_vscrollbar = Environment->addScrollBar(false, scrollbarrect, getParent(), getID());
+       m_vscrollbar->setVisible(false);
+       m_vscrollbar->setSmallStep(3 * fontHeight);
+       m_vscrollbar->setLargeStep(10 * fontHeight);
+}
+
+//! Update the vertical scrollbar (visibilty & scroll position)
+void intlGUIEditBox::updateVScrollBar()
+{
+       if (!m_vscrollbar)
+               return;
+
+       // OnScrollBarChanged(...)
+       if (m_vscrollbar->getPos() != VScrollPos) {
+               s32 deltaScrollY = m_vscrollbar->getPos() - VScrollPos;
+               CurrentTextRect.UpperLeftCorner.Y -= deltaScrollY;
+               CurrentTextRect.LowerRightCorner.Y -= deltaScrollY;
+
+               s32 scrollymax = getTextDimension().Height - FrameRect.getHeight();
+               if (scrollymax != m_vscrollbar->getMax()) {
+                       // manage a newline or a deleted line
+                       m_vscrollbar->setMax(scrollymax);
+                       calculateScrollPos();
+               } else {
+                       // manage a newline or a deleted line
+                       VScrollPos = m_vscrollbar->getPos();
+               }
+       }
+
+       // check if a vertical scrollbar is needed ?
+       if (getTextDimension().Height > (u32) FrameRect.getHeight()) {
+               s32 scrollymax = getTextDimension().Height - FrameRect.getHeight();
+               if (scrollymax != m_vscrollbar->getMax()) {
+                       m_vscrollbar->setMax(scrollymax);
+               }
+
+               if (!m_vscrollbar->isVisible() && MultiLine) {
+                       AbsoluteRect.LowerRightCorner.X -= m_scrollbar_width;
+
+                       m_vscrollbar->setVisible(true);
+               }
+       } else {
+               if (m_vscrollbar->isVisible()) {
+                       AbsoluteRect.LowerRightCorner.X += m_scrollbar_width;
+
+                       VScrollPos = 0;
+                       m_vscrollbar->setPos(0);
+                       m_vscrollbar->setMax(1);
+                       m_vscrollbar->setVisible(false);
+               }
+       }
+}
+
+void intlGUIEditBox::setWritable(bool can_write_text)
+{
+       m_writable = can_write_text;
+}
+
+//! Writes attributes of the element.
+void intlGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
+{
+       // IGUIEditBox::serializeAttributes(out,options);
+
+       out->addBool  ("OverrideColorEnabled",OverrideColorEnabled );
+       out->addColor ("OverrideColor",       OverrideColor);
+       // out->addFont("OverrideFont",OverrideFont);
+       out->addInt   ("MaxChars",            Max);
+       out->addBool  ("WordWrap",            WordWrap);
+       out->addBool  ("MultiLine",           MultiLine);
+       out->addBool  ("AutoScroll",          AutoScroll);
+       out->addBool  ("PasswordBox",         PasswordBox);
+       core::stringw ch = L" ";
+       ch[0] = PasswordChar;
+       out->addString("PasswordChar",        ch.c_str());
+       out->addEnum  ("HTextAlign",          HAlign, GUIAlignmentNames);
+       out->addEnum  ("VTextAlign",          VAlign, GUIAlignmentNames);
+       out->addBool  ("Writable",            m_writable);
+
+       IGUIEditBox::serializeAttributes(out,options);
+}
+
+
+//! Reads attributes of the element
+void intlGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
+{
+       IGUIEditBox::deserializeAttributes(in,options);
+
+       setOverrideColor(in->getAttributeAsColor("OverrideColor"));
+       enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
+       setMax(in->getAttributeAsInt("MaxChars"));
+       setWordWrap(in->getAttributeAsBool("WordWrap"));
+       setMultiLine(in->getAttributeAsBool("MultiLine"));
+       setAutoScroll(in->getAttributeAsBool("AutoScroll"));
+       core::stringw ch = in->getAttributeAsStringW("PasswordChar");
+
+       if (ch.empty())
+               setPasswordBox(in->getAttributeAsBool("PasswordBox"));
+       else
+               setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
+
+       setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
+                       (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
+
+       setWritable(in->getAttributeAsBool("Writable"));
+       // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
+}
+
+
+} // end namespace gui
+} // end namespace irr
+
+#endif // _IRR_COMPILE_WITH_GUI_
diff --git a/src/gui/intlGUIEditBox.h b/src/gui/intlGUIEditBox.h
new file mode 100644 (file)
index 0000000..aa35e2e
--- /dev/null
@@ -0,0 +1,198 @@
+// Copyright (C) 2002-2013 Nikolaus Gebhardt
+// This file is part of the "Irrlicht Engine".
+// For conditions of distribution and use, see copyright notice in irrlicht.h
+
+#pragma once
+
+#include "IrrCompileConfig.h"
+//#ifdef _IRR_COMPILE_WITH_GUI_
+
+#include "IGUIEditBox.h"
+#include "irrArray.h"
+#include "IOSOperator.h"
+#include "IGUIScrollBar.h"
+
+namespace irr
+{
+namespace gui
+{
+       class intlGUIEditBox : public IGUIEditBox
+       {
+       public:
+
+               //! constructor
+               intlGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment,
+                       IGUIElement* parent, s32 id, const core::rect<s32>& rectangle,
+                       bool writable = true, bool has_vscrollbar = false);
+
+               //! destructor
+               virtual ~intlGUIEditBox();
+
+               //! Sets another skin independent font.
+               virtual void setOverrideFont(IGUIFont* font=0);
+
+               //! Gets the override font (if any)
+               /** \return The override font (may be 0) */
+               virtual IGUIFont* getOverrideFont() const;
+
+               //! Get the font which is used right now for drawing
+               /** Currently this is the override font when one is set and the
+               font of the active skin otherwise */
+               virtual IGUIFont* getActiveFont() const;
+
+               //! Sets another color for the text.
+               virtual void setOverrideColor(video::SColor color);
+
+               //! Gets the override color
+               virtual video::SColor getOverrideColor() const;
+
+               //! Sets if the text should use the overide color or the
+               //! color in the gui skin.
+               virtual void enableOverrideColor(bool enable);
+
+               //! Checks if an override color is enabled
+               /** \return true if the override color is enabled, false otherwise */
+               virtual bool isOverrideColorEnabled(void) const;
+
+               //! Sets whether to draw the background
+               virtual void setDrawBackground(bool draw);
+
+               //! Turns the border on or off
+               virtual void setDrawBorder(bool border);
+
+               //! Enables or disables word wrap for using the edit box as multiline text editor.
+               virtual void setWordWrap(bool enable);
+
+               //! Checks if word wrap is enabled
+               //! \return true if word wrap is enabled, false otherwise
+               virtual bool isWordWrapEnabled() const;
+
+               //! Enables or disables newlines.
+               /** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
+               instead a newline character will be inserted. */
+               virtual void setMultiLine(bool enable);
+
+               //! Checks if multi line editing is enabled
+               //! \return true if mult-line is enabled, false otherwise
+               virtual bool isMultiLineEnabled() const;
+
+               //! Enables or disables automatic scrolling with cursor position
+               //! \param enable: If set to true, the text will move around with the cursor position
+               virtual void setAutoScroll(bool enable);
+
+               //! Checks to see if automatic scrolling is enabled
+               //! \return true if automatic scrolling is enabled, false if not
+               virtual bool isAutoScrollEnabled() const;
+
+               //! Gets the size area of the text in the edit box
+               //! \return Returns the size in pixels of the text
+               virtual core::dimension2du getTextDimension();
+
+               //! Sets text justification
+               virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
+
+               //! called if an event happened.
+               virtual bool OnEvent(const SEvent& event);
+
+               //! draws the element and its children
+               virtual void draw();
+
+               //! Sets the new caption of this element.
+               virtual void setText(const wchar_t* text);
+
+               //! Sets the maximum amount of characters which may be entered in the box.
+               //! \param max: Maximum amount of characters. If 0, the character amount is
+               //! infinity.
+               virtual void setMax(u32 max);
+
+               //! Returns maximum amount of characters, previously set by setMax();
+               virtual u32 getMax() const;
+
+               //! Sets whether the edit box is a password box. Setting this to true will
+               /** disable MultiLine, WordWrap and the ability to copy with ctrl+c or ctrl+x
+               \param passwordBox: true to enable password, false to disable
+               \param passwordChar: the character that is displayed instead of letters */
+               virtual void setPasswordBox(bool passwordBox, wchar_t passwordChar = L'*');
+
+               //! Returns true if the edit box is currently a password box.
+               virtual bool isPasswordBox() const;
+
+               //! Updates the absolute position, splits text if required
+               virtual void updateAbsolutePosition();
+
+               //! set true if this EditBox is writable
+               virtual void setWritable(bool can_write_text);
+
+               //! Writes attributes of the element.
+               virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const;
+
+               //! Reads attributes of the element
+               virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options);
+
+       protected:
+               //! Breaks the single text line.
+               void breakText();
+               //! sets the area of the given line
+               void setTextRect(s32 line);
+               //! returns the line number that the cursor is on
+               s32 getLineFromPos(s32 pos);
+               //! adds a letter to the edit box
+               void inputChar(wchar_t c);
+               //! calculates the current scroll position
+               void calculateScrollPos();
+               //! send some gui event to parent
+               void sendGuiEvent(EGUI_EVENT_TYPE type);
+               //! set text markers
+               void setTextMarkers(s32 begin, s32 end);
+
+               bool processKey(const SEvent& event);
+               bool processMouse(const SEvent& event);
+               s32 getCursorPos(s32 x, s32 y);
+
+               //! Create a vertical scrollbar
+               void createVScrollBar();
+
+               //! Update the vertical scrollbar (visibilty & scroll position)
+               void updateVScrollBar();
+
+               bool MouseMarking = false;
+               bool Border;
+               bool OverrideColorEnabled = false;
+               s32 MarkBegin = 0;
+               s32 MarkEnd = 0;
+
+               video::SColor OverrideColor = video::SColor(101,255,255,255);
+               gui::IGUIFont *OverrideFont = nullptr;
+               gui::IGUIFont *LastBreakFont = nullptr;
+               IOSOperator *Operator = nullptr;
+
+               u64 BlinkStartTime = 0;
+               s32 CursorPos = 0;
+               s32 HScrollPos = 0;
+               s32 VScrollPos = 0; // scroll position in characters
+               u32 Max = 0;
+
+               bool WordWrap = false;
+               bool MultiLine = false;
+               bool AutoScroll = true;
+               bool PasswordBox = false;
+               wchar_t PasswordChar = L'*';
+               EGUI_ALIGNMENT HAlign = EGUIA_UPPERLEFT;
+               EGUI_ALIGNMENT VAlign = EGUIA_CENTER;
+
+               core::array<core::stringw> BrokenText;
+               core::array<s32> BrokenTextPositions;
+
+               core::rect<s32> CurrentTextRect = core::rect<s32>(0,0,1,1);
+               core::rect<s32> FrameRect; // temporary values
+               u32 m_scrollbar_width;
+               IGUIScrollBar *m_vscrollbar;
+               bool m_writable;
+
+       };
+
+
+} // end namespace gui
+} // end namespace irr
+
+//#endif // _IRR_COMPILE_WITH_GUI_
diff --git a/src/gui/mainmenumanager.h b/src/gui/mainmenumanager.h
new file mode 100644 (file)
index 0000000..ea93278
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+/*
+       All kinds of stuff that needs to be exposed from main.cpp
+*/
+#include "modalMenu.h"
+#include <cassert>
+#include <list>
+
+class IGameCallback
+{
+public:
+       virtual void exitToOS() = 0;
+       virtual void keyConfig() = 0;
+       virtual void disconnect() = 0;
+       virtual void changePassword() = 0;
+       virtual void changeVolume() = 0;
+
+       virtual void signalKeyConfigChange() = 0;
+};
+
+extern gui::IGUIEnvironment *guienv;
+extern gui::IGUIStaticText *guiroot;
+
+// Handler for the modal menus
+
+class MainMenuManager : public IMenuManager
+{
+public:
+       virtual void createdMenu(gui::IGUIElement *menu)
+       {
+#ifndef NDEBUG
+               for (gui::IGUIElement *i : m_stack) {
+                       assert(i != menu);
+               }
+#endif
+
+               if(!m_stack.empty())
+                       m_stack.back()->setVisible(false);
+               m_stack.push_back(menu);
+       }
+
+       virtual void deletingMenu(gui::IGUIElement *menu)
+       {
+               // Remove all entries if there are duplicates
+               bool removed_entry;
+               do{
+                       removed_entry = false;
+                       for(std::list<gui::IGUIElement*>::iterator
+                                       i = m_stack.begin();
+                                       i != m_stack.end(); ++i)
+                       {
+                               if(*i == menu)
+                               {
+                                       m_stack.erase(i);
+                                       removed_entry = true;
+                                       break;
+                               }
+                       }
+               }while(removed_entry);
+
+               /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
+               assert(*i == menu);
+               m_stack.erase(i);*/
+
+               if(!m_stack.empty())
+                       m_stack.back()->setVisible(true);
+       }
+
+       // Returns true to prevent further processing
+       virtual bool preprocessEvent(const SEvent& event)
+       {
+               if (m_stack.empty())
+                       return false;
+               GUIModalMenu *mm = dynamic_cast<GUIModalMenu*>(m_stack.back());
+               return mm && mm->preprocessEvent(event);
+       }
+
+       u32 menuCount()
+       {
+               return m_stack.size();
+       }
+
+       bool pausesGame()
+       {
+               for (gui::IGUIElement *i : m_stack) {
+                       GUIModalMenu *mm = dynamic_cast<GUIModalMenu*>(i);
+                       if (mm && mm->pausesGame())
+                               return true;
+               }
+               return false;
+       }
+
+       std::list<gui::IGUIElement*> m_stack;
+};
+
+extern MainMenuManager g_menumgr;
+
+extern bool isMenuActive();
+
+class MainGameCallback : public IGameCallback
+{
+public:
+       MainGameCallback() = default;
+       virtual ~MainGameCallback() = default;
+
+       virtual void exitToOS()
+       {
+               shutdown_requested = true;
+       }
+
+       virtual void disconnect()
+       {
+               disconnect_requested = true;
+       }
+
+       virtual void changePassword()
+       {
+               changepassword_requested = true;
+       }
+
+       virtual void changeVolume()
+       {
+               changevolume_requested = true;
+       }
+
+       virtual void keyConfig()
+       {
+               keyconfig_requested = true;
+       }
+
+       virtual void signalKeyConfigChange()
+       {
+               keyconfig_changed = true;
+       }
+
+
+       bool disconnect_requested = false;
+       bool changepassword_requested = false;
+       bool changevolume_requested = false;
+       bool keyconfig_requested = false;
+       bool shutdown_requested = false;
+
+       bool keyconfig_changed = false;
+};
+
+extern MainGameCallback *g_gamecallback;
diff --git a/src/gui/modalMenu.h b/src/gui/modalMenu.h
new file mode 100644 (file)
index 0000000..f41591c
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+Minetest
+Copyright (C) 2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "irrlichttypes_extrabloated.h"
+#ifdef HAVE_TOUCHSCREENGUI
+#include "touchscreengui.h"
+#endif
+
+class GUIModalMenu;
+
+class IMenuManager
+{
+public:
+       // A GUIModalMenu calls these when this class is passed as a parameter
+       virtual void createdMenu(gui::IGUIElement *menu) = 0;
+       virtual void deletingMenu(gui::IGUIElement *menu) = 0;
+};
+
+/*
+       Remember to drop() the menu after creating, so that it can
+       remove itself when it wants to.
+*/
+
+class GUIModalMenu : public gui::IGUIElement
+{
+public:
+       GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id,
+                       IMenuManager *menumgr):
+               IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
+                               core::rect<s32>(0,0,100,100))
+       {
+               m_menumgr = menumgr;
+
+               setVisible(true);
+               Environment->setFocus(this);
+               m_menumgr->createdMenu(this);
+       }
+
+       virtual ~GUIModalMenu()
+       {
+               m_menumgr->deletingMenu(this);
+       }
+
+       void allowFocusRemoval(bool allow)
+       {
+               m_allow_focus_removal = allow;
+       }
+
+       bool canTakeFocus(gui::IGUIElement *e)
+       {
+               return (e && (e == this || isMyChild(e))) || m_allow_focus_removal;
+       }
+
+       void draw()
+       {
+               if(!IsVisible)
+                       return;
+
+               video::IVideoDriver* driver = Environment->getVideoDriver();
+               v2u32 screensize = driver->getScreenSize();
+               if(screensize != m_screensize_old /*|| m_force_regenerate_gui*/)
+               {
+                       m_screensize_old = screensize;
+                       regenerateGui(screensize);
+                       //m_force_regenerate_gui = false;
+               }
+
+               drawMenu();
+       }
+
+       /*
+               This should be called when the menu wants to quit.
+
+               WARNING: THIS DEALLOCATES THE MENU FROM MEMORY. Return
+               immediately if you call this from the menu itself.
+
+               (More precisely, this decrements the reference count.)
+       */
+       void quitMenu()
+       {
+               allowFocusRemoval(true);
+               // This removes Environment's grab on us
+               Environment->removeFocus(this);
+               m_menumgr->deletingMenu(this);
+               this->remove();
+#ifdef HAVE_TOUCHSCREENGUI
+               if (g_touchscreengui)
+                       g_touchscreengui->show();
+#endif
+       }
+
+       void removeChildren()
+       {
+               const core::list<gui::IGUIElement*> &children = getChildren();
+               core::list<gui::IGUIElement*> children_copy;
+               for (gui::IGUIElement *i : children) {
+                       children_copy.push_back(i);
+               }
+
+               for (gui::IGUIElement *i : children_copy) {
+                       i->remove();
+               }
+       }
+
+       virtual void regenerateGui(v2u32 screensize) = 0;
+       virtual void drawMenu() = 0;
+       virtual bool preprocessEvent(const SEvent& event) { return false; };
+       virtual bool OnEvent(const SEvent& event) { return false; };
+       virtual bool pausesGame(){ return false; } // Used for pause menu
+
+protected:
+       //bool m_force_regenerate_gui;
+       v2u32 m_screensize_old;
+private:
+       IMenuManager *m_menumgr;
+       // This might be necessary to expose to the implementation if it
+       // wants to launch other menus
+       bool m_allow_focus_removal = false;
+};
diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp
new file mode 100644 (file)
index 0000000..e849b40
--- /dev/null
@@ -0,0 +1,1063 @@
+/*
+Copyright (C) 2014 sapier
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "touchscreengui.h"
+#include "irrlichttypes.h"
+#include "irr_v2d.h"
+#include "log.h"
+#include "keycode.h"
+#include "settings.h"
+#include "gettime.h"
+#include "util/numeric.h"
+#include "porting.h"
+#include "guiscalingfilter.h"
+
+#include <iostream>
+#include <algorithm>
+
+#include <ISceneCollisionManager.h>
+
+// Very slow button repeat frequency (in seconds)
+#define SLOW_BUTTON_REPEAT     (1.0f)
+
+using namespace irr::core;
+
+const char** touchgui_button_imagenames = (const char*[]) {
+       "up_arrow.png",
+       "down_arrow.png",
+       "left_arrow.png",
+       "right_arrow.png",
+       "jump_btn.png",
+       "down.png"
+};
+
+static irr::EKEY_CODE id2keycode(touch_gui_button_id id)
+{
+       std::string key = "";
+       switch (id) {
+               case forward_id:
+                       key = "forward";
+                       break;
+               case left_id:
+                       key = "left";
+                       break;
+               case right_id:
+                       key = "right";
+                       break;
+               case backward_id:
+                       key = "backward";
+                       break;
+               case inventory_id:
+                       key = "inventory";
+                       break;
+               case drop_id:
+                       key = "drop";
+                       break;
+               case jump_id:
+                       key = "jump";
+                       break;
+               case crunch_id:
+                       key = "sneak";
+                       break;
+               case fly_id:
+                       key = "freemove";
+                       break;
+               case noclip_id:
+                       key = "noclip";
+                       break;
+               case fast_id:
+                       key = "fastmove";
+                       break;
+               case debug_id:
+                       key = "toggle_debug";
+                       break;
+               case chat_id:
+                       key = "chat";
+                       break;
+               case camera_id:
+                       key = "camera_mode";
+                       break;
+               case range_id:
+                       key = "rangeselect";
+                       break;
+       }
+       assert(key != "");
+       return keyname_to_keycode(g_settings->get("keymap_" + key).c_str());
+}
+
+TouchScreenGUI *g_touchscreengui;
+
+static void load_button_texture(button_info* btn, const char* path,
+               rect<s32> button_rect, ISimpleTextureSource* tsrc, video::IVideoDriver *driver)
+{
+       unsigned int tid;
+       video::ITexture *texture = guiScalingImageButton(driver,
+                       tsrc->getTexture(path, &tid), button_rect.getWidth(),
+                       button_rect.getHeight());
+       if (texture) {
+               btn->guibutton->setUseAlphaChannel(true);
+               if (g_settings->getBool("gui_scaling_filter")) {
+                       rect<s32> txr_rect = rect<s32>(0, 0, button_rect.getWidth(), button_rect.getHeight());
+                       btn->guibutton->setImage(texture, txr_rect);
+                       btn->guibutton->setPressedImage(texture, txr_rect);
+                       btn->guibutton->setScaleImage(false);
+               } else {
+                       btn->guibutton->setImage(texture);
+                       btn->guibutton->setPressedImage(texture);
+                       btn->guibutton->setScaleImage(true);
+               }
+               btn->guibutton->setDrawBorder(false);
+               btn->guibutton->setText(L"");
+               }
+}
+
+AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device,
+               IEventReceiver* receiver) :
+                       m_driver(device->getVideoDriver()),
+                       m_guienv(device->getGUIEnvironment()),
+                       m_receiver(receiver)
+{
+}
+
+void AutoHideButtonBar::init(ISimpleTextureSource* tsrc,
+               const char* starter_img, int button_id, v2s32 UpperLeft,
+               v2s32 LowerRight, autohide_button_bar_dir dir, float timeout)
+{
+       m_texturesource = tsrc;
+
+       m_upper_left = UpperLeft;
+       m_lower_right = LowerRight;
+
+       /* init settings bar */
+
+       irr::core::rect<int> current_button = rect<s32>(UpperLeft.X, UpperLeft.Y,
+                       LowerRight.X, LowerRight.Y);
+
+       m_starter.guibutton         = m_guienv->addButton(current_button, 0, button_id, L"", 0);
+       m_starter.guibutton->grab();
+       m_starter.repeatcounter     = -1;
+       m_starter.keycode           = KEY_OEM_8; // use invalid keycode as it's not relevant
+       m_starter.immediate_release = true;
+       m_starter.ids.clear();
+
+       load_button_texture(&m_starter, starter_img, current_button,
+                       m_texturesource, m_driver);
+
+       m_dir = dir;
+       m_timeout_value = timeout;
+
+       m_initialized = true;
+}
+
+AutoHideButtonBar::~AutoHideButtonBar()
+{
+       if (m_starter.guibutton) {
+               m_starter.guibutton->setVisible(false);
+               m_starter.guibutton->drop();
+       }
+}
+
+void AutoHideButtonBar::addButton(touch_gui_button_id button_id,
+               const wchar_t* caption, const char* btn_image)
+{
+
+       if (!m_initialized) {
+               errorstream << "AutoHideButtonBar::addButton not yet initialized!"
+                               << std::endl;
+               return;
+       }
+       int button_size = 0;
+
+       if ((m_dir == AHBB_Dir_Top_Bottom) || (m_dir == AHBB_Dir_Bottom_Top)) {
+               button_size = m_lower_right.X - m_upper_left.X;
+       } else {
+               button_size = m_lower_right.Y - m_upper_left.Y;
+       }
+
+       irr::core::rect<int> current_button;
+
+       if ((m_dir == AHBB_Dir_Right_Left) || (m_dir == AHBB_Dir_Left_Right)) {
+
+               int x_start = 0;
+               int x_end = 0;
+
+               if (m_dir == AHBB_Dir_Left_Right) {
+                       x_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size())
+                                       + (button_size * 0.25);
+                       x_end = x_start + button_size;
+               } else {
+                       x_end = m_upper_left.X - (button_size * 1.25 * m_buttons.size())
+                                       - (button_size * 0.25);
+                       x_start = x_end - button_size;
+               }
+
+               current_button = rect<s32>(x_start, m_upper_left.Y, x_end,
+                               m_lower_right.Y);
+       } else {
+               int y_start = 0;
+               int y_end = 0;
+
+               if (m_dir == AHBB_Dir_Top_Bottom) {
+                       y_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size())
+                                       + (button_size * 0.25);
+                       y_end = y_start + button_size;
+               } else {
+                       y_end = m_upper_left.X - (button_size * 1.25 * m_buttons.size())
+                                       - (button_size * 0.25);
+                       y_start = y_end - button_size;
+               }
+
+               current_button = rect<s32>(m_upper_left.X, y_start, m_lower_right.Y,
+                               y_end);
+       }
+
+       button_info* btn       = new button_info();
+       btn->guibutton         = m_guienv->addButton(current_button, 0, button_id, caption, 0);
+       btn->guibutton->grab();
+       btn->guibutton->setVisible(false);
+       btn->guibutton->setEnabled(false);
+       btn->repeatcounter     = -1;
+       btn->keycode           = id2keycode(button_id);
+       btn->immediate_release = true;
+       btn->ids.clear();
+
+       load_button_texture(btn, btn_image, current_button, m_texturesource,
+                       m_driver);
+
+       m_buttons.push_back(btn);
+}
+
+bool AutoHideButtonBar::isButton(const SEvent &event)
+{
+       IGUIElement* rootguielement = m_guienv->getRootGUIElement();
+
+       if (rootguielement == NULL) {
+               return false;
+       }
+
+       gui::IGUIElement *element = rootguielement->getElementFromPoint(
+                       core::position2d<s32>(event.TouchInput.X, event.TouchInput.Y));
+
+       if (element == NULL) {
+               return false;
+       }
+
+       if (m_active) {
+               /* check for all buttons in vector */
+
+               std::vector<button_info*>::iterator iter = m_buttons.begin();
+
+               while (iter != m_buttons.end()) {
+                       if ((*iter)->guibutton == element) {
+
+                               SEvent* translated = new SEvent();
+                               memset(translated, 0, sizeof(SEvent));
+                               translated->EventType            = irr::EET_KEY_INPUT_EVENT;
+                               translated->KeyInput.Key         = (*iter)->keycode;
+                               translated->KeyInput.Control     = false;
+                               translated->KeyInput.Shift       = false;
+                               translated->KeyInput.Char        = 0;
+
+                               /* add this event */
+                               translated->KeyInput.PressedDown = true;
+                               m_receiver->OnEvent(*translated);
+
+                               /* remove this event */
+                               translated->KeyInput.PressedDown = false;
+                               m_receiver->OnEvent(*translated);
+
+                               delete translated;
+
+                               (*iter)->ids.push_back(event.TouchInput.ID);
+
+                               m_timeout = 0;
+
+                               return true;
+                       }
+                       ++iter;
+               }
+       } else {
+               /* check for starter button only */
+               if (element == m_starter.guibutton) {
+                       m_starter.ids.push_back(event.TouchInput.ID);
+                       m_starter.guibutton->setVisible(false);
+                       m_starter.guibutton->setEnabled(false);
+                       m_active = true;
+                       m_timeout = 0;
+
+                       std::vector<button_info*>::iterator iter = m_buttons.begin();
+
+                       while (iter != m_buttons.end()) {
+                               (*iter)->guibutton->setVisible(true);
+                               (*iter)->guibutton->setEnabled(true);
+                               ++iter;
+                       }
+
+                       return true;
+               }
+       }
+       return false;
+}
+
+bool AutoHideButtonBar::isReleaseButton(int eventID)
+{
+       std::vector<int>::iterator id = std::find(m_starter.ids.begin(),
+                       m_starter.ids.end(), eventID);
+
+       if (id != m_starter.ids.end()) {
+               m_starter.ids.erase(id);
+               return true;
+       }
+
+       std::vector<button_info*>::iterator iter = m_buttons.begin();
+
+       while (iter != m_buttons.end()) {
+               std::vector<int>::iterator id = std::find((*iter)->ids.begin(),
+                               (*iter)->ids.end(), eventID);
+
+               if (id != (*iter)->ids.end()) {
+                       (*iter)->ids.erase(id);
+                       // TODO handle settings button release
+                       return true;
+               }
+               ++iter;
+       }
+
+       return false;
+}
+
+void AutoHideButtonBar::step(float dtime)
+{
+       if (m_active) {
+               m_timeout += dtime;
+
+               if (m_timeout > m_timeout_value) {
+                       deactivate();
+               }
+       }
+}
+
+void AutoHideButtonBar::deactivate()
+{
+       if (m_visible) {
+               m_starter.guibutton->setVisible(true);
+               m_starter.guibutton->setEnabled(true);
+       }
+       m_active = false;
+
+       std::vector<button_info*>::iterator iter = m_buttons.begin();
+
+       while (iter != m_buttons.end()) {
+                       (*iter)->guibutton->setVisible(false);
+                       (*iter)->guibutton->setEnabled(false);
+               ++iter;
+       }
+}
+
+void AutoHideButtonBar::hide()
+{
+       m_visible = false;
+       m_starter.guibutton->setVisible(false);
+       m_starter.guibutton->setEnabled(false);
+
+       std::vector<button_info*>::iterator iter = m_buttons.begin();
+
+       while (iter != m_buttons.end()) {
+               (*iter)->guibutton->setVisible(false);
+               (*iter)->guibutton->setEnabled(false);
+               ++iter;
+       }
+}
+
+void AutoHideButtonBar::show()
+{
+       m_visible = true;
+
+       if (m_active) {
+               std::vector<button_info*>::iterator iter = m_buttons.begin();
+
+               while (iter != m_buttons.end()) {
+                       (*iter)->guibutton->setVisible(true);
+                       (*iter)->guibutton->setEnabled(true);
+                       ++iter;
+               }
+       } else {
+               m_starter.guibutton->setVisible(true);
+               m_starter.guibutton->setEnabled(true);
+       }
+}
+
+TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver):
+       m_device(device),
+       m_guienv(device->getGUIEnvironment()),
+       m_receiver(receiver),
+       m_settingsbar(device, receiver),
+       m_rarecontrolsbar(device, receiver)
+{
+       for (unsigned int i=0; i < after_last_element_id; i++) {
+               m_buttons[i].guibutton     =  0;
+               m_buttons[i].repeatcounter = -1;
+               m_buttons[i].repeatdelay   = BUTTON_REPEAT_DELAY;
+       }
+
+       m_screensize = m_device->getVideoDriver()->getScreenSize();
+}
+
+void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect,
+               std::wstring caption, bool immediate_release, float repeat_delay)
+{
+
+       button_info* btn       = &m_buttons[id];
+       btn->guibutton         = m_guienv->addButton(button_rect, 0, id, caption.c_str());
+       btn->guibutton->grab();
+       btn->repeatcounter     = -1;
+       btn->repeatdelay       = repeat_delay;
+       btn->keycode           = id2keycode(id);
+       btn->immediate_release = immediate_release;
+       btn->ids.clear();
+
+       load_button_texture(btn,touchgui_button_imagenames[id],button_rect,
+                       m_texturesource, m_device->getVideoDriver());
+}
+
+static int getMaxControlPadSize(float density) {
+       return 200 * density * g_settings->getFloat("hud_scaling");
+}
+
+int TouchScreenGUI::getGuiButtonSize()
+{
+       u32 control_pad_size = MYMIN((2 * m_screensize.Y) / 3,
+                       getMaxControlPadSize(porting::getDisplayDensity()));
+
+       return control_pad_size / 3;
+}
+
+void TouchScreenGUI::init(ISimpleTextureSource* tsrc)
+{
+       assert(tsrc != 0);
+
+       u32 button_size      = getGuiButtonSize();
+       m_visible            = true;
+       m_texturesource      = tsrc;
+       /*
+       draw control pad
+       0 1 2
+       3 4 5
+       for now only 0, 1, 2, and 4 are used
+       */
+       int number = 0;
+       for (int y = 0; y < 2; ++y)
+               for (int x = 0; x < 3; ++x, ++number) {
+                       rect<s32> button_rect(
+                                       x * button_size, m_screensize.Y - button_size * (2 - y),
+                                       (x + 1) * button_size, m_screensize.Y - button_size * (1 - y)
+                       );
+                       touch_gui_button_id id = after_last_element_id;
+                       std::wstring caption;
+                       switch (number) {
+                       case 0:
+                               id = left_id;
+                               caption = L"<";
+                               break;
+                       case 1:
+                               id = forward_id;
+                               caption = L"^";
+                               break;
+                       case 2:
+                               id = right_id;
+                               caption = L">";
+                               break;
+                       case 4:
+                               id = backward_id;
+                               caption = L"v";
+                               break;
+                       }
+                       if (id != after_last_element_id) {
+                               initButton(id, button_rect, caption, false);
+                               }
+               }
+
+       /* init jump button */
+       initButton(jump_id,
+                       rect<s32>(m_screensize.X-(1.75*button_size),
+                                       m_screensize.Y - (0.5*button_size),
+                                       m_screensize.X-(0.25*button_size),
+                                       m_screensize.Y),
+                       L"x",false);
+
+       /* init crunch button */
+       initButton(crunch_id,
+                       rect<s32>(m_screensize.X-(3.25*button_size),
+                                       m_screensize.Y - (0.5*button_size),
+                                       m_screensize.X-(1.75*button_size),
+                                       m_screensize.Y),
+                       L"H",false);
+
+       m_settingsbar.init(m_texturesource, "gear_icon.png", settings_starter_id,
+                       v2s32(m_screensize.X - (button_size / 2),
+                                       m_screensize.Y - ((SETTINGS_BAR_Y_OFFSET + 1) * button_size)
+                                                       + (button_size * 0.5)),
+                       v2s32(m_screensize.X,
+                                       m_screensize.Y - (SETTINGS_BAR_Y_OFFSET * button_size)
+                                                       + (button_size * 0.5)), AHBB_Dir_Right_Left,
+                       3.0);
+
+       m_settingsbar.addButton(fly_id,    L"fly",       "fly_btn.png");
+       m_settingsbar.addButton(noclip_id, L"noclip",    "noclip_btn.png");
+       m_settingsbar.addButton(fast_id,   L"fast",      "fast_btn.png");
+       m_settingsbar.addButton(debug_id,  L"debug",     "debug_btn.png");
+       m_settingsbar.addButton(camera_id, L"camera",    "camera_btn.png");
+       m_settingsbar.addButton(range_id,  L"rangeview", "rangeview_btn.png");
+
+       m_rarecontrolsbar.init(m_texturesource, "rare_controls.png",
+                       rare_controls_starter_id,
+                       v2s32(0,
+                                       m_screensize.Y
+                                                       - ((RARE_CONTROLS_BAR_Y_OFFSET + 1) * button_size)
+                                                       + (button_size * 0.5)),
+                       v2s32(button_size / 2,
+                                       m_screensize.Y - (RARE_CONTROLS_BAR_Y_OFFSET * button_size)
+                                                       + (button_size * 0.5)), AHBB_Dir_Left_Right,
+                       2);
+
+       m_rarecontrolsbar.addButton(chat_id,      L"Chat", "chat_btn.png");
+       m_rarecontrolsbar.addButton(inventory_id, L"inv",  "inventory_btn.png");
+       m_rarecontrolsbar.addButton(drop_id,      L"drop", "drop_btn.png");
+
+}
+
+touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y)
+{
+       IGUIElement* rootguielement = m_guienv->getRootGUIElement();
+
+       if (rootguielement != NULL) {
+               gui::IGUIElement *element =
+                               rootguielement->getElementFromPoint(core::position2d<s32>(x,y));
+
+               if (element) {
+                       for (unsigned int i=0; i < after_last_element_id; i++) {
+                               if (element == m_buttons[i].guibutton) {
+                                       return (touch_gui_button_id) i;
+                               }
+                       }
+               }
+       }
+       return after_last_element_id;
+}
+
+touch_gui_button_id TouchScreenGUI::getButtonID(int eventID)
+{
+       for (unsigned int i=0; i < after_last_element_id; i++) {
+               button_info* btn = &m_buttons[i];
+
+               std::vector<int>::iterator id =
+                               std::find(btn->ids.begin(),btn->ids.end(), eventID);
+
+               if (id != btn->ids.end())
+                       return (touch_gui_button_id) i;
+       }
+
+       return after_last_element_id;
+}
+
+bool TouchScreenGUI::isHUDButton(const SEvent &event)
+{
+       // check if hud item is pressed
+       for (std::map<int,rect<s32> >::iterator iter = m_hud_rects.begin();
+                       iter != m_hud_rects.end(); ++iter) {
+               if (iter->second.isPointInside(
+                               v2s32(event.TouchInput.X,
+                                               event.TouchInput.Y)
+                       )) {
+                       if ( iter->first < 8) {
+                               SEvent* translated = new SEvent();
+                               memset(translated,0,sizeof(SEvent));
+                               translated->EventType = irr::EET_KEY_INPUT_EVENT;
+                               translated->KeyInput.Key         = (irr::EKEY_CODE) (KEY_KEY_1 + iter->first);
+                               translated->KeyInput.Control     = false;
+                               translated->KeyInput.Shift       = false;
+                               translated->KeyInput.PressedDown = true;
+                               m_receiver->OnEvent(*translated);
+                               m_hud_ids[event.TouchInput.ID]   = translated->KeyInput.Key;
+                               delete translated;
+                               return true;
+                       }
+               }
+       }
+       return false;
+}
+
+bool TouchScreenGUI::isReleaseHUDButton(int eventID)
+{
+       std::map<int,irr::EKEY_CODE>::iterator iter = m_hud_ids.find(eventID);
+
+       if (iter != m_hud_ids.end()) {
+               SEvent* translated = new SEvent();
+               memset(translated,0,sizeof(SEvent));
+               translated->EventType            = irr::EET_KEY_INPUT_EVENT;
+               translated->KeyInput.Key         = iter->second;
+               translated->KeyInput.PressedDown = false;
+               translated->KeyInput.Control     = false;
+               translated->KeyInput.Shift       = false;
+               m_receiver->OnEvent(*translated);
+               m_hud_ids.erase(iter);
+               delete translated;
+               return true;
+       }
+       return false;
+}
+
+void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button,
+               int eventID, bool action)
+{
+       button_info* btn = &m_buttons[button];
+       SEvent* translated = new SEvent();
+       memset(translated,0,sizeof(SEvent));
+       translated->EventType            = irr::EET_KEY_INPUT_EVENT;
+       translated->KeyInput.Key         = btn->keycode;
+       translated->KeyInput.Control     = false;
+       translated->KeyInput.Shift       = false;
+       translated->KeyInput.Char        = 0;
+
+       /* add this event */
+       if (action) {
+               assert(std::find(btn->ids.begin(),btn->ids.end(), eventID) == btn->ids.end());
+
+               btn->ids.push_back(eventID);
+
+               if (btn->ids.size() > 1) return;
+
+               btn->repeatcounter = 0;
+               translated->KeyInput.PressedDown = true;
+               translated->KeyInput.Key = btn->keycode;
+               m_receiver->OnEvent(*translated);
+       }
+       /* remove event */
+       if ((!action) || (btn->immediate_release)) {
+
+               std::vector<int>::iterator pos =
+                               std::find(btn->ids.begin(),btn->ids.end(), eventID);
+               /* has to be in touch list */
+               assert(pos != btn->ids.end());
+               btn->ids.erase(pos);
+
+               if (btn->ids.size() > 0)  { return; }
+
+               translated->KeyInput.PressedDown = false;
+               btn->repeatcounter               = -1;
+               m_receiver->OnEvent(*translated);
+       }
+       delete translated;
+}
+
+
+void TouchScreenGUI::handleReleaseEvent(int evt_id)
+{
+       touch_gui_button_id button = getButtonID(evt_id);
+
+       /* handle button events */
+       if (button != after_last_element_id) {
+               handleButtonEvent(button, evt_id, false);
+       }
+       /* handle hud button events */
+       else if (isReleaseHUDButton(evt_id)) {
+               /* nothing to do here */
+       } else if (m_settingsbar.isReleaseButton(evt_id)) {
+               /* nothing to do here */
+       } else if (m_rarecontrolsbar.isReleaseButton(evt_id)) {
+               /* nothing to do here */
+       }
+       /* handle the point used for moving view */
+       else if (evt_id == m_move_id) {
+               m_move_id = -1;
+
+               /* if this pointer issued a mouse event issue symmetric release here */
+               if (m_move_sent_as_mouse_event) {
+                       SEvent* translated = new SEvent;
+                       memset(translated,0,sizeof(SEvent));
+                       translated->EventType               = EET_MOUSE_INPUT_EVENT;
+                       translated->MouseInput.X            = m_move_downlocation.X;
+                       translated->MouseInput.Y            = m_move_downlocation.Y;
+                       translated->MouseInput.Shift        = false;
+                       translated->MouseInput.Control      = false;
+                       translated->MouseInput.ButtonStates = 0;
+                       translated->MouseInput.Event        = EMIE_LMOUSE_LEFT_UP;
+                       m_receiver->OnEvent(*translated);
+                       delete translated;
+               }
+               else {
+                       /* do double tap detection */
+                       doubleTapDetection();
+               }
+       }
+       else {
+               infostream
+                       << "TouchScreenGUI::translateEvent released unknown button: "
+                       << evt_id << std::endl;
+       }
+
+       for (std::vector<id_status>::iterator iter = m_known_ids.begin();
+                       iter != m_known_ids.end(); ++iter) {
+               if (iter->id == evt_id) {
+                       m_known_ids.erase(iter);
+                       break;
+               }
+       }
+}
+
+void TouchScreenGUI::translateEvent(const SEvent &event)
+{
+       if (!m_visible) {
+               infostream << "TouchScreenGUI::translateEvent got event but not visible?!" << std::endl;
+               return;
+       }
+
+       if (event.EventType != EET_TOUCH_INPUT_EVENT) {
+               return;
+       }
+
+       if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {
+
+               /* add to own copy of eventlist ...
+                * android would provide this information but irrlicht guys don't
+                * wanna design a efficient interface
+                */
+               id_status toadd;
+               toadd.id = event.TouchInput.ID;
+               toadd.X  = event.TouchInput.X;
+               toadd.Y  = event.TouchInput.Y;
+               m_known_ids.push_back(toadd);
+
+               int eventID = event.TouchInput.ID;
+
+               touch_gui_button_id button =
+                               getButtonID(event.TouchInput.X, event.TouchInput.Y);
+
+               /* handle button events */
+               if (button != after_last_element_id) {
+                       handleButtonEvent(button, eventID, true);
+                       m_settingsbar.deactivate();
+                       m_rarecontrolsbar.deactivate();
+               } else if (isHUDButton(event)) {
+                       m_settingsbar.deactivate();
+                       m_rarecontrolsbar.deactivate();
+                       /* already handled in isHUDButton() */
+               } else if (m_settingsbar.isButton(event)) {
+                       m_rarecontrolsbar.deactivate();
+                       /* already handled in isSettingsBarButton() */
+               } else if (m_rarecontrolsbar.isButton(event)) {
+                       m_settingsbar.deactivate();
+                       /* already handled in isSettingsBarButton() */
+               }
+               /* handle non button events */
+               else {
+                       m_settingsbar.deactivate();
+                       m_rarecontrolsbar.deactivate();
+                       /* if we don't already have a moving point make this the moving one */
+                       if (m_move_id == -1) {
+                               m_move_id                  = event.TouchInput.ID;
+                               m_move_has_really_moved    = false;
+                               m_move_downtime            = porting::getTimeMs();
+                               m_move_downlocation        = v2s32(event.TouchInput.X, event.TouchInput.Y);
+                               m_move_sent_as_mouse_event = false;
+                       }
+               }
+
+               m_pointerpos[event.TouchInput.ID] = v2s32(event.TouchInput.X, event.TouchInput.Y);
+       }
+       else if (event.TouchInput.Event == ETIE_LEFT_UP) {
+               verbosestream << "Up event for pointerid: " << event.TouchInput.ID << std::endl;
+               handleReleaseEvent(event.TouchInput.ID);
+       }
+       else {
+               assert(event.TouchInput.Event == ETIE_MOVED);
+               int move_idx = event.TouchInput.ID;
+
+               if (m_pointerpos[event.TouchInput.ID] ==
+                               v2s32(event.TouchInput.X, event.TouchInput.Y)) {
+                       return;
+               }
+
+               if (m_move_id != -1) {
+                       if ((event.TouchInput.ID == m_move_id) &&
+                               (!m_move_sent_as_mouse_event)) {
+
+                               double distance = sqrt(
+                                               (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) *
+                                               (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) +
+                                               (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y) *
+                                               (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y));
+
+                               if ((distance > g_settings->getU16("touchscreen_threshold")) ||
+                                               (m_move_has_really_moved)) {
+                                       m_move_has_really_moved = true;
+                                       s32 X = event.TouchInput.X;
+                                       s32 Y = event.TouchInput.Y;
+
+                                       // update camera_yaw and camera_pitch
+                                       s32 dx = X - m_pointerpos[event.TouchInput.ID].X;
+                                       s32 dy = Y - m_pointerpos[event.TouchInput.ID].Y;
+
+                                       /* adapt to similar behaviour as pc screen */
+                                       double d         = g_settings->getFloat("mouse_sensitivity") *4;
+                                       double old_yaw   = m_camera_yaw_change;
+                                       double old_pitch = m_camera_pitch;
+
+                                       m_camera_yaw_change -= dx * d;
+                                       m_camera_pitch = MYMIN(MYMAX(m_camera_pitch + (dy * d), -180), 180);
+
+                                       // update shootline
+                                       m_shootline = m_device
+                                                       ->getSceneManager()
+                                                       ->getSceneCollisionManager()
+                                                       ->getRayFromScreenCoordinates(v2s32(X, Y));
+                                       m_pointerpos[event.TouchInput.ID] = v2s32(X, Y);
+                               }
+                       }
+                       else if ((event.TouchInput.ID == m_move_id) &&
+                                       (m_move_sent_as_mouse_event)) {
+                               m_shootline = m_device
+                                               ->getSceneManager()
+                                               ->getSceneCollisionManager()
+                                               ->getRayFromScreenCoordinates(
+                                                               v2s32(event.TouchInput.X,event.TouchInput.Y));
+                       }
+               } else {
+                       handleChangedButton(event);
+               }
+       }
+}
+
+void TouchScreenGUI::handleChangedButton(const SEvent &event)
+{
+       for (unsigned int i = 0; i < after_last_element_id; i++) {
+
+               if (m_buttons[i].ids.empty()) {
+                       continue;
+               }
+               for (std::vector<int>::iterator iter = m_buttons[i].ids.begin();
+                               iter != m_buttons[i].ids.end(); ++iter) {
+
+                       if (event.TouchInput.ID == *iter) {
+
+                               int current_button_id =
+                                               getButtonID(event.TouchInput.X, event.TouchInput.Y);
+
+                               if (current_button_id == i) {
+                                       continue;
+                               }
+
+                               /* remove old button */
+                               handleButtonEvent((touch_gui_button_id) i,*iter,false);
+
+                               if (current_button_id == after_last_element_id) {
+                                       return;
+                               }
+                               handleButtonEvent((touch_gui_button_id) current_button_id,*iter,true);
+                               return;
+
+                       }
+               }
+       }
+
+       int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y);
+
+       if (current_button_id == after_last_element_id) {
+               return;
+       }
+
+       button_info* btn = &m_buttons[current_button_id];
+       if (std::find(btn->ids.begin(),btn->ids.end(), event.TouchInput.ID)
+                       == btn->ids.end())
+       {
+               handleButtonEvent((touch_gui_button_id) current_button_id,
+                               event.TouchInput.ID, true);
+       }
+
+}
+
+bool TouchScreenGUI::doubleTapDetection()
+{
+       m_key_events[0].down_time = m_key_events[1].down_time;
+       m_key_events[0].x         = m_key_events[1].x;
+       m_key_events[0].y         = m_key_events[1].y;
+       m_key_events[1].down_time = m_move_downtime;
+       m_key_events[1].x         = m_move_downlocation.X;
+       m_key_events[1].y         = m_move_downlocation.Y;
+
+       u64 delta = porting::getDeltaMs(m_key_events[0].down_time, porting::getTimeMs());
+       if (delta > 400)
+               return false;
+
+       double distance = sqrt(
+                       (m_key_events[0].x - m_key_events[1].x) * (m_key_events[0].x - m_key_events[1].x) +
+                       (m_key_events[0].y - m_key_events[1].y) * (m_key_events[0].y - m_key_events[1].y));
+
+
+       if (distance > (20 + g_settings->getU16("touchscreen_threshold")))
+               return false;
+
+       SEvent* translated = new SEvent();
+       memset(translated, 0, sizeof(SEvent));
+       translated->EventType               = EET_MOUSE_INPUT_EVENT;
+       translated->MouseInput.X            = m_key_events[0].x;
+       translated->MouseInput.Y            = m_key_events[0].y;
+       translated->MouseInput.Shift        = false;
+       translated->MouseInput.Control      = false;
+       translated->MouseInput.ButtonStates = EMBSM_RIGHT;
+
+       // update shootline
+       m_shootline = m_device
+                       ->getSceneManager()
+                       ->getSceneCollisionManager()
+                       ->getRayFromScreenCoordinates(v2s32(m_key_events[0].x, m_key_events[0].y));
+
+       translated->MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
+       verbosestream << "TouchScreenGUI::translateEvent right click press" << std::endl;
+       m_receiver->OnEvent(*translated);
+
+       translated->MouseInput.ButtonStates = 0;
+       translated->MouseInput.Event        = EMIE_RMOUSE_LEFT_UP;
+       verbosestream << "TouchScreenGUI::translateEvent right click release" << std::endl;
+       m_receiver->OnEvent(*translated);
+       delete translated;
+       return true;
+
+}
+
+TouchScreenGUI::~TouchScreenGUI()
+{
+       for (unsigned int i = 0; i < after_last_element_id; i++) {
+               button_info* btn = &m_buttons[i];
+               if (btn->guibutton != 0) {
+                       btn->guibutton->drop();
+                       btn->guibutton = NULL;
+               }
+       }
+}
+
+void TouchScreenGUI::step(float dtime)
+{
+       /* simulate keyboard repeats */
+       for (unsigned int i = 0; i < after_last_element_id; i++) {
+               button_info* btn = &m_buttons[i];
+
+               if (btn->ids.size() > 0) {
+                       btn->repeatcounter += dtime;
+
+                       /* in case we're moving around digging does not happen */
+                       if (m_move_id != -1)
+                               m_move_has_really_moved = true;
+
+                       if (btn->repeatcounter < btn->repeatdelay) continue;
+
+                       btn->repeatcounter              = 0;
+                       SEvent translated;
+                       memset(&translated, 0, sizeof(SEvent));
+                       translated.EventType            = irr::EET_KEY_INPUT_EVENT;
+                       translated.KeyInput.Key         = btn->keycode;
+                       translated.KeyInput.PressedDown = false;
+                       m_receiver->OnEvent(translated);
+
+                       translated.KeyInput.PressedDown = true;
+                       m_receiver->OnEvent(translated);
+               }
+       }
+
+       /* if a new placed pointer isn't moved for some time start digging */
+       if ((m_move_id != -1) &&
+                       (!m_move_has_really_moved) &&
+                       (!m_move_sent_as_mouse_event)) {
+
+               u64 delta = porting::getDeltaMs(m_move_downtime, porting::getTimeMs());
+
+               if (delta > MIN_DIG_TIME_MS) {
+                       m_shootline = m_device
+                                       ->getSceneManager()
+                                       ->getSceneCollisionManager()
+                                       ->getRayFromScreenCoordinates(
+                                                       v2s32(m_move_downlocation.X,m_move_downlocation.Y));
+
+                       SEvent translated;
+                       memset(&translated, 0, sizeof(SEvent));
+                       translated.EventType               = EET_MOUSE_INPUT_EVENT;
+                       translated.MouseInput.X            = m_move_downlocation.X;
+                       translated.MouseInput.Y            = m_move_downlocation.Y;
+                       translated.MouseInput.Shift        = false;
+                       translated.MouseInput.Control      = false;
+                       translated.MouseInput.ButtonStates = EMBSM_LEFT;
+                       translated.MouseInput.Event        = EMIE_LMOUSE_PRESSED_DOWN;
+                       verbosestream << "TouchScreenGUI::step left click press" << std::endl;
+                       m_receiver->OnEvent(translated);
+                       m_move_sent_as_mouse_event         = true;
+               }
+       }
+
+       m_settingsbar.step(dtime);
+       m_rarecontrolsbar.step(dtime);
+}
+
+void TouchScreenGUI::resetHud()
+{
+       m_hud_rects.clear();
+}
+
+void TouchScreenGUI::registerHudItem(int index, const rect<s32> &rect)
+{
+       m_hud_rects[index] = rect;
+}
+
+void TouchScreenGUI::Toggle(bool visible)
+{
+       m_visible = visible;
+       for (unsigned int i = 0; i < after_last_element_id; i++) {
+               button_info* btn = &m_buttons[i];
+               if (btn->guibutton != 0) {
+                       btn->guibutton->setVisible(visible);
+               }
+       }
+
+       /* clear all active buttons */
+       if (!visible) {
+               while (m_known_ids.size() > 0) {
+                       handleReleaseEvent(m_known_ids.begin()->id);
+               }
+
+               m_settingsbar.hide();
+               m_rarecontrolsbar.hide();
+       } else {
+               m_settingsbar.show();
+               m_rarecontrolsbar.show();
+       }
+}
+
+void TouchScreenGUI::hide()
+{
+       if (!m_visible)
+               return;
+
+       Toggle(false);
+}
+
+void TouchScreenGUI::show()
+{
+       if (m_visible)
+               return;
+
+       Toggle(true);
+}
diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h
new file mode 100644 (file)
index 0000000..da97381
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+Copyright (C) 2014 sapier
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include <IEventReceiver.h>
+#include <IGUIButton.h>
+#include <IGUIEnvironment.h>
+
+#include <map>
+#include <vector>
+
+#include "client/tile.h"
+#include "game.h"
+
+using namespace irr;
+using namespace irr::core;
+using namespace irr::gui;
+
+typedef enum {
+       forward_id = 0,
+       backward_id,
+       left_id,
+       right_id,
+       jump_id,
+       crunch_id,
+       after_last_element_id,
+       settings_starter_id,
+       rare_controls_starter_id,
+       fly_id,
+       noclip_id,
+       fast_id,
+       debug_id,
+       camera_id,
+       range_id,
+       chat_id,
+       inventory_id,
+       drop_id
+} touch_gui_button_id;
+
+typedef enum {
+       AHBB_Dir_Top_Bottom,
+       AHBB_Dir_Bottom_Top,
+       AHBB_Dir_Left_Right,
+       AHBB_Dir_Right_Left
+} autohide_button_bar_dir;
+
+#define MIN_DIG_TIME_MS 500
+#define MAX_TOUCH_COUNT 64
+#define BUTTON_REPEAT_DELAY 0.2f
+
+#define SETTINGS_BAR_Y_OFFSET 6.5
+#define RARE_CONTROLS_BAR_Y_OFFSET 4
+
+extern const char **touchgui_button_imagenames;
+
+struct button_info
+{
+       float repeatcounter;
+       float repeatdelay;
+       irr::EKEY_CODE keycode;
+       std::vector<int> ids;
+       IGUIButton *guibutton = nullptr;
+       bool immediate_release;
+};
+
+class AutoHideButtonBar
+{
+public:
+       AutoHideButtonBar(IrrlichtDevice *device, IEventReceiver *receiver);
+
+       void init(ISimpleTextureSource *tsrc, const char *starter_img, int button_id,
+                       v2s32 UpperLeft, v2s32 LowerRight, autohide_button_bar_dir dir,
+                       float timeout);
+
+       ~AutoHideButtonBar();
+
+       /* add button to be shown */
+       void addButton(touch_gui_button_id id, const wchar_t *caption,
+                       const char *btn_image);
+
+       /* detect settings bar button events */
+       bool isButton(const SEvent &event);
+
+       /* handle released hud buttons */
+       bool isReleaseButton(int eventID);
+
+       /* step handler */
+       void step(float dtime);
+
+       /* deactivate button bar */
+       void deactivate();
+
+       /* hide the whole buttonbar */
+       void hide();
+
+       /* unhide the buttonbar */
+       void show();
+
+private:
+       ISimpleTextureSource *m_texturesource = nullptr;
+       irr::video::IVideoDriver *m_driver;
+       IGUIEnvironment *m_guienv;
+       IEventReceiver *m_receiver;
+       button_info m_starter;
+       std::vector<button_info *> m_buttons;
+
+       v2s32 m_upper_left;
+       v2s32 m_lower_right;
+
+       /* show settings bar */
+       bool m_active = false;
+
+       bool m_visible = true;
+
+       /* settings bar timeout */
+       float m_timeout = 0.0f;
+       float m_timeout_value = 3.0f;
+       bool m_initialized = false;
+       autohide_button_bar_dir m_dir = AHBB_Dir_Right_Left;
+};
+
+class TouchScreenGUI
+{
+public:
+       TouchScreenGUI(IrrlichtDevice *device, IEventReceiver *receiver);
+       ~TouchScreenGUI();
+
+       void translateEvent(const SEvent &event);
+
+       void init(ISimpleTextureSource *tsrc);
+
+       double getYawChange()
+       {
+               double res = m_camera_yaw_change;
+               m_camera_yaw_change = 0;
+               return res;
+       }
+
+       double getPitch() { return m_camera_pitch; }
+
+       /*!
+        * Returns a line which describes what the player is pointing at.
+        * The starting point and looking direction are significant,
+        * the line should be scaled to match its length to the actual distance
+        * the player can reach.
+        * The line starts at the camera and ends on the camera's far plane.
+        * The coordinates do not contain the camera offset.
+        */
+       line3d<f32> getShootline() { return m_shootline; }
+
+       void step(float dtime);
+       void resetHud();
+       void registerHudItem(int index, const rect<s32> &rect);
+       void Toggle(bool visible);
+
+       void hide();
+       void show();
+
+private:
+       IrrlichtDevice *m_device;
+       IGUIEnvironment *m_guienv;
+       IEventReceiver *m_receiver;
+       ISimpleTextureSource *m_texturesource;
+       v2u32 m_screensize;
+       std::map<int, rect<s32>> m_hud_rects;
+       std::map<int, irr::EKEY_CODE> m_hud_ids;
+       bool m_visible; // is the gui visible
+
+       /* value in degree */
+       double m_camera_yaw_change = 0.0;
+       double m_camera_pitch = 0.0;
+
+       /*!
+        * A line starting at the camera and pointing towards the
+        * selected object.
+        * The line ends on the camera's far plane.
+        * The coordinates do not contain the camera offset.
+        */
+       line3d<f32> m_shootline;
+
+       int m_move_id = -1;
+       bool m_move_has_really_moved = false;
+       s64 m_move_downtime = 0;
+       bool m_move_sent_as_mouse_event = false;
+       v2s32 m_move_downlocation = v2s32(-10000, -10000);
+
+       button_info m_buttons[after_last_element_id];
+
+       /* gui button detection */
+       touch_gui_button_id getButtonID(s32 x, s32 y);
+
+       /* gui button by eventID */
+       touch_gui_button_id getButtonID(int eventID);
+
+       /* check if a button has changed */
+       void handleChangedButton(const SEvent &event);
+
+       /* initialize a button */
+       void initButton(touch_gui_button_id id, rect<s32> button_rect,
+                       std::wstring caption, bool immediate_release,
+                       float repeat_delay = BUTTON_REPEAT_DELAY);
+
+       struct id_status
+       {
+               int id;
+               int X;
+               int Y;
+       };
+
+       /* vector to store known ids and their initial touch positions*/
+       std::vector<id_status> m_known_ids;
+
+       /* handle a button event */
+       void handleButtonEvent(touch_gui_button_id bID, int eventID, bool action);
+
+       /* handle pressed hud buttons */
+       bool isHUDButton(const SEvent &event);
+
+       /* handle released hud buttons */
+       bool isReleaseHUDButton(int eventID);
+
+       /* handle double taps */
+       bool doubleTapDetection();
+
+       /* handle release event */
+       void handleReleaseEvent(int evt_id);
+
+       /* get size of regular gui control button */
+       int getGuiButtonSize();
+
+       /* doubleclick detection variables */
+       struct key_event
+       {
+               unsigned int down_time;
+               s32 x;
+               s32 y;
+       };
+
+       /* array for saving last known position of a pointer */
+       v2s32 m_pointerpos[MAX_TOUCH_COUNT];
+
+       /* array for doubletap detection */
+       key_event m_key_events[2];
+
+       /* settings bar */
+       AutoHideButtonBar m_settingsbar;
+
+       /* rare controls bar */
+       AutoHideButtonBar m_rarecontrolsbar;
+};
+extern TouchScreenGUI *g_touchscreengui;
diff --git a/src/guiChatConsole.cpp b/src/guiChatConsole.cpp
deleted file mode 100644 (file)
index b194834..0000000
+++ /dev/null
@@ -1,642 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "guiChatConsole.h"
-#include "chat.h"
-#include "client.h"
-#include "debug.h"
-#include "gettime.h"
-#include "keycode.h"
-#include "settings.h"
-#include "porting.h"
-#include "client/tile.h"
-#include "fontengine.h"
-#include "log.h"
-#include "gettext.h"
-#include <string>
-
-#if USE_FREETYPE
-       #include "irrlicht_changes/CGUITTFont.h"
-#endif
-
-inline u32 clamp_u8(s32 value)
-{
-       return (u32) MYMIN(MYMAX(value, 0), 255);
-}
-
-
-GUIChatConsole::GUIChatConsole(
-               gui::IGUIEnvironment* env,
-               gui::IGUIElement* parent,
-               s32 id,
-               ChatBackend* backend,
-               Client* client,
-               IMenuManager* menumgr
-):
-       IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
-                       core::rect<s32>(0,0,100,100)),
-       m_chat_backend(backend),
-       m_client(client),
-       m_menumgr(menumgr),
-       m_animate_time_old(porting::getTimeMs())
-{
-       // load background settings
-       s32 console_alpha = g_settings->getS32("console_alpha");
-       m_background_color.setAlpha(clamp_u8(console_alpha));
-
-       // load the background texture depending on settings
-       ITextureSource *tsrc = client->getTextureSource();
-       if (tsrc->isKnownSourceImage("background_chat.jpg")) {
-               m_background = tsrc->getTexture("background_chat.jpg");
-               m_background_color.setRed(255);
-               m_background_color.setGreen(255);
-               m_background_color.setBlue(255);
-       } else {
-               v3f console_color = g_settings->getV3F("console_color");
-               m_background_color.setRed(clamp_u8(myround(console_color.X)));
-               m_background_color.setGreen(clamp_u8(myround(console_color.Y)));
-               m_background_color.setBlue(clamp_u8(myround(console_color.Z)));
-       }
-
-       m_font = g_fontengine->getFont(FONT_SIZE_UNSPECIFIED, FM_Mono);
-
-       if (!m_font) {
-               errorstream << "GUIChatConsole: Unable to load mono font ";
-       } else {
-               core::dimension2d<u32> dim = m_font->getDimension(L"M");
-               m_fontsize = v2u32(dim.Width, dim.Height);
-               m_font->grab();
-       }
-       m_fontsize.X = MYMAX(m_fontsize.X, 1);
-       m_fontsize.Y = MYMAX(m_fontsize.Y, 1);
-
-       // set default cursor options
-       setCursor(true, true, 2.0, 0.1);
-}
-
-GUIChatConsole::~GUIChatConsole()
-{
-       if (m_font)
-               m_font->drop();
-}
-
-void GUIChatConsole::openConsole(f32 scale)
-{
-       assert(scale > 0.0f && scale <= 1.0f);
-
-       m_open = true;
-       m_desired_height_fraction = scale;
-       m_desired_height = scale * m_screensize.Y;
-       reformatConsole();
-       m_animate_time_old = porting::getTimeMs();
-       IGUIElement::setVisible(true);
-       Environment->setFocus(this);
-       m_menumgr->createdMenu(this);
-}
-
-bool GUIChatConsole::isOpen() const
-{
-       return m_open;
-}
-
-bool GUIChatConsole::isOpenInhibited() const
-{
-       return m_open_inhibited > 0;
-}
-
-void GUIChatConsole::closeConsole()
-{
-       m_open = false;
-       Environment->removeFocus(this);
-       m_menumgr->deletingMenu(this);
-}
-
-void GUIChatConsole::closeConsoleAtOnce()
-{
-       closeConsole();
-       m_height = 0;
-       recalculateConsolePosition();
-}
-
-f32 GUIChatConsole::getDesiredHeight() const
-{
-       return m_desired_height_fraction;
-}
-
-void GUIChatConsole::replaceAndAddToHistory(std::wstring line)
-{
-       ChatPrompt& prompt = m_chat_backend->getPrompt();
-       prompt.addToHistory(prompt.getLine());
-       prompt.replace(line);
-}
-
-
-void GUIChatConsole::setCursor(
-       bool visible, bool blinking, f32 blink_speed, f32 relative_height)
-{
-       if (visible)
-       {
-               if (blinking)
-               {
-                       // leave m_cursor_blink unchanged
-                       m_cursor_blink_speed = blink_speed;
-               }
-               else
-               {
-                       m_cursor_blink = 0x8000;  // on
-                       m_cursor_blink_speed = 0.0;
-               }
-       }
-       else
-       {
-               m_cursor_blink = 0;  // off
-               m_cursor_blink_speed = 0.0;
-       }
-       m_cursor_height = relative_height;
-}
-
-void GUIChatConsole::draw()
-{
-       if(!IsVisible)
-               return;
-
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-
-       // Check screen size
-       v2u32 screensize = driver->getScreenSize();
-       if (screensize != m_screensize)
-       {
-               // screen size has changed
-               // scale current console height to new window size
-               if (m_screensize.Y != 0)
-                       m_height = m_height * screensize.Y / m_screensize.Y;
-               m_screensize = screensize;
-               m_desired_height = m_desired_height_fraction * m_screensize.Y;
-               reformatConsole();
-       }
-
-       // Animation
-       u64 now = porting::getTimeMs();
-       animate(now - m_animate_time_old);
-       m_animate_time_old = now;
-
-       // Draw console elements if visible
-       if (m_height > 0)
-       {
-               drawBackground();
-               drawText();
-               drawPrompt();
-       }
-
-       gui::IGUIElement::draw();
-}
-
-void GUIChatConsole::reformatConsole()
-{
-       s32 cols = m_screensize.X / m_fontsize.X - 2; // make room for a margin (looks better)
-       s32 rows = m_desired_height / m_fontsize.Y - 1; // make room for the input prompt
-       if (cols <= 0 || rows <= 0)
-               cols = rows = 0;
-       recalculateConsolePosition();
-       m_chat_backend->reformat(cols, rows);
-}
-
-void GUIChatConsole::recalculateConsolePosition()
-{
-       core::rect<s32> rect(0, 0, m_screensize.X, m_height);
-       DesiredRect = rect;
-       recalculateAbsolutePosition(false);
-}
-
-void GUIChatConsole::animate(u32 msec)
-{
-       // animate the console height
-       s32 goal = m_open ? m_desired_height : 0;
-
-       // Set invisible if close animation finished (reset by openConsole)
-       // This function (animate()) is never called once its visibility becomes false so do not
-       //              actually set visible to false before the inhibited period is over
-       if (!m_open && m_height == 0 && m_open_inhibited == 0)
-               IGUIElement::setVisible(false);
-
-       if (m_height != goal)
-       {
-               s32 max_change = msec * m_screensize.Y * (m_height_speed / 1000.0);
-               if (max_change == 0)
-                       max_change = 1;
-
-               if (m_height < goal)
-               {
-                       // increase height
-                       if (m_height + max_change < goal)
-                               m_height += max_change;
-                       else
-                               m_height = goal;
-               }
-               else
-               {
-                       // decrease height
-                       if (m_height > goal + max_change)
-                               m_height -= max_change;
-                       else
-                               m_height = goal;
-               }
-
-               recalculateConsolePosition();
-       }
-
-       // blink the cursor
-       if (m_cursor_blink_speed != 0.0)
-       {
-               u32 blink_increase = 0x10000 * msec * (m_cursor_blink_speed / 1000.0);
-               if (blink_increase == 0)
-                       blink_increase = 1;
-               m_cursor_blink = ((m_cursor_blink + blink_increase) & 0xffff);
-       }
-
-       // decrease open inhibit counter
-       if (m_open_inhibited > msec)
-               m_open_inhibited -= msec;
-       else
-               m_open_inhibited = 0;
-}
-
-void GUIChatConsole::drawBackground()
-{
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-       if (m_background != NULL)
-       {
-               core::rect<s32> sourcerect(0, -m_height, m_screensize.X, 0);
-               driver->draw2DImage(
-                       m_background,
-                       v2s32(0, 0),
-                       sourcerect,
-                       &AbsoluteClippingRect,
-                       m_background_color,
-                       false);
-       }
-       else
-       {
-               driver->draw2DRectangle(
-                       m_background_color,
-                       core::rect<s32>(0, 0, m_screensize.X, m_height),
-                       &AbsoluteClippingRect);
-       }
-}
-
-void GUIChatConsole::drawText()
-{
-       if (m_font == NULL)
-               return;
-
-       ChatBuffer& buf = m_chat_backend->getConsoleBuffer();
-       for (u32 row = 0; row < buf.getRows(); ++row)
-       {
-               const ChatFormattedLine& line = buf.getFormattedLine(row);
-               if (line.fragments.empty())
-                       continue;
-
-               s32 line_height = m_fontsize.Y;
-               s32 y = row * line_height + m_height - m_desired_height;
-               if (y + line_height < 0)
-                       continue;
-
-               for (const ChatFormattedFragment &fragment : line.fragments) {
-                       s32 x = (fragment.column + 1) * m_fontsize.X;
-                       core::rect<s32> destrect(
-                               x, y, x + m_fontsize.X * fragment.text.size(), y + m_fontsize.Y);
-
-
-                       #if USE_FREETYPE
-                       // Draw colored text if FreeType is enabled
-                               irr::gui::CGUITTFont *tmp = dynamic_cast<irr::gui::CGUITTFont *>(m_font);
-                               tmp->draw(
-                                       fragment.text,
-                                       destrect,
-                                       video::SColor(255, 255, 255, 255),
-                                       false,
-                                       false,
-                                       &AbsoluteClippingRect);
-                       #else
-                       // Otherwise use standard text
-                               m_font->draw(
-                                       fragment.text.c_str(),
-                                       destrect,
-                                       video::SColor(255, 255, 255, 255),
-                                       false,
-                                       false,
-                                       &AbsoluteClippingRect);
-                       #endif
-               }
-       }
-}
-
-void GUIChatConsole::drawPrompt()
-{
-       if (!m_font)
-               return;
-
-       u32 row = m_chat_backend->getConsoleBuffer().getRows();
-       s32 line_height = m_fontsize.Y;
-       s32 y = row * line_height + m_height - m_desired_height;
-
-       ChatPrompt& prompt = m_chat_backend->getPrompt();
-       std::wstring prompt_text = prompt.getVisiblePortion();
-
-       // FIXME Draw string at once, not character by character
-       // That will only work with the cursor once we have a monospace font
-       for (u32 i = 0; i < prompt_text.size(); ++i)
-       {
-               wchar_t ws[2] = {prompt_text[i], 0};
-               s32 x = (1 + i) * m_fontsize.X;
-               core::rect<s32> destrect(
-                       x, y, x + m_fontsize.X, y + m_fontsize.Y);
-               m_font->draw(
-                       ws,
-                       destrect,
-                       video::SColor(255, 255, 255, 255),
-                       false,
-                       false,
-                       &AbsoluteClippingRect);
-       }
-
-       // Draw the cursor during on periods
-       if ((m_cursor_blink & 0x8000) != 0)
-       {
-               s32 cursor_pos = prompt.getVisibleCursorPosition();
-               if (cursor_pos >= 0)
-               {
-                       s32 cursor_len = prompt.getCursorLength();
-                       video::IVideoDriver* driver = Environment->getVideoDriver();
-                       s32 x = (1 + cursor_pos) * m_fontsize.X;
-                       core::rect<s32> destrect(
-                               x,
-                               y + m_fontsize.Y * (1.0 - m_cursor_height),
-                               x + m_fontsize.X * MYMAX(cursor_len, 1),
-                               y + m_fontsize.Y * (cursor_len ? m_cursor_height+1 : 1)
-                       );
-                       video::SColor cursor_color(255,255,255,255);
-                       driver->draw2DRectangle(
-                               cursor_color,
-                               destrect,
-                               &AbsoluteClippingRect);
-               }
-       }
-
-}
-
-bool GUIChatConsole::OnEvent(const SEvent& event)
-{
-
-       ChatPrompt &prompt = m_chat_backend->getPrompt();
-
-       if(event.EventType == EET_KEY_INPUT_EVENT && event.KeyInput.PressedDown)
-       {
-               // Key input
-               if (KeyPress(event.KeyInput) == getKeySetting("keymap_console")) {
-                       closeConsole();
-
-                       // inhibit open so the_game doesn't reopen immediately
-                       m_open_inhibited = 50;
-                       m_close_on_enter = false;
-                       return true;
-               }
-
-               if (event.KeyInput.Key == KEY_ESCAPE) {
-                       closeConsoleAtOnce();
-                       m_close_on_enter = false;
-                       // inhibit open so the_game doesn't reopen immediately
-                       m_open_inhibited = 1; // so the ESCAPE button doesn't open the "pause menu"
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_PRIOR)
-               {
-                       m_chat_backend->scrollPageUp();
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_NEXT)
-               {
-                       m_chat_backend->scrollPageDown();
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_RETURN)
-               {
-                       prompt.addToHistory(prompt.getLine());
-                       std::wstring text = prompt.replace(L"");
-                       m_client->typeChatMessage(text);
-                       if (m_close_on_enter) {
-                               closeConsoleAtOnce();
-                               m_close_on_enter = false;
-                       }
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_UP)
-               {
-                       // Up pressed
-                       // Move back in history
-                       prompt.historyPrev();
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_DOWN)
-               {
-                       // Down pressed
-                       // Move forward in history
-                       prompt.historyNext();
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_LEFT || event.KeyInput.Key == KEY_RIGHT)
-               {
-                       // Left/right pressed
-                       // Move/select character/word to the left depending on control and shift keys
-                       ChatPrompt::CursorOp op = event.KeyInput.Shift ?
-                               ChatPrompt::CURSOROP_SELECT :
-                               ChatPrompt::CURSOROP_MOVE;
-                       ChatPrompt::CursorOpDir dir = event.KeyInput.Key == KEY_LEFT ?
-                               ChatPrompt::CURSOROP_DIR_LEFT :
-                               ChatPrompt::CURSOROP_DIR_RIGHT;
-                       ChatPrompt::CursorOpScope scope = event.KeyInput.Control ?
-                               ChatPrompt::CURSOROP_SCOPE_WORD :
-                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
-                       prompt.cursorOperation(op, dir, scope);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_HOME)
-               {
-                       // Home pressed
-                       // move to beginning of line
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_MOVE,
-                               ChatPrompt::CURSOROP_DIR_LEFT,
-                               ChatPrompt::CURSOROP_SCOPE_LINE);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_END)
-               {
-                       // End pressed
-                       // move to end of line
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_MOVE,
-                               ChatPrompt::CURSOROP_DIR_RIGHT,
-                               ChatPrompt::CURSOROP_SCOPE_LINE);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_BACK)
-               {
-                       // Backspace or Ctrl-Backspace pressed
-                       // delete character / word to the left
-                       ChatPrompt::CursorOpScope scope =
-                               event.KeyInput.Control ?
-                               ChatPrompt::CURSOROP_SCOPE_WORD :
-                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_DELETE,
-                               ChatPrompt::CURSOROP_DIR_LEFT,
-                               scope);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_DELETE)
-               {
-                       // Delete or Ctrl-Delete pressed
-                       // delete character / word to the right
-                       ChatPrompt::CursorOpScope scope =
-                               event.KeyInput.Control ?
-                               ChatPrompt::CURSOROP_SCOPE_WORD :
-                               ChatPrompt::CURSOROP_SCOPE_CHARACTER;
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_DELETE,
-                               ChatPrompt::CURSOROP_DIR_RIGHT,
-                               scope);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_KEY_A && event.KeyInput.Control)
-               {
-                       // Ctrl-A pressed
-                       // Select all text
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_SELECT,
-                               ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
-                               ChatPrompt::CURSOROP_SCOPE_LINE);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_KEY_C && event.KeyInput.Control)
-               {
-                       // Ctrl-C pressed
-                       // Copy text to clipboard
-                       if (prompt.getCursorLength() <= 0)
-                               return true;
-                       std::wstring wselected = prompt.getSelection();
-                       std::string selected(wselected.begin(), wselected.end());
-                       Environment->getOSOperator()->copyToClipboard(selected.c_str());
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_KEY_V && event.KeyInput.Control)
-               {
-                       // Ctrl-V pressed
-                       // paste text from clipboard
-                       if (prompt.getCursorLength() > 0) {
-                               // Delete selected section of text
-                               prompt.cursorOperation(
-                                       ChatPrompt::CURSOROP_DELETE,
-                                       ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
-                                       ChatPrompt::CURSOROP_SCOPE_SELECTION);
-                       }
-                       IOSOperator *os_operator = Environment->getOSOperator();
-                       const c8 *text = os_operator->getTextFromClipboard();
-                       if (!text)
-                               return true;
-                       std::basic_string<unsigned char> str((const unsigned char*)text);
-                       prompt.input(std::wstring(str.begin(), str.end()));
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_KEY_X && event.KeyInput.Control)
-               {
-                       // Ctrl-X pressed
-                       // Cut text to clipboard
-                       if (prompt.getCursorLength() <= 0)
-                               return true;
-                       std::wstring wselected = prompt.getSelection();
-                       std::string selected(wselected.begin(), wselected.end());
-                       Environment->getOSOperator()->copyToClipboard(selected.c_str());
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_DELETE,
-                               ChatPrompt::CURSOROP_DIR_LEFT, // Ignored
-                               ChatPrompt::CURSOROP_SCOPE_SELECTION);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_KEY_U && event.KeyInput.Control)
-               {
-                       // Ctrl-U pressed
-                       // kill line to left end
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_DELETE,
-                               ChatPrompt::CURSOROP_DIR_LEFT,
-                               ChatPrompt::CURSOROP_SCOPE_LINE);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_KEY_K && event.KeyInput.Control)
-               {
-                       // Ctrl-K pressed
-                       // kill line to right end
-                       prompt.cursorOperation(
-                               ChatPrompt::CURSOROP_DELETE,
-                               ChatPrompt::CURSOROP_DIR_RIGHT,
-                               ChatPrompt::CURSOROP_SCOPE_LINE);
-                       return true;
-               }
-               else if(event.KeyInput.Key == KEY_TAB)
-               {
-                       // Tab or Shift-Tab pressed
-                       // Nick completion
-                       std::list<std::string> names = m_client->getConnectedPlayerNames();
-                       bool backwards = event.KeyInput.Shift;
-                       prompt.nickCompletion(names, backwards);
-                       return true;
-               } else if (!iswcntrl(event.KeyInput.Char) && !event.KeyInput.Control) {
-                       #if defined(__linux__) && (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9)
-                               wchar_t wc = L'_';
-                               mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
-                               prompt.input(wc);
-                       #else
-                               prompt.input(event.KeyInput.Char);
-                       #endif
-                       return true;
-               }
-       }
-       else if(event.EventType == EET_MOUSE_INPUT_EVENT)
-       {
-               if(event.MouseInput.Event == EMIE_MOUSE_WHEEL)
-               {
-                       s32 rows = myround(-3.0 * event.MouseInput.Wheel);
-                       m_chat_backend->scroll(rows);
-               }
-       }
-
-       return Parent ? Parent->OnEvent(event) : false;
-}
-
-void GUIChatConsole::setVisible(bool visible)
-{
-       m_open = visible;
-       IGUIElement::setVisible(visible);
-       if (!visible) {
-               m_height = 0;
-               recalculateConsolePosition();
-       }
-}
-
diff --git a/src/guiChatConsole.h b/src/guiChatConsole.h
deleted file mode 100644 (file)
index ef8a876..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include "chat.h"
-#include "config.h"
-
-class Client;
-
-class GUIChatConsole : public gui::IGUIElement
-{
-public:
-       GUIChatConsole(gui::IGUIEnvironment* env,
-                       gui::IGUIElement* parent,
-                       s32 id,
-                       ChatBackend* backend,
-                       Client* client,
-                       IMenuManager* menumgr);
-       virtual ~GUIChatConsole();
-
-       // Open the console (height = desired fraction of screen size)
-       // This doesn't open immediately but initiates an animation.
-       // You should call isOpenInhibited() before this.
-       void openConsole(f32 scale);
-
-       bool isOpen() const;
-
-       // Check if the console should not be opened at the moment
-       // This is to avoid reopening the console immediately after closing
-       bool isOpenInhibited() const;
-       // Close the console, equivalent to openConsole(0).
-       // This doesn't close immediately but initiates an animation.
-       void closeConsole();
-       // Close the console immediately, without animation.
-       void closeConsoleAtOnce();
-       // Set whether to close the console after the user presses enter.
-       void setCloseOnEnter(bool close) { m_close_on_enter = close; }
-
-       // Return the desired height (fraction of screen size)
-       // Zero if the console is closed or getting closed
-       f32 getDesiredHeight() const;
-
-       // Replace actual line when adding the actual to the history (if there is any)
-       void replaceAndAddToHistory(std::wstring line);
-
-       // Change how the cursor looks
-       void setCursor(
-               bool visible,
-               bool blinking = false,
-               f32 blink_speed = 1.0,
-               f32 relative_height = 1.0);
-
-       // Irrlicht draw method
-       virtual void draw();
-
-       bool canTakeFocus(gui::IGUIElement* element) { return false; }
-
-       virtual bool OnEvent(const SEvent& event);
-
-       virtual void setVisible(bool visible);
-
-private:
-       void reformatConsole();
-       void recalculateConsolePosition();
-
-       // These methods are called by draw
-       void animate(u32 msec);
-       void drawBackground();
-       void drawText();
-       void drawPrompt();
-
-private:
-       ChatBackend* m_chat_backend;
-       Client* m_client;
-       IMenuManager* m_menumgr;
-
-       // current screen size
-       v2u32 m_screensize;
-
-       // used to compute how much time passed since last animate()
-       u64 m_animate_time_old;
-
-       // should the console be opened or closed?
-       bool m_open = false;
-       // should it close after you press enter?
-       bool m_close_on_enter = false;
-       // current console height [pixels]
-       s32 m_height = 0;
-       // desired height [pixels]
-       f32 m_desired_height = 0.0f;
-       // desired height [screen height fraction]
-       f32 m_desired_height_fraction = 0.0f;
-       // console open/close animation speed [screen height fraction / second]
-       f32 m_height_speed = 5.0f;
-       // if nonzero, opening the console is inhibited [milliseconds]
-       u32 m_open_inhibited = 0;
-
-       // cursor blink frame (16-bit value)
-       // cursor is off during [0,32767] and on during [32768,65535]
-       u32 m_cursor_blink = 0;
-       // cursor blink speed [on/off toggles / second]
-       f32 m_cursor_blink_speed = 0.0f;
-       // cursor height [line height]
-       f32 m_cursor_height = 0.0f;
-
-       // background texture
-       video::ITexture *m_background = nullptr;
-       // background color (including alpha)
-       video::SColor m_background_color = video::SColor(255, 0, 0, 0);
-
-       // font
-       gui::IGUIFont *m_font = nullptr;
-       v2u32 m_fontsize;
-};
diff --git a/src/guiEditBoxWithScrollbar.cpp b/src/guiEditBoxWithScrollbar.cpp
deleted file mode 100644 (file)
index d4d2a0c..0000000
+++ /dev/null
@@ -1,1524 +0,0 @@
-// Copyright (C) 2002-2012 Nikolaus Gebhardt
-// Modified by Mustapha T.
-// This file is part of the "Irrlicht Engine".
-// For conditions of distribution and use, see copyright notice in irrlicht.h
-
-#include "guiEditBoxWithScrollbar.h"
-
-#include "IGUISkin.h"
-#include "IGUIEnvironment.h"
-#include "IGUIFont.h"
-#include "IVideoDriver.h"
-#include "rect.h"
-#include "porting.h"
-#include "Keycodes.h"
-
-
-/*
-todo:
-optional scrollbars [done]
-ctrl+left/right to select word
-double click/ctrl click: word select + drag to select whole words, triple click to select line
-optional? dragging selected text
-numerical
-*/
-
-
-//! constructor
-GUIEditBoxWithScrollBar::GUIEditBoxWithScrollBar(const wchar_t* text, bool border,
-       IGUIEnvironment* environment, IGUIElement* parent, s32 id,
-       const core::rect<s32>& rectangle, bool writable, bool has_vscrollbar)
-       : IGUIEditBox(environment, parent, id, rectangle), m_mouse_marking(false),
-       m_border(border), m_background(true), m_override_color_enabled(false), m_mark_begin(0), m_mark_end(0),
-       m_override_color(video::SColor(101, 255, 255, 255)), m_override_font(0), m_last_break_font(0),
-       m_operator(0), m_blink_start_time(0), m_cursor_pos(0), m_hscroll_pos(0), m_vscroll_pos(0), m_max(0),
-       m_word_wrap(false), m_multiline(false), m_autoscroll(true), m_passwordbox(false),
-       m_passwordchar(L'*'), m_halign(EGUIA_UPPERLEFT), m_valign(EGUIA_CENTER),
-       m_current_text_rect(0, 0, 1, 1), m_frame_rect(rectangle),
-       m_scrollbar_width(0), m_vscrollbar(NULL), m_writable(writable),
-       m_bg_color_used(false)
-{
-#ifdef _DEBUG
-       setDebugName("GUIEditBoxWithScrollBar");
-#endif
-
-
-       Text = text;
-
-       if (Environment)
-               m_operator = Environment->getOSOperator();
-
-       if (m_operator)
-               m_operator->grab();
-
-       // this element can be tabbed to
-       setTabStop(true);
-       setTabOrder(-1);
-
-       if (has_vscrollbar) {
-               createVScrollBar();
-       }
-
-       calculateFrameRect();
-       breakText();
-
-       calculateScrollPos();
-       setWritable(writable);
-}
-
-
-//! destructor
-GUIEditBoxWithScrollBar::~GUIEditBoxWithScrollBar()
-{
-       if (m_override_font)
-               m_override_font->drop();
-
-       if (m_operator)
-               m_operator->drop();
-
-       m_vscrollbar->remove();
-}
-
-
-//! Sets another skin independent font.
-void GUIEditBoxWithScrollBar::setOverrideFont(IGUIFont* font)
-{
-       if (m_override_font == font)
-               return;
-
-       if (m_override_font)
-               m_override_font->drop();
-
-       m_override_font = font;
-
-       if (m_override_font)
-               m_override_font->grab();
-
-       breakText();
-}
-
-//! Gets the override font (if any)
-IGUIFont * GUIEditBoxWithScrollBar::getOverrideFont() const
-{
-       return m_override_font;
-}
-
-//! Get the font which is used right now for drawing
-IGUIFont* GUIEditBoxWithScrollBar::getActiveFont() const
-{
-       if (m_override_font)
-               return m_override_font;
-       IGUISkin* skin = Environment->getSkin();
-       if (skin)
-               return skin->getFont();
-       return 0;
-}
-
-//! Sets another color for the text.
-void GUIEditBoxWithScrollBar::setOverrideColor(video::SColor color)
-{
-       m_override_color = color;
-       m_override_color_enabled = true;
-}
-
-
-video::SColor GUIEditBoxWithScrollBar::getOverrideColor() const
-{
-       return m_override_color;
-}
-
-
-//! Turns the border on or off
-void GUIEditBoxWithScrollBar::setDrawBorder(bool border)
-{
-       m_border = border;
-}
-
-//! Sets whether to draw the background
-void GUIEditBoxWithScrollBar::setDrawBackground(bool draw)
-{
-       m_background = draw;
-}
-
-//! Sets if the text should use the overide color or the color in the gui skin.
-void GUIEditBoxWithScrollBar::enableOverrideColor(bool enable)
-{
-       m_override_color_enabled = enable;
-}
-
-bool GUIEditBoxWithScrollBar::isOverrideColorEnabled() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return m_override_color_enabled;
-}
-
-//! Enables or disables word wrap
-void GUIEditBoxWithScrollBar::setWordWrap(bool enable)
-{
-       m_word_wrap = enable;
-       breakText();
-}
-
-
-void GUIEditBoxWithScrollBar::updateAbsolutePosition()
-{
-       core::rect<s32> old_absolute_rect(AbsoluteRect);
-       IGUIElement::updateAbsolutePosition();
-       if (old_absolute_rect != AbsoluteRect) {
-               calculateFrameRect();
-               breakText();
-               calculateScrollPos();
-       }
-}
-
-//! Checks if word wrap is enabled
-bool GUIEditBoxWithScrollBar::isWordWrapEnabled() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return m_word_wrap;
-}
-
-
-//! Enables or disables newlines.
-void GUIEditBoxWithScrollBar::setMultiLine(bool enable)
-{
-       m_multiline = enable;
-}
-
-
-//! Checks if multi line editing is enabled
-bool GUIEditBoxWithScrollBar::isMultiLineEnabled() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return m_multiline;
-}
-
-
-void GUIEditBoxWithScrollBar::setPasswordBox(bool password_box, wchar_t password_char)
-{
-       m_passwordbox = password_box;
-       if (m_passwordbox) {
-               m_passwordchar = password_char;
-               setMultiLine(false);
-               setWordWrap(false);
-               m_broken_text.clear();
-       }
-}
-
-
-bool GUIEditBoxWithScrollBar::isPasswordBox() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return m_passwordbox;
-}
-
-
-//! Sets text justification
-void GUIEditBoxWithScrollBar::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
-{
-       m_halign = horizontal;
-       m_valign = vertical;
-}
-
-
-//! called if an event happened.
-bool GUIEditBoxWithScrollBar::OnEvent(const SEvent& event)
-{
-       if (isEnabled()) {
-               switch (event.EventType)
-               {
-               case EET_GUI_EVENT:
-                       if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST) {
-                               if (event.GUIEvent.Caller == this) {
-                                       m_mouse_marking = false;
-                                       setTextMarkers(0, 0);
-                               }
-                       }
-                       break;
-               case EET_KEY_INPUT_EVENT:
-                       if (processKey(event))
-                               return true;
-                       break;
-               case EET_MOUSE_INPUT_EVENT:
-                       if (processMouse(event))
-                               return true;
-                       break;
-               default:
-                       break;
-               }
-       }
-
-       return IGUIElement::OnEvent(event);
-}
-
-
-bool GUIEditBoxWithScrollBar::processKey(const SEvent& event)
-{
-       if (!m_writable) {
-               return false;
-       }
-
-       if (!event.KeyInput.PressedDown)
-               return false;
-
-       bool text_changed = false;
-       s32 new_mark_begin = m_mark_begin;
-       s32 new_mark_end = m_mark_end;
-
-       // control shortcut handling
-
-       if (event.KeyInput.Control) {
-
-               // german backlash '\' entered with control + '?'
-               if (event.KeyInput.Char == '\\') {
-                       inputChar(event.KeyInput.Char);
-                       return true;
-               }
-
-               switch (event.KeyInput.Key) {
-               case KEY_KEY_A:
-                       // select all
-                       new_mark_begin = 0;
-                       new_mark_end = Text.size();
-                       break;
-               case KEY_KEY_C:
-                       // copy to clipboard
-                       if (!m_passwordbox && m_operator && m_mark_begin != m_mark_end)
-                       {
-                               const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
-                               const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
-                               core::stringc s;
-                               s = Text.subString(realmbgn, realmend - realmbgn).c_str();
-                               m_operator->copyToClipboard(s.c_str());
-                       }
-                       break;
-               case KEY_KEY_X:
-                       // cut to the clipboard
-                       if (!m_passwordbox && m_operator && m_mark_begin != m_mark_end) {
-                               const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
-                               const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
-                               // copy
-                               core::stringc sc;
-                               sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
-                               m_operator->copyToClipboard(sc.c_str());
-
-                               if (isEnabled())
-                               {
-                                       // delete
-                                       core::stringw s;
-                                       s = Text.subString(0, realmbgn);
-                                       s.append(Text.subString(realmend, Text.size() - realmend));
-                                       Text = s;
-
-                                       m_cursor_pos = realmbgn;
-                                       new_mark_begin = 0;
-                                       new_mark_end = 0;
-                                       text_changed = true;
-                               }
-                       }
-                       break;
-               case KEY_KEY_V:
-                       if (!isEnabled())
-                               break;
-
-                       // paste from the clipboard
-                       if (m_operator) {
-                               const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
-                               const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
-                               // add new character
-                               const c8* p = m_operator->getTextFromClipboard();
-                               if (p) {
-                                       if (m_mark_begin == m_mark_end) {
-                                               // insert text
-                                               core::stringw s = Text.subString(0, m_cursor_pos);
-                                               s.append(p);
-                                               s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
-
-                                               if (!m_max || s.size() <= m_max) // thx to Fish FH for fix
-                                               {
-                                                       Text = s;
-                                                       s = p;
-                                                       m_cursor_pos += s.size();
-                                               }
-                                       } else {
-                                               // replace text
-
-                                               core::stringw s = Text.subString(0, realmbgn);
-                                               s.append(p);
-                                               s.append(Text.subString(realmend, Text.size() - realmend));
-
-                                               if (!m_max || s.size() <= m_max)  // thx to Fish FH for fix
-                                               {
-                                                       Text = s;
-                                                       s = p;
-                                                       m_cursor_pos = realmbgn + s.size();
-                                               }
-                                       }
-                               }
-
-                               new_mark_begin = 0;
-                               new_mark_end = 0;
-                               text_changed = true;
-                       }
-                       break;
-               case KEY_HOME:
-                       // move/highlight to start of text
-                       if (event.KeyInput.Shift) {
-                               new_mark_end = m_cursor_pos;
-                               new_mark_begin = 0;
-                               m_cursor_pos = 0;
-                       } else {
-                               m_cursor_pos = 0;
-                               new_mark_begin = 0;
-                               new_mark_end = 0;
-                       }
-                       break;
-               case KEY_END:
-                       // move/highlight to end of text
-                       if (event.KeyInput.Shift) {
-                               new_mark_begin = m_cursor_pos;
-                               new_mark_end = Text.size();
-                               m_cursor_pos = 0;
-                       } else {
-                               m_cursor_pos = Text.size();
-                               new_mark_begin = 0;
-                               new_mark_end = 0;
-                       }
-                       break;
-               default:
-                       return false;
-               }
-       }
-       // default keyboard handling
-       else
-               switch (event.KeyInput.Key)     {
-               case KEY_END:
-               {
-                       s32 p = Text.size();
-                       if (m_word_wrap || m_multiline) {
-                               p = getLineFromPos(m_cursor_pos);
-                               p = m_broken_text_positions[p] + (s32)m_broken_text[p].size();
-                               if (p > 0 && (Text[p - 1] == L'\r' || Text[p - 1] == L'\n'))
-                                       p -= 1;
-                       }
-
-                       if (event.KeyInput.Shift) {
-                               if (m_mark_begin == m_mark_end)
-                                       new_mark_begin = m_cursor_pos;
-
-                               new_mark_end = p;
-                       } else {
-                               new_mark_begin = 0;
-                               new_mark_end = 0;
-                       }
-                       m_cursor_pos = p;
-                       m_blink_start_time = porting::getTimeMs();
-               }
-               break;
-               case KEY_HOME:
-               {
-
-                       s32 p = 0;
-                       if (m_word_wrap || m_multiline) {
-                               p = getLineFromPos(m_cursor_pos);
-                               p = m_broken_text_positions[p];
-                       }
-
-                       if (event.KeyInput.Shift) {
-                               if (m_mark_begin == m_mark_end)
-                                       new_mark_begin = m_cursor_pos;
-                               new_mark_end = p;
-                       } else {
-                               new_mark_begin = 0;
-                               new_mark_end = 0;
-                       }
-                       m_cursor_pos = p;
-                       m_blink_start_time = porting::getTimeMs();
-               }
-               break;
-               case KEY_RETURN:
-                       if (m_multiline) {
-                               inputChar(L'\n');
-                       } else {
-                               calculateScrollPos();
-                               sendGuiEvent(EGET_EDITBOX_ENTER);
-                       }
-                       return true;
-               case KEY_LEFT:
-
-                       if (event.KeyInput.Shift) {
-                               if (m_cursor_pos > 0) {
-                                       if (m_mark_begin == m_mark_end)
-                                               new_mark_begin = m_cursor_pos;
-
-                                       new_mark_end = m_cursor_pos - 1;
-                               }
-                       } else {
-                               new_mark_begin = 0;
-                               new_mark_end = 0;
-                       }
-
-                       if (m_cursor_pos > 0)
-                               m_cursor_pos--;
-                       m_blink_start_time = porting::getTimeMs();
-                       break;
-
-               case KEY_RIGHT:
-                       if (event.KeyInput.Shift) {
-                               if (Text.size() > (u32)m_cursor_pos) {
-                                       if (m_mark_begin == m_mark_end)
-                                               new_mark_begin = m_cursor_pos;
-
-                                       new_mark_end = m_cursor_pos + 1;
-                               }
-                       } else {
-                               new_mark_begin = 0;
-                               new_mark_end = 0;
-                       }
-
-                       if (Text.size() > (u32)m_cursor_pos)
-                               m_cursor_pos++;
-                       m_blink_start_time = porting::getTimeMs();
-                       break;
-               case KEY_UP:
-                       if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
-                               s32 lineNo = getLineFromPos(m_cursor_pos);
-                               s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos : (m_mark_begin > m_mark_end ? m_mark_begin : m_mark_end);
-                               if (lineNo > 0) {
-                                       s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
-                                       if ((s32)m_broken_text[lineNo - 1].size() < cp)
-                                               m_cursor_pos = m_broken_text_positions[lineNo - 1] + core::max_((u32)1, m_broken_text[lineNo - 1].size()) - 1;
-                                       else
-                                               m_cursor_pos = m_broken_text_positions[lineNo - 1] + cp;
-                               }
-
-                               if (event.KeyInput.Shift) {
-                                       new_mark_begin = mb;
-                                       new_mark_end = m_cursor_pos;
-                               } else {
-                                       new_mark_begin = 0;
-                                       new_mark_end = 0;
-                               }
-                       } else {
-                               return false;
-                       }
-                       break;
-               case KEY_DOWN:
-                       if (m_multiline || (m_word_wrap && m_broken_text.size() > 1)) {
-                               s32 lineNo = getLineFromPos(m_cursor_pos);
-                               s32 mb = (m_mark_begin == m_mark_end) ? m_cursor_pos : (m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end);
-                               if (lineNo < (s32)m_broken_text.size() - 1)
-                               {
-                                       s32 cp = m_cursor_pos - m_broken_text_positions[lineNo];
-                                       if ((s32)m_broken_text[lineNo + 1].size() < cp)
-                                               m_cursor_pos = m_broken_text_positions[lineNo + 1] + core::max_((u32)1, m_broken_text[lineNo + 1].size()) - 1;
-                                       else
-                                               m_cursor_pos = m_broken_text_positions[lineNo + 1] + cp;
-                               }
-
-                               if (event.KeyInput.Shift) {
-                                       new_mark_begin = mb;
-                                       new_mark_end = m_cursor_pos;
-                               } else {
-                                       new_mark_begin = 0;
-                                       new_mark_end = 0;
-                               }
-
-                       } else {
-                               return false;
-                       }
-                       break;
-
-               case KEY_BACK:
-                       if (!isEnabled())
-                               break;
-
-                       if (Text.size()) {
-                               core::stringw s;
-
-                               if (m_mark_begin != m_mark_end) {
-                                       // delete marked text
-                                       const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
-                                       const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
-                                       s = Text.subString(0, realmbgn);
-                                       s.append(Text.subString(realmend, Text.size() - realmend));
-                                       Text = s;
-
-                                       m_cursor_pos = realmbgn;
-                               } else {
-                                       // delete text behind cursor
-                                       if (m_cursor_pos > 0)
-                                               s = Text.subString(0, m_cursor_pos - 1);
-                                       else
-                                               s = L"";
-                                       s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
-                                       Text = s;
-                                       --m_cursor_pos;
-                               }
-
-                               if (m_cursor_pos < 0)
-                                       m_cursor_pos = 0;
-                               m_blink_start_time = porting::getTimeMs(); // os::Timer::getTime();
-                               new_mark_begin = 0;
-                               new_mark_end = 0;
-                               text_changed = true;
-                       }
-                       break;
-               case KEY_DELETE:
-                       if (!isEnabled())
-                               break;
-
-                       if (Text.size() != 0) {
-                               core::stringw s;
-
-                               if (m_mark_begin != m_mark_end) {
-                                       // delete marked text
-                                       const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
-                                       const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
-                                       s = Text.subString(0, realmbgn);
-                                       s.append(Text.subString(realmend, Text.size() - realmend));
-                                       Text = s;
-
-                                       m_cursor_pos = realmbgn;
-                               } else {
-                                       // delete text before cursor
-                                       s = Text.subString(0, m_cursor_pos);
-                                       s.append(Text.subString(m_cursor_pos + 1, Text.size() - m_cursor_pos - 1));
-                                       Text = s;
-                               }
-
-                               if (m_cursor_pos > (s32)Text.size())
-                                       m_cursor_pos = (s32)Text.size();
-
-                               m_blink_start_time = porting::getTimeMs(); // os::Timer::getTime();
-                               new_mark_begin = 0;
-                               new_mark_end = 0;
-                               text_changed = true;
-                       }
-                       break;
-
-               case KEY_ESCAPE:
-               case KEY_TAB:
-               case KEY_SHIFT:
-               case KEY_F1:
-               case KEY_F2:
-               case KEY_F3:
-               case KEY_F4:
-               case KEY_F5:
-               case KEY_F6:
-               case KEY_F7:
-               case KEY_F8:
-               case KEY_F9:
-               case KEY_F10:
-               case KEY_F11:
-               case KEY_F12:
-               case KEY_F13:
-               case KEY_F14:
-               case KEY_F15:
-               case KEY_F16:
-               case KEY_F17:
-               case KEY_F18:
-               case KEY_F19:
-               case KEY_F20:
-               case KEY_F21:
-               case KEY_F22:
-               case KEY_F23:
-               case KEY_F24:
-                       // ignore these keys
-                       return false;
-
-               default:
-                       inputChar(event.KeyInput.Char);
-                       return true;
-               }
-
-       // Set new text markers
-       setTextMarkers(new_mark_begin, new_mark_end);
-
-       // break the text if it has changed
-       if (text_changed) {
-               breakText();
-               calculateScrollPos();
-               sendGuiEvent(EGET_EDITBOX_CHANGED);
-       }
-       else
-       {
-               calculateScrollPos();
-       }
-
-       return true;
-}
-
-
-//! draws the element and its children
-void GUIEditBoxWithScrollBar::draw()
-{
-       if (!IsVisible)
-               return;
-
-       const bool focus = Environment->hasFocus(this);
-
-       IGUISkin* skin = Environment->getSkin();
-       if (!skin)
-               return;
-
-       video::SColor default_bg_color;
-       video::SColor bg_color;
-
-       default_bg_color = m_writable ? skin->getColor(EGDC_WINDOW) : video::SColor(0);
-       bg_color = m_bg_color_used ? m_bg_color : default_bg_color;
-
-       if (!m_border && m_background) {
-               skin->draw2DRectangle(this, bg_color, AbsoluteRect, &AbsoluteClippingRect);
-       }
-
-       // draw the border
-
-       if (m_border) {
-
-               if (m_writable) {
-                       skin->draw3DSunkenPane(this, bg_color, false, m_background,
-                               AbsoluteRect, &AbsoluteClippingRect);
-               }
-
-               calculateFrameRect();
-       }
-
-       core::rect<s32> local_clip_rect = m_frame_rect;
-       local_clip_rect.clipAgainst(AbsoluteClippingRect);
-
-       // draw the text
-
-       IGUIFont* font = getActiveFont();
-
-       s32 cursor_line = 0;
-       s32 charcursorpos = 0;
-
-       if (font) {
-               if (m_last_break_font != font) {
-                       breakText();
-               }
-
-               // calculate cursor pos
-
-               core::stringw *txt_line = &Text;
-               s32 start_pos = 0;
-
-               core::stringw s, s2;
-
-               // get mark position
-               const bool ml = (!m_passwordbox && (m_word_wrap || m_multiline));
-               const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
-               const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-               const s32 hline_start = ml ? getLineFromPos(realmbgn) : 0;
-               const s32 hline_count = ml ? getLineFromPos(realmend) - hline_start + 1 : 1;
-               const s32 line_count = ml ? m_broken_text.size() : 1;
-
-               // Save the override color information.
-               // Then, alter it if the edit box is disabled.
-               const bool prevOver = m_override_color_enabled;
-               const video::SColor prevColor = m_override_color;
-
-               if (Text.size()) {
-                       if (!isEnabled() && !m_override_color_enabled) {
-                               m_override_color_enabled = true;
-                               m_override_color = skin->getColor(EGDC_GRAY_TEXT);
-                       }
-
-                       for (s32 i = 0; i < line_count; ++i) {
-                               setTextRect(i);
-
-                               // clipping test - don't draw anything outside the visible area
-                               core::rect<s32> c = local_clip_rect;
-                               c.clipAgainst(m_current_text_rect);
-                               if (!c.isValid())
-                                       continue;
-
-                               // get current line
-                               if (m_passwordbox) {
-                                       if (m_broken_text.size() != 1) {
-                                               m_broken_text.clear();
-                                               m_broken_text.push_back(core::stringw());
-                                       }
-                                       if (m_broken_text[0].size() != Text.size()){
-                                               m_broken_text[0] = Text;
-                                               for (u32 q = 0; q < Text.size(); ++q)
-                                               {
-                                                       m_broken_text[0][q] = m_passwordchar;
-                                               }
-                                       }
-                                       txt_line = &m_broken_text[0];
-                                       start_pos = 0;
-                               } else {
-                                       txt_line = ml ? &m_broken_text[i] : &Text;
-                                       start_pos = ml ? m_broken_text_positions[i] : 0;
-                               }
-
-
-                               // draw normal text
-                               font->draw(txt_line->c_str(), m_current_text_rect,
-                                       m_override_color_enabled ? m_override_color : skin->getColor(EGDC_BUTTON_TEXT),
-                                       false, true, &local_clip_rect);
-
-                               // draw mark and marked text
-                               if (focus && m_mark_begin != m_mark_end && i >= hline_start && i < hline_start + hline_count) {
-
-                                       s32 mbegin = 0, mend = 0;
-                                       s32 lineStartPos = 0, lineEndPos = txt_line->size();
-
-                                       if (i == hline_start) {
-                                               // highlight start is on this line
-                                               s = txt_line->subString(0, realmbgn - start_pos);
-                                               mbegin = font->getDimension(s.c_str()).Width;
-
-                                               // deal with kerning
-                                               mbegin += font->getKerningWidth(
-                                                       &((*txt_line)[realmbgn - start_pos]),
-                                                       realmbgn - start_pos > 0 ? &((*txt_line)[realmbgn - start_pos - 1]) : 0);
-
-                                               lineStartPos = realmbgn - start_pos;
-                                       }
-                                       if (i == hline_start + hline_count - 1) {
-                                               // highlight end is on this line
-                                               s2 = txt_line->subString(0, realmend - start_pos);
-                                               mend = font->getDimension(s2.c_str()).Width;
-                                               lineEndPos = (s32)s2.size();
-                                       } else {
-                                               mend = font->getDimension(txt_line->c_str()).Width;
-                                       }
-
-
-                                       m_current_text_rect.UpperLeftCorner.X += mbegin;
-                                       m_current_text_rect.LowerRightCorner.X = m_current_text_rect.UpperLeftCorner.X + mend - mbegin;
-
-
-                                       // draw mark
-                                       skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), m_current_text_rect, &local_clip_rect);
-
-                                       // draw marked text
-                                       s = txt_line->subString(lineStartPos, lineEndPos - lineStartPos);
-
-                                       if (s.size())
-                                               font->draw(s.c_str(), m_current_text_rect,
-                                                       m_override_color_enabled ? m_override_color : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
-                                                       false, true, &local_clip_rect);
-
-                               }
-                       }
-
-                       // Return the override color information to its previous settings.
-                       m_override_color_enabled = prevOver;
-                       m_override_color = prevColor;
-               }
-
-               // draw cursor
-               if (IsEnabled && m_writable) {
-                       if (m_word_wrap || m_multiline) {
-                               cursor_line = getLineFromPos(m_cursor_pos);
-                               txt_line = &m_broken_text[cursor_line];
-                               start_pos = m_broken_text_positions[cursor_line];
-                       }
-                       s = txt_line->subString(0, m_cursor_pos - start_pos);
-                       charcursorpos = font->getDimension(s.c_str()).Width +
-                               font->getKerningWidth(L"_", m_cursor_pos - start_pos > 0 ? &((*txt_line)[m_cursor_pos - start_pos - 1]) : 0);
-
-                       if (focus && (porting::getTimeMs() - m_blink_start_time) % 700 < 350) {
-                               setTextRect(cursor_line);
-                               m_current_text_rect.UpperLeftCorner.X += charcursorpos;
-
-                               font->draw(L"_", m_current_text_rect,
-                                       m_override_color_enabled ? m_override_color : skin->getColor(EGDC_BUTTON_TEXT),
-                                       false, true, &local_clip_rect);
-                       }
-               }
-       }
-
-       // draw children
-       IGUIElement::draw();
-}
-
-
-//! Sets the new caption of this element.
-void GUIEditBoxWithScrollBar::setText(const wchar_t* text)
-{
-       Text = text;
-       if (u32(m_cursor_pos) > Text.size())
-               m_cursor_pos = Text.size();
-       m_hscroll_pos = 0;
-       breakText();
-}
-
-
-//! Enables or disables automatic scrolling with cursor position
-//! \param enable: If set to true, the text will move around with the cursor position
-void GUIEditBoxWithScrollBar::setAutoScroll(bool enable)
-{
-       m_autoscroll = enable;
-}
-
-
-//! Checks to see if automatic scrolling is enabled
-//! \return true if automatic scrolling is enabled, false if not
-bool GUIEditBoxWithScrollBar::isAutoScrollEnabled() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return m_autoscroll;
-}
-
-
-//! Gets the area of the text in the edit box
-//! \return Returns the size in pixels of the text
-core::dimension2du GUIEditBoxWithScrollBar::getTextDimension()
-{
-       core::rect<s32> ret;
-
-       setTextRect(0);
-       ret = m_current_text_rect;
-
-       for (u32 i = 1; i < m_broken_text.size(); ++i) {
-               setTextRect(i);
-               ret.addInternalPoint(m_current_text_rect.UpperLeftCorner);
-               ret.addInternalPoint(m_current_text_rect.LowerRightCorner);
-       }
-
-       return core::dimension2du(ret.getSize());
-}
-
-
-//! Sets the maximum amount of characters which may be entered in the box.
-//! \param max: Maximum amount of characters. If 0, the character amount is
-//! infinity.
-void GUIEditBoxWithScrollBar::setMax(u32 max)
-{
-       m_max = max;
-
-       if (Text.size() > m_max && m_max != 0)
-               Text = Text.subString(0, m_max);
-}
-
-
-//! Returns maximum amount of characters, previously set by setMax();
-u32 GUIEditBoxWithScrollBar::getMax() const
-{
-       return m_max;
-}
-
-
-bool GUIEditBoxWithScrollBar::processMouse(const SEvent& event)
-{
-       switch (event.MouseInput.Event)
-       {
-       case irr::EMIE_LMOUSE_LEFT_UP:
-               if (Environment->hasFocus(this)) {
-                       m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-                       if (m_mouse_marking) {
-                               setTextMarkers(m_mark_begin, m_cursor_pos);
-                       }
-                       m_mouse_marking = false;
-                       calculateScrollPos();
-                       return true;
-               }
-               break;
-       case irr::EMIE_MOUSE_MOVED:
-       {
-               if (m_mouse_marking) {
-                       m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-                       setTextMarkers(m_mark_begin, m_cursor_pos);
-                       calculateScrollPos();
-                       return true;
-               }
-       }
-       break;
-       case EMIE_LMOUSE_PRESSED_DOWN:
-
-               if (!Environment->hasFocus(this)) {
-                       m_blink_start_time = porting::getTimeMs();
-                       m_mouse_marking = true;
-                       m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-                       setTextMarkers(m_cursor_pos, m_cursor_pos);
-                       calculateScrollPos();
-                       return true;
-               } else {
-                       if (!AbsoluteClippingRect.isPointInside(
-                               core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y))) {
-                               return false;
-                       } else {
-                               // move cursor
-                               m_cursor_pos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-
-                               s32 newMarkBegin = m_mark_begin;
-                               if (!m_mouse_marking)
-                                       newMarkBegin = m_cursor_pos;
-
-                               m_mouse_marking = true;
-                               setTextMarkers(newMarkBegin, m_cursor_pos);
-                               calculateScrollPos();
-                               return true;
-                       }
-               }
-       default:
-               break;
-       }
-
-       return false;
-}
-
-
-s32 GUIEditBoxWithScrollBar::getCursorPos(s32 x, s32 y)
-{
-       IGUIFont* font = getActiveFont();
-
-       const u32 line_count = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1;
-
-       core::stringw *txt_line = 0;
-       s32 start_pos = 0;
-       x += 3;
-
-       for (u32 i = 0; i < line_count; ++i) {
-               setTextRect(i);
-               if (i == 0 && y < m_current_text_rect.UpperLeftCorner.Y)
-                       y = m_current_text_rect.UpperLeftCorner.Y;
-               if (i == line_count - 1 && y > m_current_text_rect.LowerRightCorner.Y)
-                       y = m_current_text_rect.LowerRightCorner.Y;
-
-               // is it inside this region?
-               if (y >= m_current_text_rect.UpperLeftCorner.Y && y <= m_current_text_rect.LowerRightCorner.Y) {
-                       // we've found the clicked line
-                       txt_line = (m_word_wrap || m_multiline) ? &m_broken_text[i] : &Text;
-                       start_pos = (m_word_wrap || m_multiline) ? m_broken_text_positions[i] : 0;
-                       break;
-               }
-       }
-
-       if (x < m_current_text_rect.UpperLeftCorner.X)
-               x = m_current_text_rect.UpperLeftCorner.X;
-
-       if (!txt_line)
-               return 0;
-
-       s32 idx = font->getCharacterFromPos(txt_line->c_str(), x - m_current_text_rect.UpperLeftCorner.X);
-
-       // click was on or left of the line
-       if (idx != -1)
-               return idx + start_pos;
-
-       // click was off the right edge of the line, go to end.
-       return txt_line->size() + start_pos;
-}
-
-
-//! Breaks the single text line.
-void GUIEditBoxWithScrollBar::breakText()
-{
-       if ((!m_word_wrap && !m_multiline))
-               return;
-
-       m_broken_text.clear(); // need to reallocate :/
-       m_broken_text_positions.clear();
-
-       IGUIFont* font = getActiveFont();
-       if (!font)
-               return;
-
-       m_last_break_font = font;
-
-       core::stringw line;
-       core::stringw word;
-       core::stringw whitespace;
-       s32 last_line_start = 0;
-       s32 size = Text.size();
-       s32 length = 0;
-       s32 el_width = RelativeRect.getWidth() - 6;
-       wchar_t c;
-
-       for (s32 i = 0; i < size; ++i) {
-               c = Text[i];
-               bool line_break = false;
-
-               if (c == L'\r') { // Mac or Windows breaks
-
-                       line_break = true;
-                       c = 0;
-                       if (Text[i + 1] == L'\n') { // Windows breaks
-                               // TODO: I (Michael) think that we shouldn't change the text given by the user for whatever reason.
-                               // Instead rework the cursor positioning to be able to handle this (but not in stable release
-                               // branch as users might already expect this behavior).
-                               Text.erase(i + 1);
-                               --size;
-                               if (m_cursor_pos > i)
-                                       --m_cursor_pos;
-                       }
-               } else if (c == L'\n') { // Unix breaks
-                       line_break = true;
-                       c = 0;
-               }
-
-               // don't break if we're not a multi-line edit box
-               if (!m_multiline)
-                       line_break = false;
-
-               if (c == L' ' || c == 0 || i == (size - 1)) {
-                       // here comes the next whitespace, look if
-                       // we can break the last word to the next line
-                       // We also break whitespace, otherwise cursor would vanish beside the right border.
-                       s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
-                       s32 worldlgth = font->getDimension(word.c_str()).Width;
-
-                       if (m_word_wrap && length + worldlgth + whitelgth > el_width && line.size() > 0) {
-                               // break to next line
-                               length = worldlgth;
-                               m_broken_text.push_back(line);
-                               m_broken_text_positions.push_back(last_line_start);
-                               last_line_start = i - (s32)word.size();
-                               line = word;
-                       } else {
-                               // add word to line
-                               line += whitespace;
-                               line += word;
-                               length += whitelgth + worldlgth;
-                       }
-
-                       word = L"";
-                       whitespace = L"";
-
-
-                       if (c)
-                               whitespace += c;
-
-                       // compute line break
-                       if (line_break) {
-                               line += whitespace;
-                               line += word;
-                               m_broken_text.push_back(line);
-                               m_broken_text_positions.push_back(last_line_start);
-                               last_line_start = i + 1;
-                               line = L"";
-                               word = L"";
-                               whitespace = L"";
-                               length = 0;
-                       }
-               } else {
-                       // yippee this is a word..
-                       word += c;
-               }
-       }
-
-       line += whitespace;
-       line += word;
-       m_broken_text.push_back(line);
-       m_broken_text_positions.push_back(last_line_start);
-}
-
-// TODO: that function does interpret VAlign according to line-index (indexed line is placed on top-center-bottom)
-// but HAlign according to line-width (pixels) and not by row.
-// Intuitively I suppose HAlign handling is better as VScrollPos should handle the line-scrolling.
-// But please no one change this without also rewriting (and this time fucking testing!!!) autoscrolling (I noticed this when fixing the old autoscrolling).
-void GUIEditBoxWithScrollBar::setTextRect(s32 line)
-{
-       if (line < 0)
-               return;
-
-       IGUIFont* font = getActiveFont();
-       if (!font)
-               return;
-
-       core::dimension2du d;
-
-       // get text dimension
-       const u32 line_count = (m_word_wrap || m_multiline) ? m_broken_text.size() : 1;
-       if (m_word_wrap || m_multiline) {
-               d = font->getDimension(m_broken_text[line].c_str());
-       } else {
-               d = font->getDimension(Text.c_str());
-               d.Height = AbsoluteRect.getHeight();
-       }
-       d.Height += font->getKerningHeight();
-
-       // justification
-       switch (m_halign) {
-       case EGUIA_CENTER:
-               // align to h centre
-               m_current_text_rect.UpperLeftCorner.X = (m_frame_rect.getWidth() / 2) - (d.Width / 2);
-               m_current_text_rect.LowerRightCorner.X = (m_frame_rect.getWidth() / 2) + (d.Width / 2);
-               break;
-       case EGUIA_LOWERRIGHT:
-               // align to right edge
-               m_current_text_rect.UpperLeftCorner.X = m_frame_rect.getWidth() - d.Width;
-               m_current_text_rect.LowerRightCorner.X = m_frame_rect.getWidth();
-               break;
-       default:
-               // align to left edge
-               m_current_text_rect.UpperLeftCorner.X = 0;
-               m_current_text_rect.LowerRightCorner.X = d.Width;
-
-       }
-
-       switch (m_valign) {
-       case EGUIA_CENTER:
-               // align to v centre
-               m_current_text_rect.UpperLeftCorner.Y =
-                       (m_frame_rect.getHeight() / 2) - (line_count*d.Height) / 2 + d.Height*line;
-               break;
-       case EGUIA_LOWERRIGHT:
-               // align to bottom edge
-               m_current_text_rect.UpperLeftCorner.Y =
-                       m_frame_rect.getHeight() - line_count*d.Height + d.Height*line;
-               break;
-       default:
-               // align to top edge
-               m_current_text_rect.UpperLeftCorner.Y = d.Height*line;
-               break;
-       }
-
-       m_current_text_rect.UpperLeftCorner.X -= m_hscroll_pos;
-       m_current_text_rect.LowerRightCorner.X -= m_hscroll_pos;
-       m_current_text_rect.UpperLeftCorner.Y -= m_vscroll_pos;
-       m_current_text_rect.LowerRightCorner.Y = m_current_text_rect.UpperLeftCorner.Y + d.Height;
-
-       m_current_text_rect += m_frame_rect.UpperLeftCorner;
-}
-
-
-s32 GUIEditBoxWithScrollBar::getLineFromPos(s32 pos)
-{
-       if (!m_word_wrap && !m_multiline)
-               return 0;
-
-       s32 i = 0;
-       while (i < (s32)m_broken_text_positions.size()) {
-               if (m_broken_text_positions[i] > pos)
-                       return i - 1;
-               ++i;
-       }
-       return (s32)m_broken_text_positions.size() - 1;
-}
-
-
-void GUIEditBoxWithScrollBar::inputChar(wchar_t c)
-{
-       if (!isEnabled())
-               return;
-
-       if (c != 0)     {
-               if (Text.size() < m_max || m_max == 0) {
-                       core::stringw s;
-
-                       if (m_mark_begin != m_mark_end) {
-                               // replace marked text
-                               const s32 realmbgn = m_mark_begin < m_mark_end ? m_mark_begin : m_mark_end;
-                               const s32 realmend = m_mark_begin < m_mark_end ? m_mark_end : m_mark_begin;
-
-                               s = Text.subString(0, realmbgn);
-                               s.append(c);
-                               s.append(Text.subString(realmend, Text.size() - realmend));
-                               Text = s;
-                               m_cursor_pos = realmbgn + 1;
-                       } else {
-                               // add new character
-                               s = Text.subString(0, m_cursor_pos);
-                               s.append(c);
-                               s.append(Text.subString(m_cursor_pos, Text.size() - m_cursor_pos));
-                               Text = s;
-                               ++m_cursor_pos;
-                       }
-
-                       m_blink_start_time = porting::getTimeMs();
-                       setTextMarkers(0, 0);
-               }
-       }
-       breakText();
-       calculateScrollPos();
-       sendGuiEvent(EGET_EDITBOX_CHANGED);
-}
-
-// calculate autoscroll
-void GUIEditBoxWithScrollBar::calculateScrollPos()
-{
-       if (!m_autoscroll)
-               return;
-
-       IGUISkin* skin = Environment->getSkin();
-       if (!skin)
-               return;
-       IGUIFont* font = m_override_font ? m_override_font : skin->getFont();
-       if (!font)
-               return;
-
-       s32 curs_line = getLineFromPos(m_cursor_pos);
-       if (curs_line < 0)
-               return;
-       setTextRect(curs_line);
-       const bool has_broken_text = m_multiline || m_word_wrap;
-
-       // Check horizonal scrolling
-       // NOTE: Calculations different to vertical scrolling because setTextRect interprets VAlign relative to line but HAlign not relative to row
-       {
-               // get cursor position
-               IGUIFont* font = getActiveFont();
-               if (!font)
-                       return;
-
-               // get cursor area
-               irr::u32 cursor_width = font->getDimension(L"_").Width;
-               core::stringw *txt_line = has_broken_text ? &m_broken_text[curs_line] : &Text;
-               s32 cpos = has_broken_text ? m_cursor_pos - m_broken_text_positions[curs_line] : m_cursor_pos;  // column
-               s32 cstart = font->getDimension(txt_line->subString(0, cpos).c_str()).Width;            // pixels from text-start
-               s32 cend = cstart + cursor_width;
-               s32 txt_width = font->getDimension(txt_line->c_str()).Width;
-
-               if (txt_width < m_frame_rect.getWidth()) {
-                       // TODO: Needs a clean left and right gap removal depending on HAlign, similar to vertical scrolling tests for top/bottom.
-                       // This check just fixes the case where it was most noticable (text smaller than clipping area).
-
-                       m_hscroll_pos = 0;
-                       setTextRect(curs_line);
-               }
-
-               if (m_current_text_rect.UpperLeftCorner.X + cstart < m_frame_rect.UpperLeftCorner.X) {
-                       // cursor to the left of the clipping area
-                       m_hscroll_pos -= m_frame_rect.UpperLeftCorner.X - (m_current_text_rect.UpperLeftCorner.X + cstart);
-                       setTextRect(curs_line);
-
-                       // TODO: should show more characters to the left when we're scrolling left
-                       //      and the cursor reaches the border.
-               } else if (m_current_text_rect.UpperLeftCorner.X + cend > m_frame_rect.LowerRightCorner.X)      {
-                       // cursor to the right of the clipping area
-                       m_hscroll_pos += (m_current_text_rect.UpperLeftCorner.X + cend) - m_frame_rect.LowerRightCorner.X;
-                       setTextRect(curs_line);
-               }
-       }
-
-       // calculate vertical scrolling
-       if (has_broken_text) {
-               irr::u32 line_height = font->getDimension(L"A").Height + font->getKerningHeight();
-               // only up to 1 line fits?
-               if (line_height >= (irr::u32)m_frame_rect.getHeight()) {
-                       m_vscroll_pos = 0;
-                       setTextRect(curs_line);
-                       s32 unscrolledPos = m_current_text_rect.UpperLeftCorner.Y;
-                       s32 pivot = m_frame_rect.UpperLeftCorner.Y;
-                       switch (m_valign) {
-                       case EGUIA_CENTER:
-                               pivot += m_frame_rect.getHeight() / 2;
-                               unscrolledPos += line_height / 2;
-                               break;
-                       case EGUIA_LOWERRIGHT:
-                               pivot += m_frame_rect.getHeight();
-                               unscrolledPos += line_height;
-                               break;
-                       default:
-                               break;
-                       }
-                       m_vscroll_pos = unscrolledPos - pivot;
-                       setTextRect(curs_line);
-               } else {
-                       // First 2 checks are necessary when people delete lines
-                       setTextRect(0);
-                       if (m_current_text_rect.UpperLeftCorner.Y > m_frame_rect.UpperLeftCorner.Y && m_valign != EGUIA_LOWERRIGHT) {
-                               // first line is leaving a gap on top
-                               m_vscroll_pos = 0;
-                       } else if (m_valign != EGUIA_UPPERLEFT) {
-                               u32 lastLine = m_broken_text_positions.empty() ? 0 : m_broken_text_positions.size() - 1;
-                               setTextRect(lastLine);
-                               if (m_current_text_rect.LowerRightCorner.Y < m_frame_rect.LowerRightCorner.Y)
-                               {
-                                       // last line is leaving a gap on bottom
-                                       m_vscroll_pos -= m_frame_rect.LowerRightCorner.Y - m_current_text_rect.LowerRightCorner.Y;
-                               }
-                       }
-
-                       setTextRect(curs_line);
-                       if (m_current_text_rect.UpperLeftCorner.Y < m_frame_rect.UpperLeftCorner.Y) {
-                               // text above valid area
-                               m_vscroll_pos -= m_frame_rect.UpperLeftCorner.Y - m_current_text_rect.UpperLeftCorner.Y;
-                               setTextRect(curs_line);
-                       } else if (m_current_text_rect.LowerRightCorner.Y > m_frame_rect.LowerRightCorner.Y){
-                               // text below valid area
-                               m_vscroll_pos += m_current_text_rect.LowerRightCorner.Y - m_frame_rect.LowerRightCorner.Y;
-                               setTextRect(curs_line);
-                       }
-               }
-       }
-
-       if (m_vscrollbar) {
-               m_vscrollbar->setPos(m_vscroll_pos);
-       }
-}
-
-void GUIEditBoxWithScrollBar::calculateFrameRect()
-{
-       m_frame_rect = AbsoluteRect;
-
-
-       IGUISkin *skin = 0;
-       if (Environment)
-               skin = Environment->getSkin();
-       if (m_border && skin) {
-               m_frame_rect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X) + 1;
-               m_frame_rect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y) + 1;
-               m_frame_rect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X) + 1;
-               m_frame_rect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y) + 1;
-       }
-
-       updateVScrollBar();
-}
-
-//! set text markers
-void GUIEditBoxWithScrollBar::setTextMarkers(s32 begin, s32 end)
-{
-       if (begin != m_mark_begin || end != m_mark_end) {
-               m_mark_begin = begin;
-               m_mark_end = end;
-               sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
-       }
-}
-
-//! send some gui event to parent
-void GUIEditBoxWithScrollBar::sendGuiEvent(EGUI_EVENT_TYPE type)
-{
-       if (Parent) {
-               SEvent e;
-               e.EventType = EET_GUI_EVENT;
-               e.GUIEvent.Caller = this;
-               e.GUIEvent.Element = 0;
-               e.GUIEvent.EventType = type;
-
-               Parent->OnEvent(e);
-       }
-}
-
-//! create a vertical scroll bar
-void GUIEditBoxWithScrollBar::createVScrollBar()
-{
-       IGUISkin *skin = 0;
-       if (Environment)
-               skin = Environment->getSkin();
-
-       m_scrollbar_width = skin ? skin->getSize(gui::EGDS_SCROLLBAR_SIZE) : 16;
-
-       irr::core::rect<s32> scrollbarrect = m_frame_rect;
-       scrollbarrect.UpperLeftCorner.X += m_frame_rect.getWidth() - m_scrollbar_width;
-       m_vscrollbar = Environment->addScrollBar(false, scrollbarrect, getParent(), getID());
-       m_vscrollbar->setVisible(false);
-       m_vscrollbar->setSmallStep(1);
-       m_vscrollbar->setLargeStep(1);
-}
-
-void GUIEditBoxWithScrollBar::updateVScrollBar()
-{
-       if (!m_vscrollbar) {
-               return;
-       }
-
-       // OnScrollBarChanged(...)
-       if (m_vscrollbar->getPos() != m_vscroll_pos) {
-               s32 deltaScrollY = m_vscrollbar->getPos() - m_vscroll_pos;
-               m_current_text_rect.UpperLeftCorner.Y -= deltaScrollY;
-               m_current_text_rect.LowerRightCorner.Y -= deltaScrollY;
-
-               s32 scrollymax = getTextDimension().Height - m_frame_rect.getHeight();
-               if (scrollymax != m_vscrollbar->getMax()) {
-                       // manage a newline or a deleted line
-                       m_vscrollbar->setMax(scrollymax);
-                       calculateScrollPos();
-               } else {
-                       // manage a newline or a deleted line
-                       m_vscroll_pos = m_vscrollbar->getPos();
-               }
-       }
-
-       // check if a vertical scrollbar is needed ?
-       if (getTextDimension().Height > (u32) m_frame_rect.getHeight()) {
-               m_frame_rect.LowerRightCorner.X -= m_scrollbar_width;
-
-               s32 scrollymax = getTextDimension().Height - m_frame_rect.getHeight();
-               if (scrollymax != m_vscrollbar->getMax()) {
-                       m_vscrollbar->setMax(scrollymax);
-               }
-
-               if (!m_vscrollbar->isVisible()) {
-                       m_vscrollbar->setVisible(true);
-               }
-       } else {
-               if (m_vscrollbar->isVisible())
-               {
-                       m_vscrollbar->setVisible(false);
-                       m_vscroll_pos = 0;
-                       m_vscrollbar->setPos(0);
-                       m_vscrollbar->setMax(1);
-               }
-       }
-
-
-}
-
-//! set true if this editbox is writable
-void GUIEditBoxWithScrollBar::setWritable(bool writable)
-{
-       m_writable = writable;
-}
-
-//! Change the background color
-void GUIEditBoxWithScrollBar::setBackgroundColor(const video::SColor &bg_color)
-{
-       m_bg_color = bg_color;
-       m_bg_color_used = true;
-}
-
-//! Writes attributes of the element.
-void GUIEditBoxWithScrollBar::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options = 0) const
-{
-       // IGUIEditBox::serializeAttributes(out,options);
-
-       out->addBool("Border", m_border);
-       out->addBool("Background", m_background);
-       out->addBool("OverrideColorEnabled", m_override_color_enabled);
-       out->addColor("OverrideColor", m_override_color);
-       // out->addFont("OverrideFont", OverrideFont);
-       out->addInt("MaxChars", m_max);
-       out->addBool("WordWrap", m_word_wrap);
-       out->addBool("MultiLine", m_multiline);
-       out->addBool("AutoScroll", m_autoscroll);
-       out->addBool("PasswordBox", m_passwordbox);
-       core::stringw ch = L" ";
-       ch[0] = m_passwordchar;
-       out->addString("PasswordChar", ch.c_str());
-       out->addEnum("HTextAlign", m_halign, GUIAlignmentNames);
-       out->addEnum("VTextAlign", m_valign, GUIAlignmentNames);
-       out->addBool("Writable", m_writable);
-
-       IGUIEditBox::serializeAttributes(out, options);
-}
-
-
-//! Reads attributes of the element
-void GUIEditBoxWithScrollBar::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options = 0)
-{
-       IGUIEditBox::deserializeAttributes(in, options);
-
-       setDrawBorder(in->getAttributeAsBool("Border"));
-       setDrawBackground(in->getAttributeAsBool("Background"));
-       setOverrideColor(in->getAttributeAsColor("OverrideColor"));
-       enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
-       setMax(in->getAttributeAsInt("MaxChars"));
-       setWordWrap(in->getAttributeAsBool("WordWrap"));
-       setMultiLine(in->getAttributeAsBool("MultiLine"));
-       setAutoScroll(in->getAttributeAsBool("AutoScroll"));
-       core::stringw ch = in->getAttributeAsStringW("PasswordChar");
-
-       if (!ch.size())
-               setPasswordBox(in->getAttributeAsBool("PasswordBox"));
-       else
-               setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
-
-       setTextAlignment((EGUI_ALIGNMENT)in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
-               (EGUI_ALIGNMENT)in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
-
-       // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
-       setWritable(in->getAttributeAsBool("Writable"));
-}
diff --git a/src/guiEditBoxWithScrollbar.h b/src/guiEditBoxWithScrollbar.h
deleted file mode 100644 (file)
index cca2f65..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright (C) 2002-2012 Nikolaus Gebhardt, Modified by Mustapha Tachouct
-// This file is part of the "Irrlicht Engine".
-// For conditions of distribution and use, see copyright notice in irrlicht.h
-
-#ifndef GUIEDITBOXWITHSCROLLBAR_HEADER
-#define GUIEDITBOXWITHSCROLLBAR_HEADER
-
-#include "IGUIEditBox.h"
-#include "IOSOperator.h"
-#include "IGUIScrollBar.h"
-#include <vector>
-
-using namespace irr;
-using namespace irr::gui;
-
-class GUIEditBoxWithScrollBar : public IGUIEditBox
-{
-public:
-
-       //! constructor
-       GUIEditBoxWithScrollBar(const wchar_t* text, bool border, IGUIEnvironment* environment,
-               IGUIElement* parent, s32 id, const core::rect<s32>& rectangle,
-               bool writable = true, bool has_vscrollbar = true);
-
-       //! destructor
-       virtual ~GUIEditBoxWithScrollBar();
-
-       //! Sets another skin independent font.
-       virtual void setOverrideFont(IGUIFont* font = 0);
-
-       //! Gets the override font (if any)
-       /** \return The override font (may be 0) */
-       virtual IGUIFont* getOverrideFont() const;
-
-       //! Get the font which is used right now for drawing
-       /** Currently this is the override font when one is set and the
-       font of the active skin otherwise */
-       virtual IGUIFont* getActiveFont() const;
-
-       //! Sets another color for the text.
-       virtual void setOverrideColor(video::SColor color);
-
-       //! Gets the override color
-       virtual video::SColor getOverrideColor() const;
-
-       //! Sets if the text should use the overide color or the
-       //! color in the gui skin.
-       virtual void enableOverrideColor(bool enable);
-
-       //! Checks if an override color is enabled
-       /** \return true if the override color is enabled, false otherwise */
-       virtual bool isOverrideColorEnabled(void) const;
-
-       //! Sets whether to draw the background
-       virtual void setDrawBackground(bool draw);
-
-       //! Turns the border on or off
-       virtual void setDrawBorder(bool border);
-
-       //! Enables or disables word wrap for using the edit box as multiline text editor.
-       virtual void setWordWrap(bool enable);
-
-       //! Checks if word wrap is enabled
-       //! \return true if word wrap is enabled, false otherwise
-       virtual bool isWordWrapEnabled() const;
-
-       //! Enables or disables newlines.
-       /** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
-       instead a newline character will be inserted. */
-       virtual void setMultiLine(bool enable);
-
-       //! Checks if multi line editing is enabled
-       //! \return true if mult-line is enabled, false otherwise
-       virtual bool isMultiLineEnabled() const;
-
-       //! Enables or disables automatic scrolling with cursor position
-       //! \param enable: If set to true, the text will move around with the cursor position
-       virtual void setAutoScroll(bool enable);
-
-       //! Checks to see if automatic scrolling is enabled
-       //! \return true if automatic scrolling is enabled, false if not
-       virtual bool isAutoScrollEnabled() const;
-
-       //! Gets the size area of the text in the edit box
-       //! \return Returns the size in pixels of the text
-       virtual core::dimension2du getTextDimension();
-
-       //! Sets text justification
-       virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
-
-       //! called if an event happened.
-       virtual bool OnEvent(const SEvent& event);
-
-       //! draws the element and its children
-       virtual void draw();
-
-       //! Sets the new caption of this element.
-       virtual void setText(const wchar_t* text);
-
-       //! Sets the maximum amount of characters which may be entered in the box.
-       //! \param max: Maximum amount of characters. If 0, the character amount is
-       //! infinity.
-       virtual void setMax(u32 max);
-
-       //! Returns maximum amount of characters, previously set by setMax();
-       virtual u32 getMax() const;
-
-       //! Sets whether the edit box is a password box. Setting this to true will
-       /** disable MultiLine, WordWrap and the ability to copy with ctrl+c or ctrl+x
-       \param passwordBox: true to enable password, false to disable
-       \param passwordChar: the character that is displayed instead of letters */
-       virtual void setPasswordBox(bool passwordBox, wchar_t passwordChar = L'*');
-
-       //! Returns true if the edit box is currently a password box.
-       virtual bool isPasswordBox() const;
-
-       //! Updates the absolute position, splits text if required
-       virtual void updateAbsolutePosition();
-       
-       virtual void setWritable(bool writable);
-
-       //! Change the background color
-       virtual void setBackgroundColor(const video::SColor &bg_color);
-
-       //! Writes attributes of the element.
-       virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const;
-
-       //! Reads attributes of the element
-       virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options);
-
-protected:
-       //! Breaks the single text line.
-       void breakText();
-       //! sets the area of the given line
-       void setTextRect(s32 line);
-       //! returns the line number that the cursor is on
-       s32 getLineFromPos(s32 pos);
-       //! adds a letter to the edit box
-       void inputChar(wchar_t c);
-       //! calculates the current scroll position
-       void calculateScrollPos();
-       //! calculated the FrameRect
-       void calculateFrameRect();
-       //! send some gui event to parent
-       void sendGuiEvent(EGUI_EVENT_TYPE type);
-       //! set text markers
-       void setTextMarkers(s32 begin, s32 end);
-       //! create a Vertical ScrollBar
-       void createVScrollBar();
-       //! update the vertical scrollBar (visibilty & position)
-       void updateVScrollBar();
-
-       bool processKey(const SEvent& event);
-       bool processMouse(const SEvent& event);
-       s32 getCursorPos(s32 x, s32 y);
-
-       bool m_mouse_marking;
-       bool m_border;
-       bool m_background;
-       bool m_override_color_enabled;
-       s32 m_mark_begin;
-       s32 m_mark_end;
-
-       video::SColor m_override_color;
-       gui::IGUIFont *m_override_font, *m_last_break_font;
-       IOSOperator* m_operator;
-
-       u32 m_blink_start_time;
-       s32 m_cursor_pos;
-       s32 m_hscroll_pos, m_vscroll_pos; // scroll position in characters
-       u32 m_max;
-
-       bool m_word_wrap, m_multiline, m_autoscroll, m_passwordbox;
-       wchar_t m_passwordchar;
-       EGUI_ALIGNMENT m_halign, m_valign;
-
-       std::vector<core::stringw> m_broken_text;
-       std::vector<s32> m_broken_text_positions;
-
-       core::rect<s32> m_current_text_rect, m_frame_rect; // temporary values
-
-       u32 m_scrollbar_width;
-       IGUIScrollBar *m_vscrollbar;
-       bool m_writable;
-
-       bool m_bg_color_used;
-       video::SColor m_bg_color;
-};
-
-
-#endif // GUIEDITBOXWITHSCROLLBAR_HEADER
-
diff --git a/src/guiEngine.cpp b/src/guiEngine.cpp
deleted file mode 100644 (file)
index e9b4e54..0000000
+++ /dev/null
@@ -1,587 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 sapier
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "guiEngine.h"
-
-#include <IGUIStaticText.h>
-#include <ICameraSceneNode.h>
-#include "client/renderingengine.h"
-#include "scripting_mainmenu.h"
-#include "util/numeric.h"
-#include "config.h"
-#include "version.h"
-#include "porting.h"
-#include "filesys.h"
-#include "settings.h"
-#include "guiMainMenu.h"
-#include "sound.h"
-#include "sound_openal.h"
-#include "clouds.h"
-#include "httpfetch.h"
-#include "log.h"
-#include "fontengine.h"
-#include "guiscalingfilter.h"
-#include "irrlicht_changes/static_text.h"
-
-#ifdef __ANDROID__
-#include "client/tile.h"
-#include <GLES/gl.h>
-#endif
-
-
-/******************************************************************************/
-void TextDestGuiEngine::gotText(const StringMap &fields)
-{
-       m_engine->getScriptIface()->handleMainMenuButtons(fields);
-}
-
-/******************************************************************************/
-void TextDestGuiEngine::gotText(const std::wstring &text)
-{
-       m_engine->getScriptIface()->handleMainMenuEvent(wide_to_utf8(text));
-}
-
-/******************************************************************************/
-MenuTextureSource::~MenuTextureSource()
-{
-       for (const std::string &texture_to_delete : m_to_delete) {
-               const char *tname = texture_to_delete.c_str();
-               video::ITexture *texture = m_driver->getTexture(tname);
-               m_driver->removeTexture(texture);
-       }
-}
-
-/******************************************************************************/
-video::ITexture *MenuTextureSource::getTexture(const std::string &name, u32 *id)
-{
-       if(id)
-               *id = 0;
-       if(name.empty())
-               return NULL;
-       m_to_delete.insert(name);
-
-#ifdef __ANDROID__
-       video::IImage *image = m_driver->createImageFromFile(name.c_str());
-       if (image) {
-               image = Align2Npot2(image, m_driver);
-               video::ITexture* retval = m_driver->addTexture(name.c_str(), image);
-               image->drop();
-               return retval;
-       }
-#endif
-       return m_driver->getTexture(name.c_str());
-}
-
-/******************************************************************************/
-/** MenuMusicFetcher                                                          */
-/******************************************************************************/
-void MenuMusicFetcher::fetchSounds(const std::string &name,
-                       std::set<std::string> &dst_paths,
-                       std::set<std::string> &dst_datas)
-{
-       if(m_fetched.count(name))
-               return;
-       m_fetched.insert(name);
-       std::string base;
-       base = porting::path_share + DIR_DELIM + "sounds";
-       dst_paths.insert(base + DIR_DELIM + name + ".ogg");
-       int i;
-       for(i=0; i<10; i++)
-               dst_paths.insert(base + DIR_DELIM + name + "."+itos(i)+".ogg");
-       base = porting::path_user + DIR_DELIM + "sounds";
-       dst_paths.insert(base + DIR_DELIM + name + ".ogg");
-       for(i=0; i<10; i++)
-               dst_paths.insert(base + DIR_DELIM + name + "."+itos(i)+".ogg");
-}
-
-/******************************************************************************/
-/** GUIEngine                                                                 */
-/******************************************************************************/
-GUIEngine::GUIEngine(JoystickController *joystick,
-               gui::IGUIElement *parent,
-               IMenuManager *menumgr,
-               MainMenuData *data,
-               bool &kill) :
-       m_parent(parent),
-       m_menumanager(menumgr),
-       m_smgr(RenderingEngine::get_scene_manager()),
-       m_data(data),
-       m_kill(kill)
-{
-       //initialize texture pointers
-       for (image_definition &texture : m_textures) {
-               texture.texture = NULL;
-       }
-       // is deleted by guiformspec!
-       m_buttonhandler = new TextDestGuiEngine(this);
-
-       //create texture source
-       m_texture_source = new MenuTextureSource(RenderingEngine::get_video_driver());
-
-       //create soundmanager
-       MenuMusicFetcher soundfetcher;
-#if USE_SOUND
-       m_sound_manager = createOpenALSoundManager(&soundfetcher);
-#endif
-       if(!m_sound_manager)
-               m_sound_manager = &dummySoundManager;
-
-       //create topleft header
-       m_toplefttext = L"";
-
-       core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()),
-               g_fontengine->getTextHeight());
-       rect += v2s32(4, 0);
-
-       m_irr_toplefttext =
-               addStaticText(RenderingEngine::get_gui_env(), m_toplefttext,
-                       rect, false, true, 0, -1);
-
-       //create formspecsource
-       m_formspecgui = new FormspecFormSource("");
-
-       /* Create menu */
-       m_menu = new GUIFormSpecMenu(joystick,
-                       m_parent,
-                       -1,
-                       m_menumanager,
-                       NULL /* &client */,
-                       m_texture_source,
-                       m_formspecgui,
-                       m_buttonhandler,
-                       false);
-
-       m_menu->allowClose(false);
-       m_menu->lockSize(true,v2u32(800,600));
-
-       // Initialize scripting
-
-       infostream << "GUIEngine: Initializing Lua" << std::endl;
-
-       m_script = new MainMenuScripting(this);
-
-       try {
-               m_script->setMainMenuData(&m_data->script_data);
-               m_data->script_data.errormessage = "";
-
-               if (!loadMainMenuScript()) {
-                       errorstream << "No future without main menu!" << std::endl;
-                       abort();
-               }
-
-               run();
-       } catch (LuaError &e) {
-               errorstream << "Main menu error: " << e.what() << std::endl;
-               m_data->script_data.errormessage = e.what();
-       }
-
-       m_menu->quitMenu();
-       m_menu->drop();
-       m_menu = NULL;
-}
-
-/******************************************************************************/
-bool GUIEngine::loadMainMenuScript()
-{
-       // Set main menu path (for core.get_mainmenu_path())
-       m_scriptdir = g_settings->get("main_menu_path");
-       if (m_scriptdir.empty()) {
-               m_scriptdir = porting::path_share + DIR_DELIM + "builtin" + DIR_DELIM + "mainmenu";
-       }
-
-       // Load builtin (which will load the main menu script)
-       std::string script = porting::path_share + DIR_DELIM "builtin" + DIR_DELIM "init.lua";
-       try {
-               m_script->loadScript(script);
-               // Menu script loaded
-               return true;
-       } catch (const ModError &e) {
-               errorstream << "GUIEngine: execution of menu script failed: "
-                       << e.what() << std::endl;
-       }
-
-       return false;
-}
-
-/******************************************************************************/
-void GUIEngine::run()
-{
-       // Always create clouds because they may or may not be
-       // needed based on the game selected
-       video::IVideoDriver *driver = RenderingEngine::get_video_driver();
-
-       cloudInit();
-
-       unsigned int text_height = g_fontengine->getTextHeight();
-
-       irr::core::dimension2d<u32> previous_screen_size(g_settings->getU16("screen_w"),
-               g_settings->getU16("screen_h"));
-
-       while (RenderingEngine::run() && (!m_startgame) && (!m_kill)) {
-
-               const irr::core::dimension2d<u32> &current_screen_size =
-                       RenderingEngine::get_video_driver()->getScreenSize();
-               // Verify if window size has changed and save it if it's the case
-               // Ensure evaluating settings->getBool after verifying screensize
-               // First condition is cheaper
-               if (previous_screen_size != current_screen_size &&
-                               current_screen_size != irr::core::dimension2d<u32>(0,0) &&
-                               g_settings->getBool("autosave_screensize")) {
-                       g_settings->setU16("screen_w", current_screen_size.Width);
-                       g_settings->setU16("screen_h", current_screen_size.Height);
-                       previous_screen_size = current_screen_size;
-               }
-
-               //check if we need to update the "upper left corner"-text
-               if (text_height != g_fontengine->getTextHeight()) {
-                       updateTopLeftTextSize();
-                       text_height = g_fontengine->getTextHeight();
-               }
-
-               driver->beginScene(true, true, video::SColor(255,140,186,250));
-
-               if (m_clouds_enabled)
-               {
-                       cloudPreProcess();
-                       drawOverlay(driver);
-               }
-               else
-                       drawBackground(driver);
-
-               drawHeader(driver);
-               drawFooter(driver);
-
-               RenderingEngine::get_gui_env()->drawAll();
-
-               driver->endScene();
-
-               if (m_clouds_enabled)
-                       cloudPostProcess();
-               else
-                       sleep_ms(25);
-
-               m_script->step();
-
-#ifdef __ANDROID__
-               m_menu->getAndroidUIInput();
-#endif
-       }
-}
-
-/******************************************************************************/
-GUIEngine::~GUIEngine()
-{
-       if (m_sound_manager != &dummySoundManager){
-               delete m_sound_manager;
-               m_sound_manager = NULL;
-       }
-
-       infostream<<"GUIEngine: Deinitializing scripting"<<std::endl;
-       delete m_script;
-
-       m_irr_toplefttext->setText(L"");
-
-       //clean up texture pointers
-       for (image_definition &texture : m_textures) {
-               if (texture.texture)
-                       RenderingEngine::get_video_driver()->removeTexture(texture.texture);
-       }
-
-       delete m_texture_source;
-
-       if (m_cloud.clouds)
-               m_cloud.clouds->drop();
-}
-
-/******************************************************************************/
-void GUIEngine::cloudInit()
-{
-       m_cloud.clouds = new Clouds(m_smgr, -1, rand());
-       m_cloud.clouds->setHeight(100.0f);
-       m_cloud.clouds->update(v3f(0, 0, 0), video::SColor(255,200,200,255));
-
-       m_cloud.camera = m_smgr->addCameraSceneNode(0,
-                               v3f(0,0,0), v3f(0, 60, 100));
-       m_cloud.camera->setFarValue(10000);
-
-       m_cloud.lasttime = RenderingEngine::get_timer_time();
-}
-
-/******************************************************************************/
-void GUIEngine::cloudPreProcess()
-{
-       u32 time = RenderingEngine::get_timer_time();
-
-       if(time > m_cloud.lasttime)
-               m_cloud.dtime = (time - m_cloud.lasttime) / 1000.0;
-       else
-               m_cloud.dtime = 0;
-
-       m_cloud.lasttime = time;
-
-       m_cloud.clouds->step(m_cloud.dtime*3);
-       m_cloud.clouds->render();
-       m_smgr->drawAll();
-}
-
-/******************************************************************************/
-void GUIEngine::cloudPostProcess()
-{
-       float fps_max = g_settings->getFloat("pause_fps_max");
-       // Time of frame without fps limit
-       u32 busytime_u32;
-
-       // not using getRealTime is necessary for wine
-       u32 time = RenderingEngine::get_timer_time();
-       if(time > m_cloud.lasttime)
-               busytime_u32 = time - m_cloud.lasttime;
-       else
-               busytime_u32 = 0;
-
-       // FPS limiter
-       u32 frametime_min = 1000./fps_max;
-
-       if (busytime_u32 < frametime_min) {
-               u32 sleeptime = frametime_min - busytime_u32;
-               RenderingEngine::get_raw_device()->sleep(sleeptime);
-       }
-}
-
-/******************************************************************************/
-void GUIEngine::drawBackground(video::IVideoDriver *driver)
-{
-       v2u32 screensize = driver->getScreenSize();
-
-       video::ITexture* texture = m_textures[TEX_LAYER_BACKGROUND].texture;
-
-       /* If no texture, draw background of solid color */
-       if(!texture){
-               video::SColor color(255,80,58,37);
-               core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
-               driver->draw2DRectangle(color, rect, NULL);
-               return;
-       }
-
-       v2u32 sourcesize = texture->getOriginalSize();
-
-       if (m_textures[TEX_LAYER_BACKGROUND].tile)
-       {
-               v2u32 tilesize(
-                               MYMAX(sourcesize.X,m_textures[TEX_LAYER_BACKGROUND].minsize),
-                               MYMAX(sourcesize.Y,m_textures[TEX_LAYER_BACKGROUND].minsize));
-               for (unsigned int x = 0; x < screensize.X; x += tilesize.X )
-               {
-                       for (unsigned int y = 0; y < screensize.Y; y += tilesize.Y )
-                       {
-                               draw2DImageFilterScaled(driver, texture,
-                                       core::rect<s32>(x, y, x+tilesize.X, y+tilesize.Y),
-                                       core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
-                                       NULL, NULL, true);
-                       }
-               }
-               return;
-       }
-
-       /* Draw background texture */
-       draw2DImageFilterScaled(driver, texture,
-               core::rect<s32>(0, 0, screensize.X, screensize.Y),
-               core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
-               NULL, NULL, true);
-}
-
-/******************************************************************************/
-void GUIEngine::drawOverlay(video::IVideoDriver *driver)
-{
-       v2u32 screensize = driver->getScreenSize();
-
-       video::ITexture* texture = m_textures[TEX_LAYER_OVERLAY].texture;
-
-       /* If no texture, draw nothing */
-       if(!texture)
-               return;
-
-       /* Draw background texture */
-       v2u32 sourcesize = texture->getOriginalSize();
-       draw2DImageFilterScaled(driver, texture,
-               core::rect<s32>(0, 0, screensize.X, screensize.Y),
-               core::rect<s32>(0, 0, sourcesize.X, sourcesize.Y),
-               NULL, NULL, true);
-}
-
-/******************************************************************************/
-void GUIEngine::drawHeader(video::IVideoDriver *driver)
-{
-       core::dimension2d<u32> screensize = driver->getScreenSize();
-
-       video::ITexture* texture = m_textures[TEX_LAYER_HEADER].texture;
-
-       /* If no texture, draw nothing */
-       if(!texture)
-               return;
-
-       f32 mult = (((f32)screensize.Width / 2.0)) /
-                       ((f32)texture->getOriginalSize().Width);
-
-       v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult,
-                       ((f32)texture->getOriginalSize().Height) * mult);
-
-       // Don't draw the header if there isn't enough room
-       s32 free_space = (((s32)screensize.Height)-320)/2;
-
-       if (free_space > splashsize.Y) {
-               core::rect<s32> splashrect(0, 0, splashsize.X, splashsize.Y);
-               splashrect += v2s32((screensize.Width/2)-(splashsize.X/2),
-                               ((free_space/2)-splashsize.Y/2)+10);
-
-       video::SColor bgcolor(255,50,50,50);
-
-       draw2DImageFilterScaled(driver, texture, splashrect,
-               core::rect<s32>(core::position2d<s32>(0,0),
-               core::dimension2di(texture->getOriginalSize())),
-               NULL, NULL, true);
-       }
-}
-
-/******************************************************************************/
-void GUIEngine::drawFooter(video::IVideoDriver *driver)
-{
-       core::dimension2d<u32> screensize = driver->getScreenSize();
-
-       video::ITexture* texture = m_textures[TEX_LAYER_FOOTER].texture;
-
-       /* If no texture, draw nothing */
-       if(!texture)
-               return;
-
-       f32 mult = (((f32)screensize.Width)) /
-                       ((f32)texture->getOriginalSize().Width);
-
-       v2s32 footersize(((f32)texture->getOriginalSize().Width) * mult,
-                       ((f32)texture->getOriginalSize().Height) * mult);
-
-       // Don't draw the footer if there isn't enough room
-       s32 free_space = (((s32)screensize.Height)-320)/2;
-
-       if (free_space > footersize.Y) {
-               core::rect<s32> rect(0,0,footersize.X,footersize.Y);
-               rect += v2s32(screensize.Width/2,screensize.Height-footersize.Y);
-               rect -= v2s32(footersize.X/2, 0);
-
-               draw2DImageFilterScaled(driver, texture, rect,
-                       core::rect<s32>(core::position2d<s32>(0,0),
-                       core::dimension2di(texture->getOriginalSize())),
-                       NULL, NULL, true);
-       }
-}
-
-/******************************************************************************/
-bool GUIEngine::setTexture(texture_layer layer, std::string texturepath,
-               bool tile_image, unsigned int minsize)
-{
-       video::IVideoDriver *driver = RenderingEngine::get_video_driver();
-
-       if (m_textures[layer].texture) {
-               driver->removeTexture(m_textures[layer].texture);
-               m_textures[layer].texture = NULL;
-       }
-
-       if (texturepath.empty() || !fs::PathExists(texturepath)) {
-               return false;
-       }
-
-       m_textures[layer].texture = driver->getTexture(texturepath.c_str());
-       m_textures[layer].tile    = tile_image;
-       m_textures[layer].minsize = minsize;
-
-       if (!m_textures[layer].texture) {
-               return false;
-       }
-
-       return true;
-}
-
-/******************************************************************************/
-bool GUIEngine::downloadFile(const std::string &url, const std::string &target)
-{
-#if USE_CURL
-       std::ofstream target_file(target.c_str(), std::ios::out | std::ios::binary);
-
-       if (!target_file.good()) {
-               return false;
-       }
-
-       HTTPFetchRequest fetch_request;
-       HTTPFetchResult fetch_result;
-       fetch_request.url = url;
-       fetch_request.caller = HTTPFETCH_SYNC;
-       fetch_request.timeout = g_settings->getS32("curl_file_download_timeout");
-       httpfetch_sync(fetch_request, fetch_result);
-
-       if (!fetch_result.succeeded) {
-               return false;
-       }
-       target_file << fetch_result.data;
-
-       return true;
-#else
-       return false;
-#endif
-}
-
-/******************************************************************************/
-void GUIEngine::setTopleftText(const std::string &text)
-{
-       m_toplefttext = translate_string(utf8_to_wide(text));
-
-       updateTopLeftTextSize();
-}
-
-/******************************************************************************/
-void GUIEngine::updateTopLeftTextSize()
-{
-       core::rect<s32> rect(0, 0, g_fontengine->getTextWidth(m_toplefttext.c_str()),
-               g_fontengine->getTextHeight());
-       rect += v2s32(4, 0);
-
-       m_irr_toplefttext->remove();
-       m_irr_toplefttext =
-               addStaticText(RenderingEngine::get_gui_env(), m_toplefttext,
-                       rect, false, true, 0, -1);
-}
-
-/******************************************************************************/
-s32 GUIEngine::playSound(SimpleSoundSpec spec, bool looped)
-{
-       s32 handle = m_sound_manager->playSound(spec, looped);
-       return handle;
-}
-
-/******************************************************************************/
-void GUIEngine::stopSound(s32 handle)
-{
-       m_sound_manager->stopSound(handle);
-}
-
-/******************************************************************************/
-unsigned int GUIEngine::queueAsync(const std::string &serialized_func,
-               const std::string &serialized_params)
-{
-       return m_script->queueAsync(serialized_func, serialized_params);
-}
-
diff --git a/src/guiEngine.h b/src/guiEngine.h
deleted file mode 100644 (file)
index 817d760..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 sapier
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-/******************************************************************************/
-/* Includes                                                                   */
-/******************************************************************************/
-#include "irrlichttypes.h"
-#include "modalMenu.h"
-#include "guiFormSpecMenu.h"
-#include "sound.h"
-#include "client/tile.h"
-#include "util/enriched_string.h"
-
-/******************************************************************************/
-/* Typedefs and macros                                                        */
-/******************************************************************************/
-/** texture layer ids */
-typedef enum {
-       TEX_LAYER_BACKGROUND = 0,
-       TEX_LAYER_OVERLAY,
-       TEX_LAYER_HEADER,
-       TEX_LAYER_FOOTER,
-       TEX_LAYER_MAX
-} texture_layer;
-
-typedef struct {
-       video::ITexture *texture = nullptr;
-       bool             tile;
-       unsigned int     minsize;
-} image_definition;
-
-/******************************************************************************/
-/* forward declarations                                                       */
-/******************************************************************************/
-class GUIEngine;
-class MainMenuScripting;
-class Clouds;
-struct MainMenuData;
-
-/******************************************************************************/
-/* declarations                                                               */
-/******************************************************************************/
-
-/** GUIEngine specific implementation of TextDest used within guiFormSpecMenu */
-class TextDestGuiEngine : public TextDest
-{
-public:
-       /**
-        * default constructor
-        * @param engine the engine data is transmitted for further processing
-        */
-       TextDestGuiEngine(GUIEngine* engine) : m_engine(engine) {};
-
-       /**
-        * receive fields transmitted by guiFormSpecMenu
-        * @param fields map containing formspec field elements currently active
-        */
-       void gotText(const StringMap &fields);
-
-       /**
-        * receive text/events transmitted by guiFormSpecMenu
-        * @param text textual representation of event
-        */
-       void gotText(const std::wstring &text);
-
-private:
-       /** target to transmit data to */
-       GUIEngine *m_engine = nullptr;
-};
-
-/** GUIEngine specific implementation of ISimpleTextureSource */
-class MenuTextureSource : public ISimpleTextureSource
-{
-public:
-       /**
-        * default constructor
-        * @param driver the video driver to load textures from
-        */
-       MenuTextureSource(video::IVideoDriver *driver) : m_driver(driver) {};
-
-       /**
-        * destructor, removes all loaded textures
-        */
-       virtual ~MenuTextureSource();
-
-       /**
-        * get a texture, loading it if required
-        * @param name path to the texture
-        * @param id receives the texture ID, always 0 in this implementation
-        */
-       video::ITexture *getTexture(const std::string &name, u32 *id = NULL);
-
-private:
-       /** driver to get textures from */
-       video::IVideoDriver *m_driver = nullptr;
-       /** set of texture names to delete */
-       std::set<std::string> m_to_delete;
-};
-
-/** GUIEngine specific implementation of OnDemandSoundFetcher */
-class MenuMusicFetcher: public OnDemandSoundFetcher
-{
-public:
-       /**
-        * get sound file paths according to sound name
-        * @param name sound name
-        * @param dst_paths receives possible paths to sound files
-        * @param dst_datas receives binary sound data (not used here)
-        */
-       void fetchSounds(const std::string &name,
-                       std::set<std::string> &dst_paths,
-                       std::set<std::string> &dst_datas);
-
-private:
-       /** set of fetched sound names */
-       std::set<std::string> m_fetched;
-};
-
-/** implementation of main menu based uppon formspecs */
-class GUIEngine {
-       /** grant ModApiMainMenu access to private members */
-       friend class ModApiMainMenu;
-       friend class ModApiSound;
-
-public:
-       /**
-        * default constructor
-        * @param dev device to draw at
-        * @param parent parent gui element
-        * @param menumgr manager to add menus to
-        * @param smgr scene manager to add scene elements to
-        * @param data struct to transfer data to main game handling
-        */
-       GUIEngine(JoystickController *joystick,
-                       gui::IGUIElement *parent,
-                       IMenuManager *menumgr,
-                       MainMenuData *data,
-                       bool &kill);
-
-       /** default destructor */
-       virtual ~GUIEngine();
-
-       /**
-        * return MainMenuScripting interface
-        */
-       MainMenuScripting *getScriptIface()
-       {
-               return m_script;
-       }
-
-       /**
-        * return dir of current menuscript
-        */
-       std::string getScriptDir()
-       {
-               return m_scriptdir;
-       }
-
-       /** pass async callback to scriptengine **/
-       unsigned int queueAsync(const std::string &serialized_fct,
-                       const std::string &serialized_params);
-
-private:
-
-       /** find and run the main menu script */
-       bool loadMainMenuScript();
-
-       /** run main menu loop */
-       void run();
-
-       /** update size of topleftext element */
-       void updateTopLeftTextSize();
-
-       /** parent gui element */
-       gui::IGUIElement        *m_parent = nullptr;
-       /** manager to add menus to */
-       IMenuManager            *m_menumanager = nullptr;
-       /** scene manager to add scene elements to */
-       scene::ISceneManager    *m_smgr = nullptr;
-       /** pointer to data beeing transfered back to main game handling */
-       MainMenuData            *m_data = nullptr;
-       /** pointer to texture source */
-       ISimpleTextureSource    *m_texture_source = nullptr;
-       /** pointer to soundmanager*/
-       ISoundManager           *m_sound_manager = nullptr;
-
-       /** representation of form source to be used in mainmenu formspec */
-       FormspecFormSource      *m_formspecgui = nullptr;
-       /** formspec input receiver */
-       TextDestGuiEngine       *m_buttonhandler = nullptr;
-       /** the formspec menu */
-       GUIFormSpecMenu         *m_menu = nullptr;
-
-       /** reference to kill variable managed by SIGINT handler */
-       bool                    &m_kill;
-
-       /** variable used to abort menu and return back to main game handling */
-       bool                     m_startgame = false;
-
-       /** scripting interface */
-       MainMenuScripting       *m_script = nullptr;
-
-       /** script basefolder */
-       std::string              m_scriptdir = "";
-
-       /**
-        * draw background layer
-        * @param driver to use for drawing
-        */
-       void drawBackground(video::IVideoDriver *driver);
-       /**
-        * draw overlay layer
-        * @param driver to use for drawing
-        */
-       void drawOverlay(video::IVideoDriver *driver);
-       /**
-        * draw header layer
-        * @param driver to use for drawing
-        */
-       void drawHeader(video::IVideoDriver *driver);
-       /**
-        * draw footer layer
-        * @param driver to use for drawing
-        */
-       void drawFooter(video::IVideoDriver *driver);
-
-       /**
-        * load a texture for a specified layer
-        * @param layer draw layer to specify texture
-        * @param texturepath full path of texture to load
-        */
-       bool setTexture(texture_layer layer, std::string texturepath,
-                       bool tile_image, unsigned int minsize);
-
-       /**
-        * download a file using curl
-        * @param url url to download
-        * @param target file to store to
-        */
-       static bool downloadFile(const std::string &url, const std::string &target);
-
-       /** array containing pointers to current specified texture layers */
-       image_definition m_textures[TEX_LAYER_MAX];
-
-       /**
-        * specify text to appear as top left string
-        * @param text to set
-        */
-       void setTopleftText(const std::string &text);
-
-       /** pointer to gui element shown at topleft corner */
-       irr::gui::IGUIStaticText *m_irr_toplefttext = nullptr;
-       /** and text that is in it */
-       EnrichedString m_toplefttext;
-
-       /** initialize cloud subsystem */
-       void cloudInit();
-       /** do preprocessing for cloud subsystem */
-       void cloudPreProcess();
-       /** do postprocessing for cloud subsystem */
-       void cloudPostProcess();
-
-       /** internam data required for drawing clouds */
-       struct clouddata {
-               /** delta time since last cloud processing */
-               f32     dtime;
-               /** absolute time of last cloud processing */
-               u32     lasttime;
-               /** pointer to cloud class */
-               Clouds *clouds = nullptr;
-               /** camera required for drawing clouds */
-               scene::ICameraSceneNode *camera = nullptr;
-       };
-
-       /** is drawing of clouds enabled atm */
-       bool        m_clouds_enabled = true;
-       /** data used to draw clouds */
-       clouddata   m_cloud;
-
-       /** start playing a sound and return handle */
-       s32 playSound(SimpleSoundSpec spec, bool looped);
-       /** stop playing a sound started with playSound() */
-       void stopSound(s32 handle);
-
-
-};
diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp
deleted file mode 100644 (file)
index 0691bc5..0000000
+++ /dev/null
@@ -1,3864 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 <cstdlib>
-#include <algorithm>
-#include <iterator>
-#include <sstream>
-#include <limits>
-#include "guiFormSpecMenu.h"
-#include "guiTable.h"
-#include "constants.h"
-#include "gamedef.h"
-#include "keycode.h"
-#include "util/strfnd.h"
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-#include <IGUITabControl.h>
-#include <IGUIComboBox.h>
-#include "client/renderingengine.h"
-#include "log.h"
-#include "client/tile.h" // ITextureSource
-#include "hud.h" // drawItemStack
-#include "filesys.h"
-#include "gettime.h"
-#include "gettext.h"
-#include "scripting_server.h"
-#include "porting.h"
-#include "settings.h"
-#include "client.h"
-#include "fontengine.h"
-#include "util/hex.h"
-#include "util/numeric.h"
-#include "util/string.h" // for parseColorString()
-#include "irrlicht_changes/static_text.h"
-#include "guiscalingfilter.h"
-#include "guiEditBoxWithScrollbar.h"
-
-#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
-#include "intlGUIEditBox.h"
-#endif
-
-#define MY_CHECKPOS(a,b)                                                                                                       \
-       if (v_pos.size() != 2) {                                                                                                \
-               errorstream<< "Invalid pos for element " << a << "specified: \""        \
-                       << parts[b] << "\"" << std::endl;                                                               \
-                       return;                                                                                                                 \
-       }
-
-#define MY_CHECKGEOM(a,b)                                                                                                      \
-       if (v_geom.size() != 2) {                                                                                               \
-               errorstream<< "Invalid pos for element " << a << "specified: \""        \
-                       << parts[b] << "\"" << std::endl;                                                               \
-                       return;                                                                                                                 \
-       }
-/*
-       GUIFormSpecMenu
-*/
-static unsigned int font_line_height(gui::IGUIFont *font)
-{
-       return font->getDimension(L"Ay").Height + font->getKerningHeight();
-}
-
-inline u32 clamp_u8(s32 value)
-{
-       return (u32) MYMIN(MYMAX(value, 0), 255);
-}
-
-GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
-               gui::IGUIElement *parent, s32 id, IMenuManager *menumgr,
-               Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst,
-               bool remap_dbl_click) :
-       GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr),
-       m_invmgr(client),
-       m_tsrc(tsrc),
-       m_client(client),
-       m_form_src(fsrc),
-       m_text_dst(tdst),
-       m_joystick(joystick),
-       m_remap_dbl_click(remap_dbl_click)
-#ifdef __ANDROID__
-       , m_JavaDialogFieldName("")
-#endif
-{
-       current_keys_pending.key_down = false;
-       current_keys_pending.key_up = false;
-       current_keys_pending.key_enter = false;
-       current_keys_pending.key_escape = false;
-
-       m_doubleclickdetect[0].time = 0;
-       m_doubleclickdetect[1].time = 0;
-
-       m_doubleclickdetect[0].pos = v2s32(0, 0);
-       m_doubleclickdetect[1].pos = v2s32(0, 0);
-
-       m_tooltip_show_delay = (u32)g_settings->getS32("tooltip_show_delay");
-       m_tooltip_append_itemname = g_settings->getBool("tooltip_append_itemname");
-}
-
-GUIFormSpecMenu::~GUIFormSpecMenu()
-{
-       removeChildren();
-
-       for (auto &table_it : m_tables) {
-               table_it.second->drop();
-       }
-
-       delete m_selected_item;
-       delete m_form_src;
-       delete m_text_dst;
-}
-
-void GUIFormSpecMenu::removeChildren()
-{
-       const core::list<gui::IGUIElement*> &children = getChildren();
-
-       while(!children.empty()) {
-               (*children.getLast())->remove();
-       }
-
-       if(m_tooltip_element) {
-               m_tooltip_element->remove();
-               m_tooltip_element->drop();
-               m_tooltip_element = NULL;
-       }
-
-}
-
-void GUIFormSpecMenu::setInitialFocus()
-{
-       // Set initial focus according to following order of precedence:
-       // 1. first empty editbox
-       // 2. first editbox
-       // 3. first table
-       // 4. last button
-       // 5. first focusable (not statictext, not tabheader)
-       // 6. first child element
-
-       core::list<gui::IGUIElement*> children = getChildren();
-
-       // in case "children" contains any NULL elements, remove them
-       for (core::list<gui::IGUIElement*>::Iterator it = children.begin();
-                       it != children.end();) {
-               if (*it)
-                       ++it;
-               else
-                       it = children.erase(it);
-       }
-
-       // 1. first empty editbox
-       for (gui::IGUIElement *it : children) {
-               if (it->getType() == gui::EGUIET_EDIT_BOX
-                               && it->getText()[0] == 0) {
-                       Environment->setFocus(it);
-                       return;
-               }
-       }
-
-       // 2. first editbox
-       for (gui::IGUIElement *it : children) {
-               if (it->getType() == gui::EGUIET_EDIT_BOX) {
-                       Environment->setFocus(it);
-                       return;
-               }
-       }
-
-       // 3. first table
-       for (gui::IGUIElement *it : children) {
-               if (it->getTypeName() == std::string("GUITable")) {
-                       Environment->setFocus(it);
-                       return;
-               }
-       }
-
-       // 4. last button
-       for (core::list<gui::IGUIElement*>::Iterator it = children.getLast();
-                       it != children.end(); --it) {
-               if ((*it)->getType() == gui::EGUIET_BUTTON) {
-                       Environment->setFocus(*it);
-                       return;
-               }
-       }
-
-       // 5. first focusable (not statictext, not tabheader)
-       for (gui::IGUIElement *it : children) {
-               if (it->getType() != gui::EGUIET_STATIC_TEXT &&
-                       it->getType() != gui::EGUIET_TAB_CONTROL) {
-                       Environment->setFocus(it);
-                       return;
-               }
-       }
-
-       // 6. first child element
-       if (children.empty())
-               Environment->setFocus(this);
-       else
-               Environment->setFocus(*(children.begin()));
-}
-
-GUITable* GUIFormSpecMenu::getTable(const std::string &tablename)
-{
-       for (auto &table : m_tables) {
-               if (tablename == table.first.fname)
-                       return table.second;
-       }
-       return 0;
-}
-
-std::vector<std::string>* GUIFormSpecMenu::getDropDownValues(const std::string &name)
-{
-       for (auto &dropdown : m_dropdowns) {
-               if (name == dropdown.first.fname)
-                       return &dropdown.second;
-       }
-       return NULL;
-}
-
-void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,',');
-
-       if (((parts.size() == 2) || parts.size() == 3) ||
-               ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               if (parts[1].find(';') != std::string::npos)
-                       parts[1] = parts[1].substr(0,parts[1].find(';'));
-
-               data->invsize.X = MYMAX(0, stof(parts[0]));
-               data->invsize.Y = MYMAX(0, stof(parts[1]));
-
-               lockSize(false);
-               if (parts.size() == 3) {
-                       if (parts[2] == "true") {
-                               lockSize(true,v2u32(800,600));
-                       }
-               }
-
-               data->explicit_size = true;
-               return;
-       }
-       errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseContainer(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element, ',');
-
-       if (parts.size() >= 2) {
-               if (parts[1].find(';') != std::string::npos)
-                       parts[1] = parts[1].substr(0, parts[1].find(';'));
-
-               container_stack.push(pos_offset);
-               pos_offset.X += MYMAX(0, stof(parts[0]));
-               pos_offset.Y += MYMAX(0, stof(parts[1]));
-               return;
-       }
-       errorstream<< "Invalid container start element (" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseContainerEnd(parserData* data)
-{
-       if (container_stack.empty()) {
-               errorstream<< "Invalid container end element, no matching container start element"  << std::endl;
-       } else {
-               pos_offset = container_stack.top();
-               container_stack.pop();
-       }
-}
-
-void GUIFormSpecMenu::parseList(parserData* data, const std::string &element)
-{
-       if (m_client == 0) {
-               warningstream<<"invalid use of 'list' with m_client==0"<<std::endl;
-               return;
-       }
-
-       std::vector<std::string> parts = split(element,';');
-
-       if (((parts.size() == 4) || (parts.size() == 5)) ||
-               ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::string location = parts[0];
-               std::string listname = parts[1];
-               std::vector<std::string> v_pos  = split(parts[2],',');
-               std::vector<std::string> v_geom = split(parts[3],',');
-               std::string startindex;
-               if (parts.size() == 5)
-                       startindex = parts[4];
-
-               MY_CHECKPOS("list",2);
-               MY_CHECKGEOM("list",3);
-
-               InventoryLocation loc;
-
-               if(location == "context" || location == "current_name")
-                       loc = m_current_inventory_location;
-               else
-                       loc.deSerialize(location);
-
-               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float)spacing.X;
-               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
-
-               v2s32 geom;
-               geom.X = stoi(v_geom[0]);
-               geom.Y = stoi(v_geom[1]);
-
-               s32 start_i = 0;
-               if (!startindex.empty())
-                       start_i = stoi(startindex);
-
-               if (geom.X < 0 || geom.Y < 0 || start_i < 0) {
-                       errorstream<< "Invalid list element: '" << element << "'"  << std::endl;
-                       return;
-               }
-
-               if(!data->explicit_size)
-                       warningstream<<"invalid use of list without a size[] element"<<std::endl;
-               m_inventorylists.emplace_back(loc, listname, pos, geom, start_i);
-               return;
-       }
-       errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseListRing(parserData* data, const std::string &element)
-{
-       if (m_client == 0) {
-               errorstream << "WARNING: invalid use of 'listring' with m_client==0" << std::endl;
-               return;
-       }
-
-       std::vector<std::string> parts = split(element, ';');
-
-       if (parts.size() == 2) {
-               std::string location = parts[0];
-               std::string listname = parts[1];
-
-               InventoryLocation loc;
-
-               if (location == "context" || location == "current_name")
-                       loc = m_current_inventory_location;
-               else
-                       loc.deSerialize(location);
-
-               m_inventory_rings.emplace_back(loc, listname);
-               return;
-       }
-
-       if (element.empty() && m_inventorylists.size() > 1) {
-               size_t siz = m_inventorylists.size();
-               // insert the last two inv list elements into the list ring
-               const ListDrawSpec &spa = m_inventorylists[siz - 2];
-               const ListDrawSpec &spb = m_inventorylists[siz - 1];
-               m_inventory_rings.emplace_back(spa.inventoryloc, spa.listname);
-               m_inventory_rings.emplace_back(spb.inventoryloc, spb.listname);
-               return;
-       }
-
-       errorstream<< "Invalid list ring element(" << parts.size() << ", "
-               << m_inventorylists.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if (((parts.size() >= 3) && (parts.size() <= 4)) ||
-               ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::string name = parts[1];
-               std::string label = parts[2];
-               std::string selected;
-
-               if (parts.size() >= 4)
-                       selected = parts[3];
-
-               MY_CHECKPOS("checkbox",0);
-
-               v2s32 pos = padding + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float) spacing.X;
-               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
-
-               bool fselected = false;
-
-               if (selected == "true")
-                       fselected = true;
-
-               std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
-
-               core::rect<s32> rect = core::rect<s32>(
-                               pos.X, pos.Y + ((imgsize.Y/2) - m_btn_height),
-                               pos.X + m_font->getDimension(wlabel.c_str()).Width + 25, // text size + size of checkbox
-                               pos.Y + ((imgsize.Y/2) + m_btn_height));
-
-               FieldSpec spec(
-                               name,
-                               wlabel, //Needed for displaying text on MSVC
-                               wlabel,
-                               258+m_fields.size()
-                       );
-
-               spec.ftype = f_CheckBox;
-
-               gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this,
-                                       spec.fid, spec.flabel.c_str());
-
-               if (spec.fname == data->focused_fieldname) {
-                       Environment->setFocus(e);
-               }
-
-               m_checkboxes.emplace_back(spec,e);
-               m_fields.push_back(spec);
-               return;
-       }
-       errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if (parts.size() >= 5) {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::vector<std::string> v_dim = split(parts[1],',');
-               std::string name = parts[3];
-               std::string value = parts[4];
-
-               MY_CHECKPOS("scrollbar",0);
-
-               v2s32 pos = padding + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float) spacing.X;
-               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
-
-               if (v_dim.size() != 2) {
-                       errorstream<< "Invalid size for element " << "scrollbar"
-                               << "specified: \"" << parts[1] << "\"" << std::endl;
-                       return;
-               }
-
-               v2s32 dim;
-               dim.X = stof(v_dim[0]) * (float) spacing.X;
-               dim.Y = stof(v_dim[1]) * (float) spacing.Y;
-
-               core::rect<s32> rect =
-                               core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y);
-
-               FieldSpec spec(
-                               name,
-                               L"",
-                               L"",
-                               258+m_fields.size()
-                       );
-
-               bool is_horizontal = true;
-
-               if (parts[2] == "vertical")
-                       is_horizontal = false;
-
-               spec.ftype = f_ScrollBar;
-               spec.send  = true;
-               gui::IGUIScrollBar* e =
-                               Environment->addScrollBar(is_horizontal,rect,this,spec.fid);
-
-               e->setMax(1000);
-               e->setMin(0);
-               e->setPos(stoi(parts[4]));
-               e->setSmallStep(10);
-               e->setLargeStep(100);
-
-               m_scrollbars.emplace_back(spec,e);
-               m_fields.push_back(spec);
-               return;
-       }
-       errorstream<< "Invalid scrollbar element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if ((parts.size() == 3) ||
-               ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::vector<std::string> v_geom = split(parts[1],',');
-               std::string name = unescape_string(parts[2]);
-
-               MY_CHECKPOS("image", 0);
-               MY_CHECKGEOM("image", 1);
-
-               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float) spacing.X;
-               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
-
-               v2s32 geom;
-               geom.X = stof(v_geom[0]) * (float)imgsize.X;
-               geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
-
-               if (!data->explicit_size)
-                       warningstream<<"invalid use of image without a size[] element"<<std::endl;
-               m_images.emplace_back(name, pos, geom);
-               return;
-       }
-
-       if (parts.size() == 2) {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::string name = unescape_string(parts[1]);
-
-               MY_CHECKPOS("image", 0);
-
-               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float) spacing.X;
-               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
-
-               if (!data->explicit_size)
-                       warningstream<<"invalid use of image without a size[] element"<<std::endl;
-               m_images.emplace_back(name, pos);
-               return;
-       }
-       errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if ((parts.size() == 3) ||
-               ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::vector<std::string> v_geom = split(parts[1],',');
-               std::string name = parts[2];
-
-               MY_CHECKPOS("itemimage",0);
-               MY_CHECKGEOM("itemimage",1);
-
-               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float) spacing.X;
-               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
-
-               v2s32 geom;
-               geom.X = stof(v_geom[0]) * (float)imgsize.X;
-               geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
-
-               if(!data->explicit_size)
-                       warningstream<<"invalid use of item_image without a size[] element"<<std::endl;
-               m_itemimages.emplace_back("", name, pos, geom);
-               return;
-       }
-       errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
-               const std::string &type)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if ((parts.size() == 4) ||
-               ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::vector<std::string> v_geom = split(parts[1],',');
-               std::string name = parts[2];
-               std::string label = parts[3];
-
-               MY_CHECKPOS("button",0);
-               MY_CHECKGEOM("button",1);
-
-               v2s32 pos = padding + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float)spacing.X;
-               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
-
-               v2s32 geom;
-               geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
-               pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
-
-               core::rect<s32> rect =
-                               core::rect<s32>(pos.X, pos.Y - m_btn_height,
-                                               pos.X + geom.X, pos.Y + m_btn_height);
-
-               if(!data->explicit_size)
-                       warningstream<<"invalid use of button without a size[] element"<<std::endl;
-
-               std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
-
-               FieldSpec spec(
-                       name,
-                       wlabel,
-                       L"",
-                       258+m_fields.size()
-               );
-               spec.ftype = f_Button;
-               if(type == "button_exit")
-                       spec.is_exit = true;
-               gui::IGUIButton* e = Environment->addButton(rect, this, spec.fid,
-                               spec.flabel.c_str());
-
-               if (spec.fname == data->focused_fieldname) {
-                       Environment->setFocus(e);
-               }
-
-               m_fields.push_back(spec);
-               return;
-       }
-       errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if (((parts.size() == 3) || (parts.size() == 4)) ||
-               ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::vector<std::string> v_geom = split(parts[1],',');
-               std::string name = unescape_string(parts[2]);
-
-               MY_CHECKPOS("background",0);
-               MY_CHECKGEOM("background",1);
-
-               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X - (float)imgsize.X)/2;
-               pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y - (float)imgsize.Y)/2;
-
-               v2s32 geom;
-               geom.X = stof(v_geom[0]) * (float)spacing.X;
-               geom.Y = stof(v_geom[1]) * (float)spacing.Y;
-
-               if (!data->explicit_size)
-                       warningstream<<"invalid use of background without a size[] element"<<std::endl;
-
-               bool clip = false;
-               if (parts.size() == 4 && is_yes(parts[3])) {
-                       pos.X = stoi(v_pos[0]); //acts as offset
-                       pos.Y = stoi(v_pos[1]); //acts as offset
-                       clip = true;
-               }
-               m_backgrounds.emplace_back(name, pos, geom, clip);
-
-               return;
-       }
-       errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseTableOptions(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       data->table_options.clear();
-       for (const std::string &part : parts) {
-               // Parse table option
-               std::string opt = unescape_string(part);
-               data->table_options.push_back(GUITable::splitOption(opt));
-       }
-}
-
-void GUIFormSpecMenu::parseTableColumns(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       data->table_columns.clear();
-       for (const std::string &part : parts) {
-               std::vector<std::string> col_parts = split(part,',');
-               GUITable::TableColumn column;
-               // Parse column type
-               if (!col_parts.empty())
-                       column.type = col_parts[0];
-               // Parse column options
-               for (size_t j = 1; j < col_parts.size(); ++j) {
-                       std::string opt = unescape_string(col_parts[j]);
-                       column.options.push_back(GUITable::splitOption(opt));
-               }
-               data->table_columns.push_back(column);
-       }
-}
-
-void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if (((parts.size() == 4) || (parts.size() == 5)) ||
-               ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::vector<std::string> v_geom = split(parts[1],',');
-               std::string name = parts[2];
-               std::vector<std::string> items = split(parts[3],',');
-               std::string str_initial_selection;
-               std::string str_transparent = "false";
-
-               if (parts.size() >= 5)
-                       str_initial_selection = parts[4];
-
-               MY_CHECKPOS("table",0);
-               MY_CHECKGEOM("table",1);
-
-               v2s32 pos = padding + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float)spacing.X;
-               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
-
-               v2s32 geom;
-               geom.X = stof(v_geom[0]) * (float)spacing.X;
-               geom.Y = stof(v_geom[1]) * (float)spacing.Y;
-
-               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
-
-               FieldSpec spec(
-                       name,
-                       L"",
-                       L"",
-                       258+m_fields.size()
-               );
-
-               spec.ftype = f_Table;
-
-               for (std::string &item : items) {
-                       item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item))));
-               }
-
-               //now really show table
-               GUITable *e = new GUITable(Environment, this, spec.fid, rect,
-                               m_tsrc);
-
-               if (spec.fname == data->focused_fieldname) {
-                       Environment->setFocus(e);
-               }
-
-               e->setTable(data->table_options, data->table_columns, items);
-
-               if (data->table_dyndata.find(name) != data->table_dyndata.end()) {
-                       e->setDynamicData(data->table_dyndata[name]);
-               }
-
-               if (!str_initial_selection.empty() && str_initial_selection != "0")
-                       e->setSelected(stoi(str_initial_selection));
-
-               m_tables.emplace_back(spec, e);
-               m_fields.push_back(spec);
-               return;
-       }
-       errorstream<< "Invalid table element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if (((parts.size() == 4) || (parts.size() == 5) || (parts.size() == 6)) ||
-               ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::vector<std::string> v_geom = split(parts[1],',');
-               std::string name = parts[2];
-               std::vector<std::string> items = split(parts[3],',');
-               std::string str_initial_selection;
-               std::string str_transparent = "false";
-
-               if (parts.size() >= 5)
-                       str_initial_selection = parts[4];
-
-               if (parts.size() >= 6)
-                       str_transparent = parts[5];
-
-               MY_CHECKPOS("textlist",0);
-               MY_CHECKGEOM("textlist",1);
-
-               v2s32 pos = padding + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float)spacing.X;
-               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
-
-               v2s32 geom;
-               geom.X = stof(v_geom[0]) * (float)spacing.X;
-               geom.Y = stof(v_geom[1]) * (float)spacing.Y;
-
-
-               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
-
-               FieldSpec spec(
-                       name,
-                       L"",
-                       L"",
-                       258+m_fields.size()
-               );
-
-               spec.ftype = f_Table;
-
-               for (std::string &item : items) {
-                       item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item))));
-               }
-
-               //now really show list
-               GUITable *e = new GUITable(Environment, this, spec.fid, rect,
-                               m_tsrc);
-
-               if (spec.fname == data->focused_fieldname) {
-                       Environment->setFocus(e);
-               }
-
-               e->setTextList(items, is_yes(str_transparent));
-
-               if (data->table_dyndata.find(name) != data->table_dyndata.end()) {
-                       e->setDynamicData(data->table_dyndata[name]);
-               }
-
-               if (!str_initial_selection.empty() && str_initial_selection != "0")
-                       e->setSelected(stoi(str_initial_selection));
-
-               m_tables.emplace_back(spec, e);
-               m_fields.push_back(spec);
-               return;
-       }
-       errorstream<< "Invalid textlist element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-
-void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if ((parts.size() == 5) ||
-               ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::string name = parts[2];
-               std::vector<std::string> items = split(parts[3],',');
-               std::string str_initial_selection;
-               str_initial_selection = parts[4];
-
-               MY_CHECKPOS("dropdown",0);
-
-               v2s32 pos = padding + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float)spacing.X;
-               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
-
-               s32 width = stof(parts[1]) * (float)spacing.Y;
-
-               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y,
-                               pos.X + width, pos.Y + (m_btn_height * 2));
-
-               FieldSpec spec(
-                       name,
-                       L"",
-                       L"",
-                       258+m_fields.size()
-               );
-
-               spec.ftype = f_DropDown;
-               spec.send = true;
-
-               //now really show list
-               gui::IGUIComboBox *e = Environment->addComboBox(rect, this,spec.fid);
-
-               if (spec.fname == data->focused_fieldname) {
-                       Environment->setFocus(e);
-               }
-
-               for (const std::string &item : items) {
-                       e->addItem(unescape_translate(unescape_string(
-                               utf8_to_wide(item))).c_str());
-               }
-
-               if (!str_initial_selection.empty())
-                       e->setSelected(stoi(str_initial_selection)-1);
-
-               m_fields.push_back(spec);
-
-               m_dropdowns.emplace_back(spec, std::vector<std::string>());
-               std::vector<std::string> &values = m_dropdowns.back().second;
-               for (const std::string &item : items) {
-                       values.push_back(unescape_string(item));
-               }
-
-               return;
-       }
-       errorstream << "Invalid dropdown element(" << parts.size() << "): '"
-                               << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseFieldCloseOnEnter(parserData *data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-       if (parts.size() == 2 ||
-                       (parts.size() > 2 && m_formspec_version > FORMSPEC_API_VERSION)) {
-               field_close_on_enter[parts[0]] = is_yes(parts[1]);
-       }
-}
-
-void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if ((parts.size() == 4) || (parts.size() == 5) ||
-               ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::vector<std::string> v_geom = split(parts[1],',');
-               std::string name = parts[2];
-               std::string label = parts[3];
-
-               MY_CHECKPOS("pwdfield",0);
-               MY_CHECKGEOM("pwdfield",1);
-
-               v2s32 pos = pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float)spacing.X;
-               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
-
-               v2s32 geom;
-               geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
-
-               pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
-               pos.Y -= m_btn_height;
-               geom.Y = m_btn_height*2;
-
-               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
-
-               std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
-
-               FieldSpec spec(
-                       name,
-                       wlabel,
-                       L"",
-                       258+m_fields.size()
-                       );
-
-               spec.send = true;
-               gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid);
-
-               if (spec.fname == data->focused_fieldname) {
-                       Environment->setFocus(e);
-               }
-
-               if (label.length() >= 1)
-               {
-                       int font_height = g_fontengine->getTextHeight();
-                       rect.UpperLeftCorner.Y -= font_height;
-                       rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
-                       addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
-               }
-
-               e->setPasswordBox(true,L'*');
-
-               irr::SEvent evt;
-               evt.EventType            = EET_KEY_INPUT_EVENT;
-               evt.KeyInput.Key         = KEY_END;
-               evt.KeyInput.Char        = 0;
-               evt.KeyInput.Control     = false;
-               evt.KeyInput.Shift       = false;
-               evt.KeyInput.PressedDown = true;
-               e->OnEvent(evt);
-
-               if (parts.size() >= 5) {
-                       // TODO: remove after 2016-11-03
-                       warningstream << "pwdfield: use field_close_on_enter[name, enabled]" <<
-                                       " instead of the 5th param" << std::endl;
-                       field_close_on_enter[name] = is_yes(parts[4]);
-               }
-
-               m_fields.push_back(spec);
-               return;
-       }
-       errorstream<< "Invalid pwdfield element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseSimpleField(parserData* data,
-               std::vector<std::string> &parts)
-{
-       std::string name = parts[0];
-       std::string label = parts[1];
-       std::string default_val = parts[2];
-
-       core::rect<s32> rect;
-
-       if(data->explicit_size)
-               warningstream<<"invalid use of unpositioned \"field\" in inventory"<<std::endl;
-
-       v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
-       pos.Y = ((m_fields.size()+2)*60);
-       v2s32 size = DesiredRect.getSize();
-
-       rect = core::rect<s32>(size.X / 2 - 150, pos.Y,
-                       (size.X / 2 - 150) + 300, pos.Y + (m_btn_height*2));
-
-
-       if(m_form_src)
-               default_val = m_form_src->resolveText(default_val);
-
-
-       std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
-
-       FieldSpec spec(
-               name,
-               wlabel,
-               utf8_to_wide(unescape_string(default_val)),
-               258+m_fields.size()
-       );
-
-       if (name.empty()) {
-               // spec field id to 0, this stops submit searching for a value that isn't there
-               addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid);
-       } else {
-               spec.send = true;
-               gui::IGUIElement *e;
-#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
-               if (g_settings->getBool("freetype")) {
-                       e = (gui::IGUIElement *) new gui::intlGUIEditBox(spec.fdefault.c_str(),
-                               true, Environment, this, spec.fid, rect);
-                       e->drop();
-               } else {
-#else
-               {
-#endif
-                       e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
-               }
-               if (spec.fname == data->focused_fieldname) {
-                       Environment->setFocus(e);
-               }
-
-               irr::SEvent evt;
-               evt.EventType            = EET_KEY_INPUT_EVENT;
-               evt.KeyInput.Key         = KEY_END;
-               evt.KeyInput.Char        = 0;
-               evt.KeyInput.Control     = 0;
-               evt.KeyInput.Shift       = 0;
-               evt.KeyInput.PressedDown = true;
-               e->OnEvent(evt);
-
-               if (label.length() >= 1)
-               {
-                       int font_height = g_fontengine->getTextHeight();
-                       rect.UpperLeftCorner.Y -= font_height;
-                       rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
-                       addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
-               }
-       }
-
-       if (parts.size() >= 4) {
-               // TODO: remove after 2016-11-03
-               warningstream << "field/simple: use field_close_on_enter[name, enabled]" <<
-                               " instead of the 4th param" << std::endl;
-               field_close_on_enter[name] = is_yes(parts[3]);
-       }
-
-       m_fields.push_back(spec);
-}
-
-void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector<std::string>& parts,
-               const std::string &type)
-{
-
-       std::vector<std::string> v_pos = split(parts[0],',');
-       std::vector<std::string> v_geom = split(parts[1],',');
-       std::string name = parts[2];
-       std::string label = parts[3];
-       std::string default_val = parts[4];
-
-       MY_CHECKPOS(type,0);
-       MY_CHECKGEOM(type,1);
-
-       v2s32 pos = pos_offset * spacing;
-       pos.X += stof(v_pos[0]) * (float) spacing.X;
-       pos.Y += stof(v_pos[1]) * (float) spacing.Y;
-
-       v2s32 geom;
-
-       geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
-
-       if (type == "textarea")
-       {
-               geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
-               pos.Y += m_btn_height;
-       }
-       else
-       {
-               pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
-               pos.Y -= m_btn_height;
-               geom.Y = m_btn_height*2;
-       }
-
-       core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
-
-       if(!data->explicit_size)
-               warningstream<<"invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
-
-       if(m_form_src)
-               default_val = m_form_src->resolveText(default_val);
-
-
-       std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
-
-       FieldSpec spec(
-               name,
-               wlabel,
-               utf8_to_wide(unescape_string(default_val)),
-               258+m_fields.size()
-       );
-
-       bool is_editable = !name.empty();
-
-       if (is_editable)
-               spec.send = true;
-
-       gui::IGUIEditBox *e = nullptr;
-       const wchar_t *text = spec.fdefault.empty() ?
-               wlabel.c_str() : spec.fdefault.c_str();
-
-#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
-       if (g_settings->getBool("freetype")) {
-               e = (gui::IGUIEditBox *) new gui::intlGUIEditBox(text,
-                       true, Environment, this, spec.fid, rect, is_editable, true);
-               e->drop();
-       } else {
-#else
-       {
-#endif
-               e = new GUIEditBoxWithScrollBar(text, true,
-                       Environment, this, spec.fid, rect, is_editable, true);
-       }
-
-       if (is_editable && spec.fname == data->focused_fieldname)
-               Environment->setFocus(e);
-
-       if (e) {
-               if (type == "textarea")
-               {
-                       e->setMultiLine(true);
-                       e->setWordWrap(true);
-                       e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_UPPERLEFT);
-               } else {
-                       irr::SEvent evt;
-                       evt.EventType            = EET_KEY_INPUT_EVENT;
-                       evt.KeyInput.Key         = KEY_END;
-                       evt.KeyInput.Char        = 0;
-                       evt.KeyInput.Control     = 0;
-                       evt.KeyInput.Shift       = 0;
-                       evt.KeyInput.PressedDown = true;
-                       e->OnEvent(evt);
-               }
-       }
-
-       if (is_editable && !label.empty()) {
-               int font_height = g_fontengine->getTextHeight();
-               rect.UpperLeftCorner.Y -= font_height;
-               rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
-               addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
-       }
-
-       if (parts.size() >= 6) {
-               // TODO: remove after 2016-11-03
-               warningstream << "field/textarea: use field_close_on_enter[name, enabled]" <<
-                               " instead of the 6th param" << std::endl;
-               field_close_on_enter[name] = is_yes(parts[5]);
-       }
-
-       m_fields.push_back(spec);
-}
-
-void GUIFormSpecMenu::parseField(parserData* data, const std::string &element,
-               const std::string &type)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if (parts.size() == 3 || parts.size() == 4) {
-               parseSimpleField(data,parts);
-               return;
-       }
-
-       if ((parts.size() == 5) || (parts.size() == 6) ||
-               ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               parseTextArea(data,parts,type);
-               return;
-       }
-       errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if ((parts.size() == 2) ||
-               ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::string text = parts[1];
-
-               MY_CHECKPOS("label",0);
-
-               v2s32 pos = padding + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float)spacing.X;
-               pos.Y += (stof(v_pos[1]) + 7.0/30.0) * (float)spacing.Y;
-
-               if(!data->explicit_size)
-                       warningstream<<"invalid use of label without a size[] element"<<std::endl;
-
-               std::vector<std::string> lines = split(text, '\n');
-
-               for (unsigned int i = 0; i != lines.size(); i++) {
-                       // Lines are spaced at the nominal distance of
-                       // 2/5 inventory slot, even if the font doesn't
-                       // quite match that.  This provides consistent
-                       // form layout, at the expense of sometimes
-                       // having sub-optimal spacing for the font.
-                       // We multiply by 2 and then divide by 5, rather
-                       // than multiply by 0.4, to get exact results
-                       // in the integer cases: 0.4 is not exactly
-                       // representable in binary floating point.
-                       s32 posy = pos.Y + ((float)i) * spacing.Y * 2.0 / 5.0;
-                       std::wstring wlabel = utf8_to_wide(unescape_string(lines[i]));
-                       core::rect<s32> rect = core::rect<s32>(
-                               pos.X, posy - m_btn_height,
-                               pos.X + m_font->getDimension(wlabel.c_str()).Width,
-                               posy + m_btn_height);
-                       FieldSpec spec(
-                               "",
-                               wlabel,
-                               L"",
-                               258+m_fields.size()
-                       );
-                       gui::IGUIStaticText *e =
-                               addStaticText(Environment, spec.flabel.c_str(),
-                                       rect, false, false, this, spec.fid);
-                       e->setTextAlignment(gui::EGUIA_UPPERLEFT,
-                                               gui::EGUIA_CENTER);
-                       m_fields.push_back(spec);
-               }
-
-               return;
-       }
-       errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if ((parts.size() == 2) ||
-               ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::wstring text = unescape_translate(
-                       unescape_string(utf8_to_wide(parts[1])));
-
-               MY_CHECKPOS("vertlabel",1);
-
-               v2s32 pos = padding + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float)spacing.X;
-               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
-
-               core::rect<s32> rect = core::rect<s32>(
-                               pos.X, pos.Y+((imgsize.Y/2)- m_btn_height),
-                               pos.X+15, pos.Y +
-                                       font_line_height(m_font)
-                                       * (text.length()+1)
-                                       +((imgsize.Y/2)- m_btn_height));
-               //actually text.length() would be correct but adding +1 avoids to break all mods
-
-               if(!data->explicit_size)
-                       warningstream<<"invalid use of label without a size[] element"<<std::endl;
-
-               std::wstring label;
-
-               for (wchar_t i : text) {
-                       label += i;
-                       label += L"\n";
-               }
-
-               FieldSpec spec(
-                       "",
-                       label,
-                       L"",
-                       258+m_fields.size()
-               );
-               gui::IGUIStaticText *t =
-                               addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid);
-               t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
-               m_fields.push_back(spec);
-               return;
-       }
-       errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &element,
-               const std::string &type)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if ((((parts.size() >= 5) && (parts.size() <= 8)) && (parts.size() != 6)) ||
-               ((parts.size() > 8) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::vector<std::string> v_geom = split(parts[1],',');
-               std::string image_name = parts[2];
-               std::string name = parts[3];
-               std::string label = parts[4];
-
-               MY_CHECKPOS("imagebutton",0);
-               MY_CHECKGEOM("imagebutton",1);
-
-               v2s32 pos = padding + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float)spacing.X;
-               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
-               v2s32 geom;
-               geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
-               geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
-
-               bool noclip     = false;
-               bool drawborder = true;
-               std::string pressed_image_name;
-
-               if (parts.size() >= 7) {
-                       if (parts[5] == "true")
-                               noclip = true;
-                       if (parts[6] == "false")
-                               drawborder = false;
-               }
-
-               if (parts.size() >= 8) {
-                       pressed_image_name = parts[7];
-               }
-
-               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
-
-               if(!data->explicit_size)
-                       warningstream<<"invalid use of image_button without a size[] element"<<std::endl;
-
-               image_name = unescape_string(image_name);
-               pressed_image_name = unescape_string(pressed_image_name);
-
-               std::wstring wlabel = utf8_to_wide(unescape_string(label));
-
-               FieldSpec spec(
-                       name,
-                       wlabel,
-                       utf8_to_wide(image_name),
-                       258+m_fields.size()
-               );
-               spec.ftype = f_Button;
-               if(type == "image_button_exit")
-                       spec.is_exit = true;
-
-               video::ITexture *texture = 0;
-               video::ITexture *pressed_texture = 0;
-               texture = m_tsrc->getTexture(image_name);
-               if (!pressed_image_name.empty())
-                       pressed_texture = m_tsrc->getTexture(pressed_image_name);
-               else
-                       pressed_texture = texture;
-
-               gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str());
-
-               if (spec.fname == data->focused_fieldname) {
-                       Environment->setFocus(e);
-               }
-
-               e->setUseAlphaChannel(true);
-               e->setImage(guiScalingImageButton(
-                       Environment->getVideoDriver(), texture, geom.X, geom.Y));
-               e->setPressedImage(guiScalingImageButton(
-                       Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
-               e->setScaleImage(true);
-               e->setNotClipped(noclip);
-               e->setDrawBorder(drawborder);
-
-               m_fields.push_back(spec);
-               return;
-       }
-
-       errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if (((parts.size() == 4) || (parts.size() == 6)) ||
-               ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::string name = parts[1];
-               std::vector<std::string> buttons = split(parts[2],',');
-               std::string str_index = parts[3];
-               bool show_background = true;
-               bool show_border = true;
-               int tab_index = stoi(str_index) -1;
-
-               MY_CHECKPOS("tabheader",0);
-
-               if (parts.size() == 6) {
-                       if (parts[4] == "true")
-                               show_background = false;
-                       if (parts[5] == "false")
-                               show_border = false;
-               }
-
-               FieldSpec spec(
-                       name,
-                       L"",
-                       L"",
-                       258+m_fields.size()
-               );
-
-               spec.ftype = f_TabHeader;
-
-               v2s32 pos = pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float)spacing.X;
-               pos.Y += stof(v_pos[1]) * (float)spacing.Y - m_btn_height * 2;
-               v2s32 geom;
-               geom.X = DesiredRect.getWidth();
-               geom.Y = m_btn_height*2;
-
-               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
-                               pos.Y+geom.Y);
-
-               gui::IGUITabControl *e = Environment->addTabControl(rect, this,
-                               show_background, show_border, spec.fid);
-               e->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT,
-                               irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
-               e->setTabHeight(m_btn_height*2);
-
-               if (spec.fname == data->focused_fieldname) {
-                       Environment->setFocus(e);
-               }
-
-               e->setNotClipped(true);
-
-               for (const std::string &button : buttons) {
-                       e->addTab(unescape_translate(unescape_string(
-                               utf8_to_wide(button))).c_str(), -1);
-               }
-
-               if ((tab_index >= 0) &&
-                               (buttons.size() < INT_MAX) &&
-                               (tab_index < (int) buttons.size()))
-                       e->setActiveTab(tab_index);
-
-               m_fields.push_back(spec);
-               return;
-       }
-       errorstream << "Invalid TabHeader element(" << parts.size() << "): '"
-                       << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &element)
-{
-
-       if (m_client == 0) {
-               warningstream << "invalid use of item_image_button with m_client==0"
-                       << std::endl;
-               return;
-       }
-
-       std::vector<std::string> parts = split(element,';');
-
-       if ((parts.size() == 5) ||
-               ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::vector<std::string> v_geom = split(parts[1],',');
-               std::string item_name = parts[2];
-               std::string name = parts[3];
-               std::string label = parts[4];
-
-               label = unescape_string(label);
-               item_name = unescape_string(item_name);
-
-               MY_CHECKPOS("itemimagebutton",0);
-               MY_CHECKGEOM("itemimagebutton",1);
-
-               v2s32 pos = padding + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float)spacing.X;
-               pos.Y += stof(v_pos[1]) * (float)spacing.Y;
-               v2s32 geom;
-               geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
-               geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
-
-               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
-
-               if(!data->explicit_size)
-                       warningstream<<"invalid use of item_image_button without a size[] element"<<std::endl;
-
-               IItemDefManager *idef = m_client->idef();
-               ItemStack item;
-               item.deSerialize(item_name, idef);
-
-               m_tooltips[name] =
-                       TooltipSpec(utf8_to_wide(item.getDefinition(idef).description),
-                                               m_default_tooltip_bgcolor,
-                                               m_default_tooltip_color);
-
-               FieldSpec spec(
-                       name,
-                       utf8_to_wide(label),
-                       utf8_to_wide(item_name),
-                       258 + m_fields.size()
-               );
-
-               gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, L"");
-
-               if (spec.fname == data->focused_fieldname) {
-                       Environment->setFocus(e);
-               }
-
-               spec.ftype = f_Button;
-               rect+=data->basepos-padding;
-               spec.rect=rect;
-               m_fields.push_back(spec);
-               pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float) spacing.X;
-               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
-               m_itemimages.emplace_back("", item_name, e, pos, geom);
-               m_static_texts.emplace_back(utf8_to_wide(label), rect, e);
-               return;
-       }
-       errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if ((parts.size() == 3) ||
-               ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               std::vector<std::string> v_pos = split(parts[0],',');
-               std::vector<std::string> v_geom = split(parts[1],',');
-
-               MY_CHECKPOS("box",0);
-               MY_CHECKGEOM("box",1);
-
-               v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
-               pos.X += stof(v_pos[0]) * (float) spacing.X;
-               pos.Y += stof(v_pos[1]) * (float) spacing.Y;
-
-               v2s32 geom;
-               geom.X = stof(v_geom[0]) * (float)spacing.X;
-               geom.Y = stof(v_geom[1]) * (float)spacing.Y;
-
-               video::SColor tmp_color;
-
-               if (parseColorString(parts[2], tmp_color, false)) {
-                       BoxDrawSpec spec(pos, geom, tmp_color);
-
-                       m_boxes.push_back(spec);
-               }
-               else {
-                       errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'  INVALID COLOR"  << std::endl;
-               }
-               return;
-       }
-       errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if (((parts.size() == 1) || (parts.size() == 2)) ||
-                       ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION))) {
-               parseColorString(parts[0], m_bgcolor, false);
-
-               if (parts.size() == 2) {
-                       std::string fullscreen = parts[1];
-                       m_bgfullscreen = is_yes(fullscreen);
-               }
-
-               return;
-       }
-
-       errorstream << "Invalid bgcolor element(" << parts.size() << "): '" << element << "'"
-                       << std::endl;
-}
-
-void GUIFormSpecMenu::parseListColors(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-
-       if (((parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) ||
-               ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
-       {
-               parseColorString(parts[0], m_slotbg_n, false);
-               parseColorString(parts[1], m_slotbg_h, false);
-
-               if (parts.size() >= 3) {
-                       if (parseColorString(parts[2], m_slotbordercolor, false)) {
-                               m_slotborder = true;
-                       }
-               }
-               if (parts.size() == 5) {
-                       video::SColor tmp_color;
-
-                       if (parseColorString(parts[3], tmp_color, false))
-                               m_default_tooltip_bgcolor = tmp_color;
-                       if (parseColorString(parts[4], tmp_color, false))
-                               m_default_tooltip_color = tmp_color;
-               }
-               return;
-       }
-       errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element,';');
-       if (parts.size() == 2) {
-               std::string name = parts[0];
-               m_tooltips[name] = TooltipSpec(utf8_to_wide(unescape_string(parts[1])),
-                       m_default_tooltip_bgcolor, m_default_tooltip_color);
-               return;
-       }
-
-       if (parts.size() == 4) {
-               std::string name = parts[0];
-               video::SColor tmp_color1, tmp_color2;
-               if ( parseColorString(parts[2], tmp_color1, false) && parseColorString(parts[3], tmp_color2, false) ) {
-                       m_tooltips[name] = TooltipSpec(utf8_to_wide(unescape_string(parts[1])),
-                               tmp_color1, tmp_color2);
-                       return;
-               }
-       }
-       errorstream<< "Invalid tooltip element(" << parts.size() << "): '" << element << "'"  << std::endl;
-}
-
-bool GUIFormSpecMenu::parseVersionDirect(const std::string &data)
-{
-       //some prechecks
-       if (data.empty())
-               return false;
-
-       std::vector<std::string> parts = split(data,'[');
-
-       if (parts.size() < 2) {
-               return false;
-       }
-
-       if (parts[0] != "formspec_version") {
-               return false;
-       }
-
-       if (is_number(parts[1])) {
-               m_formspec_version = mystoi(parts[1]);
-               return true;
-       }
-
-       return false;
-}
-
-bool GUIFormSpecMenu::parseSizeDirect(parserData* data, const std::string &element)
-{
-       if (element.empty())
-               return false;
-
-       std::vector<std::string> parts = split(element,'[');
-
-       if (parts.size() < 2)
-               return false;
-
-       std::string type = trim(parts[0]);
-       std::string description = trim(parts[1]);
-
-       if (type != "size" && type != "invsize")
-               return false;
-
-       if (type == "invsize")
-               log_deprecated("Deprecated formspec element \"invsize\" is used");
-
-       parseSize(data, description);
-
-       return true;
-}
-
-bool GUIFormSpecMenu::parsePositionDirect(parserData *data, const std::string &element)
-{
-       if (element.empty())
-               return false;
-
-       std::vector<std::string> parts = split(element, '[');
-
-       if (parts.size() != 2)
-               return false;
-
-       std::string type = trim(parts[0]);
-       std::string description = trim(parts[1]);
-
-       if (type != "position")
-               return false;
-
-       parsePosition(data, description);
-
-       return true;
-}
-
-void GUIFormSpecMenu::parsePosition(parserData *data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element, ',');
-
-       if (parts.size() == 2) {
-               data->offset.X = stof(parts[0]);
-               data->offset.Y = stof(parts[1]);
-               return;
-       }
-
-       errorstream << "Invalid position element (" << parts.size() << "): '" << element << "'" << std::endl;
-}
-
-bool GUIFormSpecMenu::parseAnchorDirect(parserData *data, const std::string &element)
-{
-       if (element.empty())
-               return false;
-
-       std::vector<std::string> parts = split(element, '[');
-
-       if (parts.size() != 2)
-               return false;
-
-       std::string type = trim(parts[0]);
-       std::string description = trim(parts[1]);
-
-       if (type != "anchor")
-               return false;
-
-       parseAnchor(data, description);
-
-       return true;
-}
-
-void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element)
-{
-       std::vector<std::string> parts = split(element, ',');
-
-       if (parts.size() == 2) {
-               data->anchor.X = stof(parts[0]);
-               data->anchor.Y = stof(parts[1]);
-               return;
-       }
-
-       errorstream << "Invalid anchor element (" << parts.size() << "): '" << element
-                       << "'" << std::endl;
-}
-
-void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
-{
-       //some prechecks
-       if (element.empty())
-               return;
-
-       std::vector<std::string> parts = split(element,'[');
-
-       // ugly workaround to keep compatibility
-       if (parts.size() > 2) {
-               if (trim(parts[0]) == "image") {
-                       for (unsigned int i=2;i< parts.size(); i++) {
-                               parts[1] += "[" + parts[i];
-                       }
-               }
-               else { return; }
-       }
-
-       if (parts.size() < 2) {
-               return;
-       }
-
-       std::string type = trim(parts[0]);
-       std::string description = trim(parts[1]);
-
-       if (type == "container") {
-               parseContainer(data, description);
-               return;
-       }
-
-       if (type == "container_end") {
-               parseContainerEnd(data);
-               return;
-       }
-
-       if (type == "list") {
-               parseList(data, description);
-               return;
-       }
-
-       if (type == "listring") {
-               parseListRing(data, description);
-               return;
-       }
-
-       if (type == "checkbox") {
-               parseCheckbox(data, description);
-               return;
-       }
-
-       if (type == "image") {
-               parseImage(data, description);
-               return;
-       }
-
-       if (type == "item_image") {
-               parseItemImage(data, description);
-               return;
-       }
-
-       if (type == "button" || type == "button_exit") {
-               parseButton(data, description, type);
-               return;
-       }
-
-       if (type == "background") {
-               parseBackground(data,description);
-               return;
-       }
-
-       if (type == "tableoptions"){
-               parseTableOptions(data,description);
-               return;
-       }
-
-       if (type == "tablecolumns"){
-               parseTableColumns(data,description);
-               return;
-       }
-
-       if (type == "table"){
-               parseTable(data,description);
-               return;
-       }
-
-       if (type == "textlist"){
-               parseTextList(data,description);
-               return;
-       }
-
-       if (type == "dropdown"){
-               parseDropDown(data,description);
-               return;
-       }
-
-       if (type == "field_close_on_enter") {
-               parseFieldCloseOnEnter(data, description);
-               return;
-       }
-
-       if (type == "pwdfield") {
-               parsePwdField(data,description);
-               return;
-       }
-
-       if ((type == "field") || (type == "textarea")){
-               parseField(data,description,type);
-               return;
-       }
-
-       if (type == "label") {
-               parseLabel(data,description);
-               return;
-       }
-
-       if (type == "vertlabel") {
-               parseVertLabel(data,description);
-               return;
-       }
-
-       if (type == "item_image_button") {
-               parseItemImageButton(data,description);
-               return;
-       }
-
-       if ((type == "image_button") || (type == "image_button_exit")) {
-               parseImageButton(data,description,type);
-               return;
-       }
-
-       if (type == "tabheader") {
-               parseTabHeader(data,description);
-               return;
-       }
-
-       if (type == "box") {
-               parseBox(data,description);
-               return;
-       }
-
-       if (type == "bgcolor") {
-               parseBackgroundColor(data,description);
-               return;
-       }
-
-       if (type == "listcolors") {
-               parseListColors(data,description);
-               return;
-       }
-
-       if (type == "tooltip") {
-               parseTooltip(data,description);
-               return;
-       }
-
-       if (type == "scrollbar") {
-               parseScrollBar(data, description);
-               return;
-       }
-
-       // Ignore others
-       infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
-                       << std::endl;
-}
-
-void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
-{
-       /* useless to regenerate without a screensize */
-       if ((screensize.X <= 0) || (screensize.Y <= 0)) {
-               return;
-       }
-
-       parserData mydata;
-
-       //preserve tables
-       for (auto &m_table : m_tables) {
-               std::string tablename = m_table.first.fname;
-               GUITable *table = m_table.second;
-               mydata.table_dyndata[tablename] = table->getDynamicData();
-       }
-
-       //set focus
-       if (!m_focused_element.empty())
-               mydata.focused_fieldname = m_focused_element;
-
-       //preserve focus
-       gui::IGUIElement *focused_element = Environment->getFocus();
-       if (focused_element && focused_element->getParent() == this) {
-               s32 focused_id = focused_element->getID();
-               if (focused_id > 257) {
-                       for (const GUIFormSpecMenu::FieldSpec &field : m_fields) {
-                               if (field.fid == focused_id) {
-                                       mydata.focused_fieldname = field.fname;
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       // Remove children
-       removeChildren();
-
-       for (auto &table_it : m_tables) {
-               table_it.second->drop();
-       }
-
-       mydata.size= v2s32(100,100);
-       mydata.screensize = screensize;
-       mydata.offset = v2f32(0.5f, 0.5f);
-       mydata.anchor = v2f32(0.5f, 0.5f);
-
-       // Base position of contents of form
-       mydata.basepos = getBasePos();
-
-       /* Convert m_init_draw_spec to m_inventorylists */
-
-       m_inventorylists.clear();
-       m_images.clear();
-       m_backgrounds.clear();
-       m_itemimages.clear();
-       m_tables.clear();
-       m_checkboxes.clear();
-       m_scrollbars.clear();
-       m_fields.clear();
-       m_boxes.clear();
-       m_tooltips.clear();
-       m_inventory_rings.clear();
-       m_static_texts.clear();
-       m_dropdowns.clear();
-
-       m_bgfullscreen = false;
-
-       {
-               v3f formspec_bgcolor = g_settings->getV3F("formspec_default_bg_color");
-               m_bgcolor = video::SColor(
-                       (u8) clamp_u8(g_settings->getS32("formspec_default_bg_opacity")),
-                       clamp_u8(myround(formspec_bgcolor.X)),
-                       clamp_u8(myround(formspec_bgcolor.Y)),
-                       clamp_u8(myround(formspec_bgcolor.Z))
-               );
-       }
-
-       {
-               v3f formspec_bgcolor = g_settings->getV3F("formspec_fullscreen_bg_color");
-               m_fullscreen_bgcolor = video::SColor(
-                       (u8) clamp_u8(g_settings->getS32("formspec_fullscreen_bg_opacity")),
-                       clamp_u8(myround(formspec_bgcolor.X)),
-                       clamp_u8(myround(formspec_bgcolor.Y)),
-                       clamp_u8(myround(formspec_bgcolor.Z))
-               );
-       }
-
-
-       m_slotbg_n = video::SColor(255,128,128,128);
-       m_slotbg_h = video::SColor(255,192,192,192);
-
-       m_default_tooltip_bgcolor = video::SColor(255,110,130,60);
-       m_default_tooltip_color = video::SColor(255,255,255,255);
-
-       m_slotbordercolor = video::SColor(200,0,0,0);
-       m_slotborder = false;
-
-       // Add tooltip
-       {
-               assert(!m_tooltip_element);
-               // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
-               m_tooltip_element = addStaticText(Environment, L"",core::rect<s32>(0,0,110,18));
-               m_tooltip_element->enableOverrideColor(true);
-               m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
-               m_tooltip_element->setDrawBackground(true);
-               m_tooltip_element->setDrawBorder(true);
-               m_tooltip_element->setOverrideColor(m_default_tooltip_color);
-               m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
-               m_tooltip_element->setWordWrap(false);
-               //we're not parent so no autograb for this one!
-               m_tooltip_element->grab();
-       }
-
-       std::vector<std::string> elements = split(m_formspec_string,']');
-       unsigned int i = 0;
-
-       /* try to read version from first element only */
-       if (!elements.empty()) {
-               if ( parseVersionDirect(elements[0]) ) {
-                       i++;
-               }
-       }
-
-       /* we need size first in order to calculate image scale */
-       mydata.explicit_size = false;
-       for (; i< elements.size(); i++) {
-               if (!parseSizeDirect(&mydata, elements[i])) {
-                       break;
-               }
-       }
-
-       /* "position" element is always after "size" element if it used */
-       for (; i< elements.size(); i++) {
-               if (!parsePositionDirect(&mydata, elements[i])) {
-                       break;
-               }
-       }
-
-       /* "anchor" element is always after "position" (or  "size" element) if it used */
-       for (; i< elements.size(); i++) {
-               if (!parseAnchorDirect(&mydata, elements[i])) {
-                       break;
-               }
-       }
-
-
-       if (mydata.explicit_size) {
-               // compute scaling for specified form size
-               if (m_lock) {
-                       v2u32 current_screensize = RenderingEngine::get_video_driver()->getScreenSize();
-                       v2u32 delta = current_screensize - m_lockscreensize;
-
-                       if (current_screensize.Y > m_lockscreensize.Y)
-                               delta.Y /= 2;
-                       else
-                               delta.Y = 0;
-
-                       if (current_screensize.X > m_lockscreensize.X)
-                               delta.X /= 2;
-                       else
-                               delta.X = 0;
-
-                       offset = v2s32(delta.X,delta.Y);
-
-                       mydata.screensize = m_lockscreensize;
-               } else {
-                       offset = v2s32(0,0);
-               }
-
-               double gui_scaling = g_settings->getFloat("gui_scaling");
-               double screen_dpi = RenderingEngine::getDisplayDensity() * 96;
-
-               double use_imgsize;
-               if (m_lock) {
-                       // In fixed-size mode, inventory image size
-                       // is 0.53 inch multiplied by the gui_scaling
-                       // config parameter.  This magic size is chosen
-                       // to make the main menu (15.5 inventory images
-                       // wide, including border) just fit into the
-                       // default window (800 pixels wide) at 96 DPI
-                       // and default scaling (1.00).
-                       use_imgsize = 0.5555 * screen_dpi * gui_scaling;
-               } else {
-                       // In variable-size mode, we prefer to make the
-                       // inventory image size 1/15 of screen height,
-                       // multiplied by the gui_scaling config parameter.
-                       // If the preferred size won't fit the whole
-                       // form on the screen, either horizontally or
-                       // vertically, then we scale it down to fit.
-                       // (The magic numbers in the computation of what
-                       // fits arise from the scaling factors in the
-                       // following stanza, including the form border,
-                       // help text space, and 0.1 inventory slot spare.)
-                       // However, a minimum size is also set, that
-                       // the image size can't be less than 0.3 inch
-                       // multiplied by gui_scaling, even if this means
-                       // the form doesn't fit the screen.
-                       double prefer_imgsize = mydata.screensize.Y / 15 *
-                                                       gui_scaling;
-                       double fitx_imgsize = mydata.screensize.X /
-                               ((5.0/4.0) * (0.5 + mydata.invsize.X));
-                       double fity_imgsize = mydata.screensize.Y /
-                               ((15.0/13.0) * (0.85 * mydata.invsize.Y));
-                       double screen_dpi = RenderingEngine::getDisplayDensity() * 96;
-                       double min_imgsize = 0.3 * screen_dpi * gui_scaling;
-                       use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize,
-                               MYMIN(fitx_imgsize, fity_imgsize)));
-               }
-
-               // Everything else is scaled in proportion to the
-               // inventory image size.  The inventory slot spacing
-               // is 5/4 image size horizontally and 15/13 image size
-               // vertically.  The padding around the form (incorporating
-               // the border of the outer inventory slots) is 3/8
-               // image size.  Font height (baseline to baseline)
-               // is 2/5 vertical inventory slot spacing, and button
-               // half-height is 7/8 of font height.
-               imgsize = v2s32(use_imgsize, use_imgsize);
-               spacing = v2s32(use_imgsize*5.0/4, use_imgsize*15.0/13);
-               padding = v2s32(use_imgsize*3.0/8, use_imgsize*3.0/8);
-               m_btn_height = use_imgsize*15.0/13 * 0.35;
-
-               m_font = g_fontengine->getFont();
-
-               mydata.size = v2s32(
-                       padding.X*2+spacing.X*(mydata.invsize.X-1.0)+imgsize.X,
-                       padding.Y*2+spacing.Y*(mydata.invsize.Y-1.0)+imgsize.Y + m_btn_height*2.0/3.0
-               );
-               DesiredRect = mydata.rect = core::rect<s32>(
-                               (s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * (f32)mydata.size.X) + offset.X,
-                               (s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * (f32)mydata.size.Y) + offset.Y,
-                               (s32)((f32)mydata.screensize.X * mydata.offset.X) + (s32)((1.0 - mydata.anchor.X) * (f32)mydata.size.X) + offset.X,
-                               (s32)((f32)mydata.screensize.Y * mydata.offset.Y) + (s32)((1.0 - mydata.anchor.Y) * (f32)mydata.size.Y) + offset.Y
-               );
-       } else {
-               // Non-size[] form must consist only of text fields and
-               // implicit "Proceed" button.  Use default font, and
-               // temporary form size which will be recalculated below.
-               m_font = g_fontengine->getFont();
-               m_btn_height = font_line_height(m_font) * 0.875;
-               DesiredRect = core::rect<s32>(
-                       (s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * 580.0),
-                       (s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * 300.0),
-                       (s32)((f32)mydata.screensize.X * mydata.offset.X) + (s32)((1.0 - mydata.anchor.X) * 580.0),
-                       (s32)((f32)mydata.screensize.Y * mydata.offset.Y) + (s32)((1.0 - mydata.anchor.Y) * 300.0)
-               );
-       }
-       recalculateAbsolutePosition(false);
-       mydata.basepos = getBasePos();
-       m_tooltip_element->setOverrideFont(m_font);
-
-       gui::IGUISkin* skin = Environment->getSkin();
-       sanity_check(skin);
-       gui::IGUIFont *old_font = skin->getFont();
-       skin->setFont(m_font);
-
-       pos_offset = v2s32();
-       for (; i< elements.size(); i++) {
-               parseElement(&mydata, elements[i]);
-       }
-
-       if (!container_stack.empty()) {
-               errorstream << "Invalid formspec string: container was never closed!"
-                       << std::endl;
-       }
-
-       // If there are fields without explicit size[], add a "Proceed"
-       // button and adjust size to fit all the fields.
-       if (!m_fields.empty() && !mydata.explicit_size) {
-               mydata.rect = core::rect<s32>(
-                               mydata.screensize.X/2 - 580/2,
-                               mydata.screensize.Y/2 - 300/2,
-                               mydata.screensize.X/2 + 580/2,
-                               mydata.screensize.Y/2 + 240/2+(m_fields.size()*60)
-               );
-               DesiredRect = mydata.rect;
-               recalculateAbsolutePosition(false);
-               mydata.basepos = getBasePos();
-
-               {
-                       v2s32 pos = mydata.basepos;
-                       pos.Y = ((m_fields.size()+2)*60);
-
-                       v2s32 size = DesiredRect.getSize();
-                       mydata.rect =
-                                       core::rect<s32>(size.X/2-70, pos.Y,
-                                                       (size.X/2-70)+140, pos.Y + (m_btn_height*2));
-                       const wchar_t *text = wgettext("Proceed");
-                       Environment->addButton(mydata.rect, this, 257, text);
-                       delete[] text;
-               }
-
-       }
-
-       //set initial focus if parser didn't set it
-       focused_element = Environment->getFocus();
-       if (!focused_element
-                       || !isMyChild(focused_element)
-                       || focused_element->getType() == gui::EGUIET_TAB_CONTROL)
-               setInitialFocus();
-
-       skin->setFont(old_font);
-}
-
-#ifdef __ANDROID__
-bool GUIFormSpecMenu::getAndroidUIInput()
-{
-       /* no dialog shown */
-       if (m_JavaDialogFieldName == "") {
-               return false;
-       }
-
-       /* still waiting */
-       if (porting::getInputDialogState() == -1) {
-               return true;
-       }
-
-       std::string fieldname = m_JavaDialogFieldName;
-       m_JavaDialogFieldName = "";
-
-       /* no value abort dialog processing */
-       if (porting::getInputDialogState() != 0) {
-               return false;
-       }
-
-       for(std::vector<FieldSpec>::iterator iter =  m_fields.begin();
-                       iter != m_fields.end(); ++iter) {
-
-               if (iter->fname != fieldname) {
-                       continue;
-               }
-               IGUIElement* tochange = getElementFromId(iter->fid);
-
-               if (tochange == 0) {
-                       return false;
-               }
-
-               if (tochange->getType() != irr::gui::EGUIET_EDIT_BOX) {
-                       return false;
-               }
-
-               std::string text = porting::getInputDialogValue();
-
-               ((gui::IGUIEditBox*) tochange)->
-                       setText(utf8_to_wide(text).c_str());
-       }
-       return false;
-}
-#endif
-
-GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
-{
-       core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
-
-       for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) {
-               for(s32 i=0; i<s.geom.X*s.geom.Y; i++) {
-                       s32 item_i = i + s.start_item_i;
-                       s32 x = (i%s.geom.X) * spacing.X;
-                       s32 y = (i/s.geom.X) * spacing.Y;
-                       v2s32 p0(x,y);
-                       core::rect<s32> rect = imgrect + s.pos + p0;
-                       if(rect.isPointInside(p))
-                       {
-                               return ItemSpec(s.inventoryloc, s.listname, item_i);
-                       }
-               }
-       }
-
-       return ItemSpec(InventoryLocation(), "", -1);
-}
-
-void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
-               bool &item_hovered)
-{
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-
-       Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
-       if(!inv){
-               warningstream<<"GUIFormSpecMenu::drawList(): "
-                               <<"The inventory location "
-                               <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
-                               <<std::endl;
-               return;
-       }
-       InventoryList *ilist = inv->getList(s.listname);
-       if(!ilist){
-               warningstream<<"GUIFormSpecMenu::drawList(): "
-                               <<"The inventory list \""<<s.listname<<"\" @ \""
-                               <<s.inventoryloc.dump()<<"\" doesn't exist"
-                               <<std::endl;
-               return;
-       }
-
-       core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
-
-       for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
-       {
-               s32 item_i = i + s.start_item_i;
-               if(item_i >= (s32) ilist->getSize())
-                       break;
-               s32 x = (i%s.geom.X) * spacing.X;
-               s32 y = (i/s.geom.X) * spacing.Y;
-               v2s32 p(x,y);
-               core::rect<s32> rect = imgrect + s.pos + p;
-               ItemStack item;
-               if(ilist)
-                       item = ilist->getItem(item_i);
-
-               bool selected = m_selected_item
-                       && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
-                       && m_selected_item->listname == s.listname
-                       && m_selected_item->i == item_i;
-               bool hovering = rect.isPointInside(m_pointer);
-               ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED :
-                       (hovering ? IT_ROT_HOVERED : IT_ROT_NONE);
-
-               if (phase == 0) {
-                       if (hovering) {
-                               item_hovered = true;
-                               driver->draw2DRectangle(m_slotbg_h, rect, &AbsoluteClippingRect);
-                       } else {
-                               driver->draw2DRectangle(m_slotbg_n, rect, &AbsoluteClippingRect);
-                       }
-               }
-
-               //Draw inv slot borders
-               if (m_slotborder) {
-                       s32 x1 = rect.UpperLeftCorner.X;
-                       s32 y1 = rect.UpperLeftCorner.Y;
-                       s32 x2 = rect.LowerRightCorner.X;
-                       s32 y2 = rect.LowerRightCorner.Y;
-                       s32 border = 1;
-                       driver->draw2DRectangle(m_slotbordercolor,
-                               core::rect<s32>(v2s32(x1 - border, y1 - border),
-                                                               v2s32(x2 + border, y1)), NULL);
-                       driver->draw2DRectangle(m_slotbordercolor,
-                               core::rect<s32>(v2s32(x1 - border, y2),
-                                                               v2s32(x2 + border, y2 + border)), NULL);
-                       driver->draw2DRectangle(m_slotbordercolor,
-                               core::rect<s32>(v2s32(x1 - border, y1),
-                                                               v2s32(x1, y2)), NULL);
-                       driver->draw2DRectangle(m_slotbordercolor,
-                               core::rect<s32>(v2s32(x2, y1),
-                                                               v2s32(x2 + border, y2)), NULL);
-               }
-
-               if(phase == 1)
-               {
-                       // Draw item stack
-                       if(selected)
-                       {
-                               item.takeItem(m_selected_amount);
-                       }
-                       if(!item.empty())
-                       {
-                               drawItemStack(driver, m_font, item,
-                                       rect, &AbsoluteClippingRect, m_client,
-                                       rotation_kind);
-                       }
-
-                       // Draw tooltip
-                       std::wstring tooltip_text;
-                       if (hovering && !m_selected_item) {
-                               const std::string &desc = item.metadata.getString("description");
-                               if (desc.empty())
-                                       tooltip_text =
-                                               utf8_to_wide(item.getDefinition(m_client->idef()).description);
-                               else
-                                       tooltip_text = utf8_to_wide(desc);
-
-                               if (!item.name.empty()) {
-                                       if (tooltip_text.empty())
-                                               tooltip_text = utf8_to_wide(item.name);
-                                       if (m_tooltip_append_itemname)
-                                               tooltip_text += utf8_to_wide(" [" + item.name + "]");
-                               }
-                       }
-                       if (!tooltip_text.empty()) {
-                               showTooltip(tooltip_text, m_default_tooltip_color,
-                                       m_default_tooltip_bgcolor);
-                       }
-               }
-       }
-}
-
-void GUIFormSpecMenu::drawSelectedItem()
-{
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-
-       if (!m_selected_item) {
-               drawItemStack(driver, m_font, ItemStack(),
-                       core::rect<s32>(v2s32(0, 0), v2s32(0, 0)),
-                       NULL, m_client, IT_ROT_DRAGGED);
-               return;
-       }
-
-       Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
-       sanity_check(inv);
-       InventoryList *list = inv->getList(m_selected_item->listname);
-       sanity_check(list);
-       ItemStack stack = list->getItem(m_selected_item->i);
-       stack.count = m_selected_amount;
-
-       core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
-       core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
-       rect.constrainTo(driver->getViewPort());
-       drawItemStack(driver, m_font, stack, rect, NULL, m_client, IT_ROT_DRAGGED);
-}
-
-void GUIFormSpecMenu::drawMenu()
-{
-       if (m_form_src) {
-               const std::string &newform = m_form_src->getForm();
-               if (newform != m_formspec_string) {
-                       m_formspec_string = newform;
-                       regenerateGui(m_screensize_old);
-               }
-       }
-
-       gui::IGUISkin* skin = Environment->getSkin();
-       sanity_check(skin != NULL);
-       gui::IGUIFont *old_font = skin->getFont();
-       skin->setFont(m_font);
-
-       updateSelectedItem();
-
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-
-       v2u32 screenSize = driver->getScreenSize();
-       core::rect<s32> allbg(0, 0, screenSize.X, screenSize.Y);
-
-       if (m_bgfullscreen)
-               driver->draw2DRectangle(m_fullscreen_bgcolor, allbg, &allbg);
-       else
-               driver->draw2DRectangle(m_bgcolor, AbsoluteRect, &AbsoluteClippingRect);
-
-       m_tooltip_element->setVisible(false);
-
-       /*
-               Draw backgrounds
-       */
-       for (const GUIFormSpecMenu::ImageDrawSpec &spec : m_backgrounds) {
-               video::ITexture *texture = m_tsrc->getTexture(spec.name);
-
-               if (texture != 0) {
-                       // Image size on screen
-                       core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
-                       // Image rectangle on screen
-                       core::rect<s32> rect = imgrect + spec.pos;
-
-                       if (spec.clip) {
-                               core::dimension2d<s32> absrec_size = AbsoluteRect.getSize();
-                               rect = core::rect<s32>(AbsoluteRect.UpperLeftCorner.X - spec.pos.X,
-                                                                       AbsoluteRect.UpperLeftCorner.Y - spec.pos.Y,
-                                                                       AbsoluteRect.UpperLeftCorner.X + absrec_size.Width + spec.pos.X,
-                                                                       AbsoluteRect.UpperLeftCorner.Y + absrec_size.Height + spec.pos.Y);
-                       }
-
-                       const video::SColor color(255,255,255,255);
-                       const video::SColor colors[] = {color,color,color,color};
-                       draw2DImageFilterScaled(driver, texture, rect,
-                               core::rect<s32>(core::position2d<s32>(0,0),
-                                               core::dimension2di(texture->getOriginalSize())),
-                               NULL/*&AbsoluteClippingRect*/, colors, true);
-               } else {
-                       errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl;
-                       errorstream << "\t" << spec.name << std::endl;
-               }
-       }
-
-       /*
-               Draw Boxes
-       */
-       for (const GUIFormSpecMenu::BoxDrawSpec &spec : m_boxes) {
-               irr::video::SColor todraw = spec.color;
-
-               todraw.setAlpha(140);
-
-               core::rect<s32> rect(spec.pos.X,spec.pos.Y,
-                                                       spec.pos.X + spec.geom.X,spec.pos.Y + spec.geom.Y);
-
-               driver->draw2DRectangle(todraw, rect, 0);
-       }
-
-       /*
-               Call base class
-       */
-       gui::IGUIElement::draw();
-
-       /*
-               Draw images
-       */
-       for (const GUIFormSpecMenu::ImageDrawSpec &spec : m_images) {
-               video::ITexture *texture = m_tsrc->getTexture(spec.name);
-
-               if (texture != 0) {
-                       const core::dimension2d<u32>& img_origsize = texture->getOriginalSize();
-                       // Image size on screen
-                       core::rect<s32> imgrect;
-
-                       if (spec.scale)
-                               imgrect = core::rect<s32>(0,0,spec.geom.X, spec.geom.Y);
-                       else {
-
-                               imgrect = core::rect<s32>(0,0,img_origsize.Width,img_origsize.Height);
-                       }
-                       // Image rectangle on screen
-                       core::rect<s32> rect = imgrect + spec.pos;
-                       const video::SColor color(255,255,255,255);
-                       const video::SColor colors[] = {color,color,color,color};
-                       draw2DImageFilterScaled(driver, texture, rect,
-                               core::rect<s32>(core::position2d<s32>(0,0),img_origsize),
-                               NULL/*&AbsoluteClippingRect*/, colors, true);
-               }
-               else {
-                       errorstream << "GUIFormSpecMenu::drawMenu() Draw images unable to load texture:" << std::endl;
-                       errorstream << "\t" << spec.name << std::endl;
-               }
-       }
-
-       /*
-               Draw item images
-       */
-       for (const GUIFormSpecMenu::ImageDrawSpec &spec : m_itemimages) {
-               if (m_client == 0)
-                       break;
-
-               IItemDefManager *idef = m_client->idef();
-               ItemStack item;
-               item.deSerialize(spec.item_name, idef);
-               core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
-               // Viewport rectangle on screen
-               core::rect<s32> rect = imgrect + spec.pos;
-               if (spec.parent_button && spec.parent_button->isPressed()) {
-#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
-                       rect += core::dimension2d<s32>(
-                               0.05 * (float)rect.getWidth(), 0.05 * (float)rect.getHeight());
-#else
-                       rect += core::dimension2d<s32>(
-                               skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
-                               skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y));
-#endif
-               }
-               drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect,
-                               m_client, IT_ROT_NONE);
-       }
-
-       /*
-               Draw items
-               Phase 0: Item slot rectangles
-               Phase 1: Item images; prepare tooltip
-       */
-       bool item_hovered = false;
-       int start_phase = 0;
-       for (int phase = start_phase; phase <= 1; phase++) {
-               for (const GUIFormSpecMenu::ListDrawSpec &spec : m_inventorylists) {
-                       drawList(spec, phase, item_hovered);
-               }
-       }
-       if (!item_hovered) {
-               drawItemStack(driver, m_font, ItemStack(),
-                       core::rect<s32>(v2s32(0, 0), v2s32(0, 0)),
-                       NULL, m_client, IT_ROT_HOVERED);
-       }
-
-/* TODO find way to show tooltips on touchscreen */
-#ifndef HAVE_TOUCHSCREENGUI
-       m_pointer = RenderingEngine::get_raw_device()->getCursorControl()->getPosition();
-#endif
-
-       /*
-               Draw static text elements
-       */
-       for (const GUIFormSpecMenu::StaticTextSpec &spec : m_static_texts) {
-               core::rect<s32> rect = spec.rect;
-               if (spec.parent_button && spec.parent_button->isPressed()) {
-#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
-                       rect += core::dimension2d<s32>(
-                               0.05 * (float)rect.getWidth(), 0.05 * (float)rect.getHeight());
-#else
-                       // Use image offset instead of text's because its a bit smaller
-                       // and fits better, also TEXT_OFFSET_X is always 0
-                       rect += core::dimension2d<s32>(
-                               skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
-                               skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y));
-#endif
-               }
-               video::SColor color(255, 255, 255, 255);
-               m_font->draw(spec.text.c_str(), rect, color, true, true, &rect);
-       }
-
-       /*
-               Draw fields/buttons tooltips
-       */
-       gui::IGUIElement *hovered =
-                       Environment->getRootGUIElement()->getElementFromPoint(m_pointer);
-
-       if (hovered != NULL) {
-               s32 id = hovered->getID();
-
-               u64 delta = 0;
-               if (id == -1) {
-                       m_old_tooltip_id = id;
-               } else {
-                       if (id == m_old_tooltip_id) {
-                               delta = porting::getDeltaMs(m_hovered_time, porting::getTimeMs());
-                       } else {
-                               m_hovered_time = porting::getTimeMs();
-                               m_old_tooltip_id = id;
-                       }
-               }
-
-               // Find and update the current tooltip
-               if (id != -1 && delta >= m_tooltip_show_delay) {
-                       for (const FieldSpec &field : m_fields) {
-
-                               if (field.fid != id)
-                                       continue;
-
-                               const std::wstring &text = m_tooltips[field.fname].tooltip;
-                               if (!text.empty())
-                                       showTooltip(text, m_tooltips[field.fname].color,
-                                               m_tooltips[field.fname].bgcolor);
-
-                               break;
-                       }
-               }
-       }
-
-       m_tooltip_element->draw();
-
-       /*
-               Draw dragged item stack
-       */
-       drawSelectedItem();
-
-       skin->setFont(old_font);
-}
-
-
-void GUIFormSpecMenu::showTooltip(const std::wstring &text,
-       const irr::video::SColor &color, const irr::video::SColor &bgcolor)
-{
-       const std::wstring ntext = translate_string(text);
-       m_tooltip_element->setOverrideColor(color);
-       m_tooltip_element->setBackgroundColor(bgcolor);
-       setStaticText(m_tooltip_element, ntext.c_str());
-
-       // Tooltip size and offset
-       s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
-#if (IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2) || USE_FREETYPE == 1
-       std::vector<std::wstring> text_rows = str_split(ntext, L'\n');
-       s32 tooltip_height = m_tooltip_element->getTextHeight() * text_rows.size() + 5;
-#else
-       s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
-#endif
-       v2u32 screenSize = Environment->getVideoDriver()->getScreenSize();
-       int tooltip_offset_x = m_btn_height;
-       int tooltip_offset_y = m_btn_height;
-#ifdef __ANDROID__
-       tooltip_offset_x *= 3;
-       tooltip_offset_y  = 0;
-       if (m_pointer.X > (s32)screenSize.X / 2)
-               tooltip_offset_x = -(tooltip_offset_x + tooltip_width);
-#endif
-
-       // Calculate and set the tooltip position
-       s32 tooltip_x = m_pointer.X + tooltip_offset_x;
-       s32 tooltip_y = m_pointer.Y + tooltip_offset_y;
-       if (tooltip_x + tooltip_width > (s32)screenSize.X)
-               tooltip_x = (s32)screenSize.X - tooltip_width  - m_btn_height;
-       if (tooltip_y + tooltip_height > (s32)screenSize.Y)
-               tooltip_y = (s32)screenSize.Y - tooltip_height - m_btn_height;
-
-       m_tooltip_element->setRelativePosition(
-               core::rect<s32>(
-                       core::position2d<s32>(tooltip_x, tooltip_y),
-                       core::dimension2d<s32>(tooltip_width, tooltip_height)
-               )
-       );
-
-       // Display the tooltip
-       m_tooltip_element->setVisible(true);
-       bringToFront(m_tooltip_element);
-}
-
-void GUIFormSpecMenu::updateSelectedItem()
-{
-       // If the selected stack has become empty for some reason, deselect it.
-       // If the selected stack has become inaccessible, deselect it.
-       // If the selected stack has become smaller, adjust m_selected_amount.
-       ItemStack selected = verifySelectedItem();
-
-       // WARNING: BLACK MAGIC
-       // See if there is a stack suited for our current guess.
-       // If such stack does not exist, clear the guess.
-       if (!m_selected_content_guess.name.empty() &&
-                       selected.name == m_selected_content_guess.name &&
-                       selected.count == m_selected_content_guess.count){
-               // Selected item fits the guess. Skip the black magic.
-       } else if (!m_selected_content_guess.name.empty()) {
-               bool found = false;
-               for(u32 i=0; i<m_inventorylists.size() && !found; i++){
-                       const ListDrawSpec &s = m_inventorylists[i];
-                       Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
-                       if(!inv)
-                               continue;
-                       InventoryList *list = inv->getList(s.listname);
-                       if(!list)
-                               continue;
-                       for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
-                               u32 item_i = i + s.start_item_i;
-                               if(item_i >= list->getSize())
-                                       continue;
-                               ItemStack stack = list->getItem(item_i);
-                               if(stack.name == m_selected_content_guess.name &&
-                                               stack.count == m_selected_content_guess.count){
-                                       found = true;
-                                       infostream<<"Client: Changing selected content guess to "
-                                                       <<s.inventoryloc.dump()<<" "<<s.listname
-                                                       <<" "<<item_i<<std::endl;
-                                       delete m_selected_item;
-                                       m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
-                                       m_selected_amount = stack.count;
-                               }
-                       }
-               }
-               if(!found){
-                       infostream<<"Client: Discarding selected content guess: "
-                                       <<m_selected_content_guess.getItemString()<<std::endl;
-                       m_selected_content_guess.name = "";
-               }
-       }
-
-       // If craftresult is nonempty and nothing else is selected, select it now.
-       if(!m_selected_item)
-       {
-               for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) {
-                       if(s.listname == "craftpreview")
-                       {
-                               Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
-                               InventoryList *list = inv->getList("craftresult");
-                               if(list && list->getSize() >= 1 && !list->getItem(0).empty())
-                               {
-                                       m_selected_item = new ItemSpec;
-                                       m_selected_item->inventoryloc = s.inventoryloc;
-                                       m_selected_item->listname = "craftresult";
-                                       m_selected_item->i = 0;
-                                       m_selected_amount = 0;
-                                       m_selected_dragging = false;
-                                       break;
-                               }
-                       }
-               }
-       }
-
-       // If craftresult is selected, keep the whole stack selected
-       if(m_selected_item && m_selected_item->listname == "craftresult")
-       {
-               m_selected_amount = verifySelectedItem().count;
-       }
-}
-
-ItemStack GUIFormSpecMenu::verifySelectedItem()
-{
-       // If the selected stack has become empty for some reason, deselect it.
-       // If the selected stack has become inaccessible, deselect it.
-       // If the selected stack has become smaller, adjust m_selected_amount.
-       // Return the selected stack.
-
-       if(m_selected_item)
-       {
-               if(m_selected_item->isValid())
-               {
-                       Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
-                       if(inv)
-                       {
-                               InventoryList *list = inv->getList(m_selected_item->listname);
-                               if(list && (u32) m_selected_item->i < list->getSize())
-                               {
-                                       ItemStack stack = list->getItem(m_selected_item->i);
-                                       if(m_selected_amount > stack.count)
-                                               m_selected_amount = stack.count;
-                                       if(!stack.empty())
-                                               return stack;
-                               }
-                       }
-               }
-
-               // selection was not valid
-               delete m_selected_item;
-               m_selected_item = NULL;
-               m_selected_amount = 0;
-               m_selected_dragging = false;
-       }
-       return ItemStack();
-}
-
-void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
-{
-       if(m_text_dst)
-       {
-               StringMap fields;
-
-               if (quitmode == quit_mode_accept) {
-                       fields["quit"] = "true";
-               }
-
-               if (quitmode == quit_mode_cancel) {
-                       fields["quit"] = "true";
-                       m_text_dst->gotText(fields);
-                       return;
-               }
-
-               if (current_keys_pending.key_down) {
-                       fields["key_down"] = "true";
-                       current_keys_pending.key_down = false;
-               }
-
-               if (current_keys_pending.key_up) {
-                       fields["key_up"] = "true";
-                       current_keys_pending.key_up = false;
-               }
-
-               if (current_keys_pending.key_enter) {
-                       fields["key_enter"] = "true";
-                       current_keys_pending.key_enter = false;
-               }
-
-               if (!current_field_enter_pending.empty()) {
-                       fields["key_enter_field"] = current_field_enter_pending;
-                       current_field_enter_pending = "";
-               }
-
-               if (current_keys_pending.key_escape) {
-                       fields["key_escape"] = "true";
-                       current_keys_pending.key_escape = false;
-               }
-
-               for (const GUIFormSpecMenu::FieldSpec &s : m_fields) {
-                       if(s.send) {
-                               std::string name = s.fname;
-                               if (s.ftype == f_Button) {
-                                       fields[name] = wide_to_utf8(s.flabel);
-                               } else if (s.ftype == f_Table) {
-                                       GUITable *table = getTable(s.fname);
-                                       if (table) {
-                                               fields[name] = table->checkEvent();
-                                       }
-                               }
-                               else if(s.ftype == f_DropDown) {
-                                       // no dynamic cast possible due to some distributions shipped
-                                       // without rtti support in irrlicht
-                                       IGUIElement * element = getElementFromId(s.fid);
-                                       gui::IGUIComboBox *e = NULL;
-                                       if ((element) && (element->getType() == gui::EGUIET_COMBO_BOX)) {
-                                               e = static_cast<gui::IGUIComboBox*>(element);
-                                       }
-                                       s32 selected = e->getSelected();
-                                       if (selected >= 0) {
-                                               std::vector<std::string> *dropdown_values =
-                                                       getDropDownValues(s.fname);
-                                               if (dropdown_values && selected < (s32)dropdown_values->size()) {
-                                                       fields[name] = (*dropdown_values)[selected];
-                                               }
-                                       }
-                               }
-                               else if (s.ftype == f_TabHeader) {
-                                       // no dynamic cast possible due to some distributions shipped
-                                       // without rttzi support in irrlicht
-                                       IGUIElement * element = getElementFromId(s.fid);
-                                       gui::IGUITabControl *e = NULL;
-                                       if ((element) && (element->getType() == gui::EGUIET_TAB_CONTROL)) {
-                                               e = static_cast<gui::IGUITabControl *>(element);
-                                       }
-
-                                       if (e != 0) {
-                                               std::stringstream ss;
-                                               ss << (e->getActiveTab() +1);
-                                               fields[name] = ss.str();
-                                       }
-                               }
-                               else if (s.ftype == f_CheckBox) {
-                                       // no dynamic cast possible due to some distributions shipped
-                                       // without rtti support in irrlicht
-                                       IGUIElement * element = getElementFromId(s.fid);
-                                       gui::IGUICheckBox *e = NULL;
-                                       if ((element) && (element->getType() == gui::EGUIET_CHECK_BOX)) {
-                                               e = static_cast<gui::IGUICheckBox*>(element);
-                                       }
-
-                                       if (e != 0) {
-                                               if (e->isChecked())
-                                                       fields[name] = "true";
-                                               else
-                                                       fields[name] = "false";
-                                       }
-                               }
-                               else if (s.ftype == f_ScrollBar) {
-                                       // no dynamic cast possible due to some distributions shipped
-                                       // without rtti support in irrlicht
-                                       IGUIElement * element = getElementFromId(s.fid);
-                                       gui::IGUIScrollBar *e = NULL;
-                                       if ((element) && (element->getType() == gui::EGUIET_SCROLL_BAR)) {
-                                               e = static_cast<gui::IGUIScrollBar*>(element);
-                                       }
-
-                                       if (e != 0) {
-                                               std::stringstream os;
-                                               os << e->getPos();
-                                               if (s.fdefault == L"Changed")
-                                                       fields[name] = "CHG:" + os.str();
-                                               else
-                                                       fields[name] = "VAL:" + os.str();
-                                       }
-                               }
-                               else
-                               {
-                                       IGUIElement* e = getElementFromId(s.fid);
-                                       if(e != NULL) {
-                                               fields[name] = wide_to_utf8(e->getText());
-                                       }
-                               }
-                       }
-               }
-
-               m_text_dst->gotText(fields);
-       }
-}
-
-static bool isChild(gui::IGUIElement * tocheck, gui::IGUIElement * parent)
-{
-       while(tocheck != NULL) {
-               if (tocheck == parent) {
-                       return true;
-               }
-               tocheck = tocheck->getParent();
-       }
-       return false;
-}
-
-bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
-{
-       // The IGUITabControl renders visually using the skin's selected
-       // font, which we override for the duration of form drawing,
-       // but computes tab hotspots based on how it would have rendered
-       // using the font that is selected at the time of button release.
-       // To make these two consistent, temporarily override the skin's
-       // font while the IGUITabControl is processing the event.
-       if (event.EventType == EET_MOUSE_INPUT_EVENT &&
-                       event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
-               s32 x = event.MouseInput.X;
-               s32 y = event.MouseInput.Y;
-               gui::IGUIElement *hovered =
-                       Environment->getRootGUIElement()->getElementFromPoint(
-                               core::position2d<s32>(x, y));
-               if (hovered && isMyChild(hovered) &&
-                               hovered->getType() == gui::EGUIET_TAB_CONTROL) {
-                       gui::IGUISkin* skin = Environment->getSkin();
-                       sanity_check(skin != NULL);
-                       gui::IGUIFont *old_font = skin->getFont();
-                       skin->setFont(m_font);
-                       bool retval = hovered->OnEvent(event);
-                       skin->setFont(old_font);
-                       return retval;
-               }
-       }
-
-       // Fix Esc/Return key being eaten by checkboxen and tables
-       if(event.EventType==EET_KEY_INPUT_EVENT) {
-               KeyPress kp(event.KeyInput);
-               if (kp == EscapeKey || kp == CancelKey
-                               || kp == getKeySetting("keymap_inventory")
-                               || event.KeyInput.Key==KEY_RETURN) {
-                       gui::IGUIElement *focused = Environment->getFocus();
-                       if (focused && isMyChild(focused) &&
-                                       (focused->getType() == gui::EGUIET_LIST_BOX ||
-                                        focused->getType() == gui::EGUIET_CHECK_BOX)) {
-                               OnEvent(event);
-                               return true;
-                       }
-               }
-       }
-       // Mouse wheel events: send to hovered element instead of focused
-       if(event.EventType==EET_MOUSE_INPUT_EVENT
-                       && event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
-               s32 x = event.MouseInput.X;
-               s32 y = event.MouseInput.Y;
-               gui::IGUIElement *hovered =
-                       Environment->getRootGUIElement()->getElementFromPoint(
-                               core::position2d<s32>(x, y));
-               if (hovered && isMyChild(hovered)) {
-                       hovered->OnEvent(event);
-                       return true;
-               }
-       }
-
-       if (event.EventType == EET_MOUSE_INPUT_EVENT) {
-               s32 x = event.MouseInput.X;
-               s32 y = event.MouseInput.Y;
-               gui::IGUIElement *hovered =
-                       Environment->getRootGUIElement()->getElementFromPoint(
-                               core::position2d<s32>(x, y));
-               if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
-                       m_old_tooltip_id = -1;
-               }
-               if (!isChild(hovered,this)) {
-                       if (DoubleClickDetection(event)) {
-                               return true;
-                       }
-               }
-       }
-
-       #ifdef __ANDROID__
-       // display software keyboard when clicking edit boxes
-       if (event.EventType == EET_MOUSE_INPUT_EVENT
-                       && event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
-               gui::IGUIElement *hovered =
-                       Environment->getRootGUIElement()->getElementFromPoint(
-                               core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
-               if ((hovered) && (hovered->getType() == irr::gui::EGUIET_EDIT_BOX)) {
-                       bool retval = hovered->OnEvent(event);
-                       if (retval) {
-                               Environment->setFocus(hovered);
-                       }
-                       m_JavaDialogFieldName = getNameByID(hovered->getID());
-                       std::string message   = gettext("Enter ");
-                       std::string label     = wide_to_utf8(getLabelByID(hovered->getID()));
-                       if (label == "") {
-                               label = "text";
-                       }
-                       message += gettext(label) + ":";
-
-                       /* single line text input */
-                       int type = 2;
-
-                       /* multi line text input */
-                       if (((gui::IGUIEditBox*) hovered)->isMultiLineEnabled()) {
-                               type = 1;
-                       }
-
-                       /* passwords are always single line */
-                       if (((gui::IGUIEditBox*) hovered)->isPasswordBox()) {
-                               type = 3;
-                       }
-
-                       porting::showInputDialog(gettext("ok"), "",
-                                       wide_to_utf8(((gui::IGUIEditBox*) hovered)->getText()),
-                                       type);
-                       return retval;
-               }
-       }
-
-       if (event.EventType == EET_TOUCH_INPUT_EVENT)
-       {
-               SEvent translated;
-               memset(&translated, 0, sizeof(SEvent));
-               translated.EventType   = EET_MOUSE_INPUT_EVENT;
-               gui::IGUIElement* root = Environment->getRootGUIElement();
-
-               if (!root) {
-                       errorstream
-                       << "GUIFormSpecMenu::preprocessEvent unable to get root element"
-                       << std::endl;
-                       return false;
-               }
-               gui::IGUIElement* hovered = root->getElementFromPoint(
-                       core::position2d<s32>(
-                                       event.TouchInput.X,
-                                       event.TouchInput.Y));
-
-               translated.MouseInput.X = event.TouchInput.X;
-               translated.MouseInput.Y = event.TouchInput.Y;
-               translated.MouseInput.Control = false;
-
-               bool dont_send_event = false;
-
-               if (event.TouchInput.touchedCount == 1) {
-                       switch (event.TouchInput.Event) {
-                               case ETIE_PRESSED_DOWN:
-                                       m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
-                                       translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
-                                       translated.MouseInput.ButtonStates = EMBSM_LEFT;
-                                       m_down_pos = m_pointer;
-                                       break;
-                               case ETIE_MOVED:
-                                       m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
-                                       translated.MouseInput.Event = EMIE_MOUSE_MOVED;
-                                       translated.MouseInput.ButtonStates = EMBSM_LEFT;
-                                       break;
-                               case ETIE_LEFT_UP:
-                                       translated.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
-                                       translated.MouseInput.ButtonStates = 0;
-                                       hovered = root->getElementFromPoint(m_down_pos);
-                                       /* we don't have a valid pointer element use last
-                                        * known pointer pos */
-                                       translated.MouseInput.X = m_pointer.X;
-                                       translated.MouseInput.Y = m_pointer.Y;
-
-                                       /* reset down pos */
-                                       m_down_pos = v2s32(0,0);
-                                       break;
-                               default:
-                                       dont_send_event = true;
-                                       //this is not supposed to happen
-                                       errorstream
-                                       << "GUIFormSpecMenu::preprocessEvent unexpected usecase Event="
-                                       << event.TouchInput.Event << std::endl;
-                       }
-               } else if ( (event.TouchInput.touchedCount == 2) &&
-                               (event.TouchInput.Event == ETIE_PRESSED_DOWN) ) {
-                       hovered = root->getElementFromPoint(m_down_pos);
-
-                       translated.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
-                       translated.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT;
-                       translated.MouseInput.X = m_pointer.X;
-                       translated.MouseInput.Y = m_pointer.Y;
-
-                       if (hovered) {
-                               hovered->OnEvent(translated);
-                       }
-
-                       translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
-                       translated.MouseInput.ButtonStates = EMBSM_LEFT;
-
-
-                       if (hovered) {
-                               hovered->OnEvent(translated);
-                       }
-                       dont_send_event = true;
-               }
-               /* ignore unhandled 2 touch events ... accidental moving for example */
-               else if (event.TouchInput.touchedCount == 2) {
-                       dont_send_event = true;
-               }
-               else if (event.TouchInput.touchedCount > 2) {
-                       errorstream
-                       << "GUIFormSpecMenu::preprocessEvent to many multitouch events "
-                       << event.TouchInput.touchedCount << " ignoring them" << std::endl;
-               }
-
-               if (dont_send_event) {
-                       return true;
-               }
-
-               /* check if translated event needs to be preprocessed again */
-               if (preprocessEvent(translated)) {
-                       return true;
-               }
-               if (hovered) {
-                       grab();
-                       bool retval = hovered->OnEvent(translated);
-
-                       if (event.TouchInput.Event == ETIE_LEFT_UP) {
-                               /* reset pointer */
-                               m_pointer = v2s32(0,0);
-                       }
-                       drop();
-                       return retval;
-               }
-       }
-       #endif
-
-       if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
-               /* TODO add a check like:
-               if (event.JoystickEvent != joystick_we_listen_for)
-                       return false;
-               */
-               bool handled = m_joystick->handleEvent(event.JoystickEvent);
-               if (handled) {
-                       if (m_joystick->wasKeyDown(KeyType::ESC)) {
-                               tryClose();
-                       } else if (m_joystick->wasKeyDown(KeyType::JUMP)) {
-                               if (m_allowclose) {
-                                       acceptInput(quit_mode_accept);
-                                       quitMenu();
-                               }
-                       }
-               }
-               return handled;
-       }
-
-       return false;
-}
-
-/******************************************************************************/
-bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event)
-{
-       /* The following code is for capturing double-clicks of the mouse button
-        * and translating the double-click into an EET_KEY_INPUT_EVENT event
-        * -- which closes the form -- under some circumstances.
-        *
-        * There have been many github issues reporting this as a bug even though it
-        * was an intended feature.  For this reason, remapping the double-click as
-        * an ESC must be explicitly set when creating this class via the
-        * /p remap_dbl_click parameter of the constructor.
-        */
-
-       if (!m_remap_dbl_click)
-               return false;
-
-       if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
-               m_doubleclickdetect[0].pos  = m_doubleclickdetect[1].pos;
-               m_doubleclickdetect[0].time = m_doubleclickdetect[1].time;
-
-               m_doubleclickdetect[1].pos  = m_pointer;
-               m_doubleclickdetect[1].time = porting::getTimeMs();
-       }
-       else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
-               u64 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, porting::getTimeMs());
-               if (delta > 400) {
-                       return false;
-               }
-
-               double squaredistance =
-                               m_doubleclickdetect[0].pos
-                               .getDistanceFromSQ(m_doubleclickdetect[1].pos);
-
-               if (squaredistance > (30*30)) {
-                       return false;
-               }
-
-               SEvent* translated = new SEvent();
-               assert(translated != 0);
-               //translate doubleclick to escape
-               memset(translated, 0, sizeof(SEvent));
-               translated->EventType = irr::EET_KEY_INPUT_EVENT;
-               translated->KeyInput.Key         = KEY_ESCAPE;
-               translated->KeyInput.Control     = false;
-               translated->KeyInput.Shift       = false;
-               translated->KeyInput.PressedDown = true;
-               translated->KeyInput.Char        = 0;
-               OnEvent(*translated);
-
-               // no need to send the key up event as we're already deleted
-               // and no one else did notice this event
-               delete translated;
-               return true;
-       }
-
-       return false;
-}
-
-void GUIFormSpecMenu::tryClose()
-{
-       if (m_allowclose) {
-               doPause = false;
-               acceptInput(quit_mode_cancel);
-               quitMenu();
-       } else {
-               m_text_dst->gotText(L"MenuQuit");
-       }
-}
-
-bool GUIFormSpecMenu::OnEvent(const SEvent& event)
-{
-       if (event.EventType==EET_KEY_INPUT_EVENT) {
-               KeyPress kp(event.KeyInput);
-               if (event.KeyInput.PressedDown && (
-                               (kp == EscapeKey) || (kp == CancelKey) ||
-                               ((m_client != NULL) && (kp == getKeySetting("keymap_inventory"))))) {
-                       tryClose();
-                       return true;
-               }
-
-               if (m_client != NULL && event.KeyInput.PressedDown &&
-                               (kp == getKeySetting("keymap_screenshot"))) {
-                       m_client->makeScreenshot();
-               }
-               if (event.KeyInput.PressedDown &&
-                       (event.KeyInput.Key==KEY_RETURN ||
-                        event.KeyInput.Key==KEY_UP ||
-                        event.KeyInput.Key==KEY_DOWN)
-                       ) {
-                       switch (event.KeyInput.Key) {
-                               case KEY_RETURN:
-                                       current_keys_pending.key_enter = true;
-                                       break;
-                               case KEY_UP:
-                                       current_keys_pending.key_up = true;
-                                       break;
-                               case KEY_DOWN:
-                                       current_keys_pending.key_down = true;
-                                       break;
-                               break;
-                               default:
-                                       //can't happen at all!
-                                       FATAL_ERROR("Reached a source line that can't ever been reached");
-                                       break;
-                       }
-                       if (current_keys_pending.key_enter && m_allowclose) {
-                               acceptInput(quit_mode_accept);
-                               quitMenu();
-                       } else {
-                               acceptInput();
-                       }
-                       return true;
-               }
-
-       }
-
-       /* Mouse event other than movement, or crossing the border of inventory
-         field while holding right mouse button
-        */
-       if (event.EventType == EET_MOUSE_INPUT_EVENT &&
-                       (event.MouseInput.Event != EMIE_MOUSE_MOVED ||
-                        (event.MouseInput.Event == EMIE_MOUSE_MOVED &&
-                         event.MouseInput.isRightPressed() &&
-                         getItemAtPos(m_pointer).i != getItemAtPos(m_old_pointer).i))) {
-
-               // Get selected item and hovered/clicked item (s)
-
-               m_old_tooltip_id = -1;
-               updateSelectedItem();
-               ItemSpec s = getItemAtPos(m_pointer);
-
-               Inventory *inv_selected = NULL;
-               Inventory *inv_s = NULL;
-               InventoryList *list_s = NULL;
-
-               if (m_selected_item) {
-                       inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
-                       sanity_check(inv_selected);
-                       sanity_check(inv_selected->getList(m_selected_item->listname) != NULL);
-               }
-
-               u32 s_count = 0;
-
-               if (s.isValid())
-               do { // breakable
-                       inv_s = m_invmgr->getInventory(s.inventoryloc);
-
-                       if (!inv_s) {
-                               errorstream << "InventoryMenu: The selected inventory location "
-                                               << "\"" << s.inventoryloc.dump() << "\" doesn't exist"
-                                               << std::endl;
-                               s.i = -1;  // make it invalid again
-                               break;
-                       }
-
-                       list_s = inv_s->getList(s.listname);
-                       if (list_s == NULL) {
-                               verbosestream << "InventoryMenu: The selected inventory list \""
-                                               << s.listname << "\" does not exist" << std::endl;
-                               s.i = -1;  // make it invalid again
-                               break;
-                       }
-
-                       if ((u32)s.i >= list_s->getSize()) {
-                               infostream << "InventoryMenu: The selected inventory list \""
-                                               << s.listname << "\" is too small (i=" << s.i << ", size="
-                                               << list_s->getSize() << ")" << std::endl;
-                               s.i = -1;  // make it invalid again
-                               break;
-                       }
-
-                       s_count = list_s->getItem(s.i).count;
-               } while(0);
-
-               bool identical = (m_selected_item != NULL) && s.isValid() &&
-                       (inv_selected == inv_s) &&
-                       (m_selected_item->listname == s.listname) &&
-                       (m_selected_item->i == s.i);
-
-               // buttons: 0 = left, 1 = right, 2 = middle
-               // up/down: 0 = down (press), 1 = up (release), 2 = unknown event, -1 movement
-               int button = 0;
-               int updown = 2;
-               if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
-                       { button = 0; updown = 0; }
-               else if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
-                       { button = 1; updown = 0; }
-               else if (event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
-                       { button = 2; updown = 0; }
-               else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
-                       { button = 0; updown = 1; }
-               else if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
-                       { button = 1; updown = 1; }
-               else if (event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
-                       { button = 2; updown = 1; }
-               else if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
-                       { updown = -1;}
-
-               // Set this number to a positive value to generate a move action
-               // from m_selected_item to s.
-               u32 move_amount = 0;
-
-               // Set this number to a positive value to generate a move action
-               // from s to the next inventory ring.
-               u32 shift_move_amount = 0;
-
-               // Set this number to a positive value to generate a drop action
-               // from m_selected_item.
-               u32 drop_amount = 0;
-
-               // Set this number to a positive value to generate a craft action at s.
-               u32 craft_amount = 0;
-
-               if (updown == 0) {
-                       // Some mouse button has been pressed
-
-                       //infostream<<"Mouse button "<<button<<" pressed at p=("
-                       //      <<p.X<<","<<p.Y<<")"<<std::endl;
-
-                       m_selected_dragging = false;
-
-                       if (s.isValid() && s.listname == "craftpreview") {
-                               // Craft preview has been clicked: craft
-                               craft_amount = (button == 2 ? 10 : 1);
-                       } else if (m_selected_item == NULL) {
-                               if (s_count != 0) {
-                                       // Non-empty stack has been clicked: select or shift-move it
-                                       m_selected_item = new ItemSpec(s);
-
-                                       u32 count;
-                                       if (button == 1)  // right
-                                               count = (s_count + 1) / 2;
-                                       else if (button == 2)  // middle
-                                               count = MYMIN(s_count, 10);
-                                       else  // left
-                                               count = s_count;
-
-                                       if (!event.MouseInput.Shift) {
-                                               // no shift: select item
-                                               m_selected_amount = count;
-                                               m_selected_dragging = true;
-                                               m_auto_place = false;
-                                       } else {
-                                               // shift pressed: move item
-                                               if (button != 1)
-                                                       shift_move_amount = count;
-                                               else // count of 1 at left click like after drag & drop
-                                                       shift_move_amount = 1;
-                                       }
-                               }
-                       } else { // m_selected_item != NULL
-                               assert(m_selected_amount >= 1);
-
-                               if (s.isValid()) {
-                                       // Clicked a slot: move
-                                       if (button == 1)  // right
-                                               move_amount = 1;
-                                       else if (button == 2)  // middle
-                                               move_amount = MYMIN(m_selected_amount, 10);
-                                       else  // left
-                                               move_amount = m_selected_amount;
-
-                                       if (identical) {
-                                               if (move_amount >= m_selected_amount)
-                                                       m_selected_amount = 0;
-                                               else
-                                                       m_selected_amount -= move_amount;
-                                               move_amount = 0;
-                                       }
-                               }
-                               else if (!getAbsoluteClippingRect().isPointInside(m_pointer)) {
-                                       // Clicked outside of the window: drop
-                                       if (button == 1)  // right
-                                               drop_amount = 1;
-                                       else if (button == 2)  // middle
-                                               drop_amount = MYMIN(m_selected_amount, 10);
-                                       else  // left
-                                               drop_amount = m_selected_amount;
-                               }
-                       }
-               }
-               else if (updown == 1) {
-                       // Some mouse button has been released
-
-                       //infostream<<"Mouse button "<<button<<" released at p=("
-                       //      <<p.X<<","<<p.Y<<")"<<std::endl;
-
-                       if (m_selected_item != NULL && m_selected_dragging && s.isValid()) {
-                               if (!identical) {
-                                       // Dragged to different slot: move all selected
-                                       move_amount = m_selected_amount;
-                               }
-                       } else if (m_selected_item != NULL && m_selected_dragging &&
-                                       !(getAbsoluteClippingRect().isPointInside(m_pointer))) {
-                               // Dragged outside of window: drop all selected
-                               drop_amount = m_selected_amount;
-                       }
-
-                       m_selected_dragging = false;
-                       // Keep track of whether the mouse button be released
-                       // One click is drag without dropping. Click + release
-                       // + click changes to drop item when moved mode
-                       if (m_selected_item)
-                               m_auto_place = true;
-               } else if (updown == -1) {
-                       // Mouse has been moved and rmb is down and mouse pointer just
-                       // entered a new inventory field (checked in the entry-if, this
-                       // is the only action here that is generated by mouse movement)
-                       if (m_selected_item != NULL && s.isValid()) {
-                               // Move 1 item
-                               // TODO: middle mouse to move 10 items might be handy
-                               if (m_auto_place) {
-                                       // Only move an item if the destination slot is empty
-                                       // or contains the same item type as what is going to be
-                                       // moved
-                                       InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
-                                       InventoryList *list_to = list_s;
-                                       assert(list_from && list_to);
-                                       ItemStack stack_from = list_from->getItem(m_selected_item->i);
-                                       ItemStack stack_to = list_to->getItem(s.i);
-                                       if (stack_to.empty() || stack_to.name == stack_from.name)
-                                               move_amount = 1;
-                               }
-                       }
-               }
-
-               // Possibly send inventory action to server
-               if (move_amount > 0) {
-                       // Send IAction::Move
-
-                       assert(m_selected_item && m_selected_item->isValid());
-                       assert(s.isValid());
-
-                       assert(inv_selected && inv_s);
-                       InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
-                       InventoryList *list_to = list_s;
-                       assert(list_from && list_to);
-                       ItemStack stack_from = list_from->getItem(m_selected_item->i);
-                       ItemStack stack_to = list_to->getItem(s.i);
-
-                       // Check how many items can be moved
-                       move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
-                       ItemStack leftover = stack_to.addItem(stack_from, m_client->idef());
-                       // If source stack cannot be added to destination stack at all,
-                       // they are swapped
-                       if ((leftover.count == stack_from.count) &&
-                                       (leftover.name == stack_from.name)) {
-                               m_selected_amount = stack_to.count;
-                               // In case the server doesn't directly swap them but instead
-                               // moves stack_to somewhere else, set this
-                               m_selected_content_guess = stack_to;
-                               m_selected_content_guess_inventory = s.inventoryloc;
-                       }
-                       // Source stack goes fully into destination stack
-                       else if (leftover.empty()) {
-                               m_selected_amount -= move_amount;
-                               m_selected_content_guess = ItemStack(); // Clear
-                       }
-                       // Source stack goes partly into destination stack
-                       else {
-                               move_amount -= leftover.count;
-                               m_selected_amount -= move_amount;
-                               m_selected_content_guess = ItemStack(); // Clear
-                       }
-
-                       infostream << "Handing IAction::Move to manager" << std::endl;
-                       IMoveAction *a = new IMoveAction();
-                       a->count = move_amount;
-                       a->from_inv = m_selected_item->inventoryloc;
-                       a->from_list = m_selected_item->listname;
-                       a->from_i = m_selected_item->i;
-                       a->to_inv = s.inventoryloc;
-                       a->to_list = s.listname;
-                       a->to_i = s.i;
-                       m_invmgr->inventoryAction(a);
-               } else if (shift_move_amount > 0) {
-                       u32 mis = m_inventory_rings.size();
-                       u32 i = 0;
-                       for (; i < mis; i++) {
-                               const ListRingSpec &sp = m_inventory_rings[i];
-                               if (sp.inventoryloc == s.inventoryloc
-                                               && sp.listname == s.listname)
-                                       break;
-                       }
-                       do {
-                               if (i >= mis) // if not found
-                                       break;
-                               u32 to_inv_ind = (i + 1) % mis;
-                               const ListRingSpec &to_inv_sp = m_inventory_rings[to_inv_ind];
-                               InventoryList *list_from = list_s;
-                               if (!s.isValid())
-                                       break;
-                               Inventory *inv_to = m_invmgr->getInventory(to_inv_sp.inventoryloc);
-                               if (!inv_to)
-                                       break;
-                               InventoryList *list_to = inv_to->getList(to_inv_sp.listname);
-                               if (!list_to)
-                                       break;
-                               ItemStack stack_from = list_from->getItem(s.i);
-                               assert(shift_move_amount <= stack_from.count);
-                               if (m_client->getProtoVersion() >= 25) {
-                                       infostream << "Handing IAction::Move to manager" << std::endl;
-                                       IMoveAction *a = new IMoveAction();
-                                       a->count = shift_move_amount;
-                                       a->from_inv = s.inventoryloc;
-                                       a->from_list = s.listname;
-                                       a->from_i = s.i;
-                                       a->to_inv = to_inv_sp.inventoryloc;
-                                       a->to_list = to_inv_sp.listname;
-                                       a->move_somewhere = true;
-                                       m_invmgr->inventoryAction(a);
-                               } else {
-                                       // find a place (or more than one) to add the new item
-                                       u32 ilt_size = list_to->getSize();
-                                       ItemStack leftover;
-                                       for (u32 slot_to = 0; slot_to < ilt_size
-                                                       && shift_move_amount > 0; slot_to++) {
-                                               list_to->itemFits(slot_to, stack_from, &leftover);
-                                               if (leftover.count < stack_from.count) {
-                                                       infostream << "Handing IAction::Move to manager" << std::endl;
-                                                       IMoveAction *a = new IMoveAction();
-                                                       a->count = MYMIN(shift_move_amount,
-                                                               (u32) (stack_from.count - leftover.count));
-                                                       shift_move_amount -= a->count;
-                                                       a->from_inv = s.inventoryloc;
-                                                       a->from_list = s.listname;
-                                                       a->from_i = s.i;
-                                                       a->to_inv = to_inv_sp.inventoryloc;
-                                                       a->to_list = to_inv_sp.listname;
-                                                       a->to_i = slot_to;
-                                                       m_invmgr->inventoryAction(a);
-                                                       stack_from = leftover;
-                                               }
-                                       }
-                               }
-                       } while (0);
-               } else if (drop_amount > 0) {
-                       m_selected_content_guess = ItemStack(); // Clear
-
-                       // Send IAction::Drop
-
-                       assert(m_selected_item && m_selected_item->isValid());
-                       assert(inv_selected);
-                       InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
-                       assert(list_from);
-                       ItemStack stack_from = list_from->getItem(m_selected_item->i);
-
-                       // Check how many items can be dropped
-                       drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
-                       assert(drop_amount > 0 && drop_amount <= m_selected_amount);
-                       m_selected_amount -= drop_amount;
-
-                       infostream << "Handing IAction::Drop to manager" << std::endl;
-                       IDropAction *a = new IDropAction();
-                       a->count = drop_amount;
-                       a->from_inv = m_selected_item->inventoryloc;
-                       a->from_list = m_selected_item->listname;
-                       a->from_i = m_selected_item->i;
-                       m_invmgr->inventoryAction(a);
-               } else if (craft_amount > 0) {
-                       m_selected_content_guess = ItemStack(); // Clear
-
-                       // Send IAction::Craft
-
-                       assert(s.isValid());
-                       assert(inv_s);
-
-                       infostream << "Handing IAction::Craft to manager" << std::endl;
-                       ICraftAction *a = new ICraftAction();
-                       a->count = craft_amount;
-                       a->craft_inv = s.inventoryloc;
-                       m_invmgr->inventoryAction(a);
-               }
-
-               // If m_selected_amount has been decreased to zero, deselect
-               if (m_selected_amount == 0) {
-                       delete m_selected_item;
-                       m_selected_item = NULL;
-                       m_selected_amount = 0;
-                       m_selected_dragging = false;
-                       m_selected_content_guess = ItemStack();
-               }
-               m_old_pointer = m_pointer;
-       }
-       if (event.EventType == EET_GUI_EVENT) {
-
-               if (event.GUIEvent.EventType == gui::EGET_TAB_CHANGED
-                               && isVisible()) {
-                       // find the element that was clicked
-                       for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
-                               if ((s.ftype == f_TabHeader) &&
-                                               (s.fid == event.GUIEvent.Caller->getID())) {
-                                       s.send = true;
-                                       acceptInput();
-                                       s.send = false;
-                                       return true;
-                               }
-                       }
-               }
-               if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST
-                               && isVisible()) {
-                       if (!canTakeFocus(event.GUIEvent.Element)) {
-                               infostream<<"GUIFormSpecMenu: Not allowing focus change."
-                                               <<std::endl;
-                               // Returning true disables focus change
-                               return true;
-                       }
-               }
-               if ((event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) ||
-                               (event.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED) ||
-                               (event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED) ||
-                               (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED)) {
-                       unsigned int btn_id = event.GUIEvent.Caller->getID();
-
-                       if (btn_id == 257) {
-                               if (m_allowclose) {
-                                       acceptInput(quit_mode_accept);
-                                       quitMenu();
-                               } else {
-                                       acceptInput();
-                                       m_text_dst->gotText(L"ExitButton");
-                               }
-                               // quitMenu deallocates menu
-                               return true;
-                       }
-
-                       // find the element that was clicked
-                       for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
-                               // if its a button, set the send field so
-                               // lua knows which button was pressed
-                               if ((s.ftype == f_Button || s.ftype == f_CheckBox) &&
-                                               s.fid == event.GUIEvent.Caller->getID()) {
-                                       s.send = true;
-                                       if (s.is_exit) {
-                                               if (m_allowclose) {
-                                                       acceptInput(quit_mode_accept);
-                                                       quitMenu();
-                                               } else {
-                                                       m_text_dst->gotText(L"ExitButton");
-                                               }
-                                               return true;
-                                       }
-
-                                       acceptInput(quit_mode_no);
-                                       s.send = false;
-                                       return true;
-
-                               } else if ((s.ftype == f_DropDown) &&
-                                               (s.fid == event.GUIEvent.Caller->getID())) {
-                                       // only send the changed dropdown
-                                       for (GUIFormSpecMenu::FieldSpec &s2 : m_fields) {
-                                               if (s2.ftype == f_DropDown) {
-                                                       s2.send = false;
-                                               }
-                                       }
-                                       s.send = true;
-                                       acceptInput(quit_mode_no);
-
-                                       // revert configuration to make sure dropdowns are sent on
-                                       // regular button click
-                                       for (GUIFormSpecMenu::FieldSpec &s2 : m_fields) {
-                                               if (s2.ftype == f_DropDown) {
-                                                       s2.send = true;
-                                               }
-                                       }
-                                       return true;
-                               } else if ((s.ftype == f_ScrollBar) &&
-                                               (s.fid == event.GUIEvent.Caller->getID())) {
-                                       s.fdefault = L"Changed";
-                                       acceptInput(quit_mode_no);
-                                       s.fdefault = L"";
-                               }
-                       }
-               }
-
-               if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
-                       if (event.GUIEvent.Caller->getID() > 257) {
-                               bool close_on_enter = true;
-                               for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
-                                       if (s.ftype == f_Unknown &&
-                                                       s.fid == event.GUIEvent.Caller->getID()) {
-                                               current_field_enter_pending = s.fname;
-                                               std::unordered_map<std::string, bool>::const_iterator it =
-                                                       field_close_on_enter.find(s.fname);
-                                               if (it != field_close_on_enter.end())
-                                                       close_on_enter = (*it).second;
-
-                                               break;
-                                       }
-                               }
-
-                               if (m_allowclose && close_on_enter) {
-                                       current_keys_pending.key_enter = true;
-                                       acceptInput(quit_mode_accept);
-                                       quitMenu();
-                               } else {
-                                       current_keys_pending.key_enter = true;
-                                       acceptInput();
-                               }
-                               // quitMenu deallocates menu
-                               return true;
-                       }
-               }
-
-               if (event.GUIEvent.EventType == gui::EGET_TABLE_CHANGED) {
-                       int current_id = event.GUIEvent.Caller->getID();
-                       if (current_id > 257) {
-                               // find the element that was clicked
-                               for (GUIFormSpecMenu::FieldSpec &s : m_fields) {
-                                       // if it's a table, set the send field
-                                       // so lua knows which table was changed
-                                       if ((s.ftype == f_Table) && (s.fid == current_id)) {
-                                               s.send = true;
-                                               acceptInput();
-                                               s.send=false;
-                                       }
-                               }
-                               return true;
-                       }
-               }
-       }
-
-       return Parent ? Parent->OnEvent(event) : false;
-}
-
-/**
- * get name of element by element id
- * @param id of element
- * @return name string or empty string
- */
-std::string GUIFormSpecMenu::getNameByID(s32 id)
-{
-       for (FieldSpec &spec : m_fields) {
-               if (spec.fid == id) {
-                       return spec.fname;
-               }
-       }
-       return "";
-}
-
-/**
- * get label of element by id
- * @param id of element
- * @return label string or empty string
- */
-std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
-{
-       for (FieldSpec &spec : m_fields) {
-               if (spec.fid == id) {
-                       return spec.flabel;
-               }
-       }
-       return L"";
-}
diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h
deleted file mode 100644 (file)
index 071efb3..0000000
+++ /dev/null
@@ -1,565 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include <utility>
-#include <stack>
-
-#include "irrlichttypes_extrabloated.h"
-#include "inventorymanager.h"
-#include "modalMenu.h"
-#include "guiTable.h"
-#include "network/networkprotocol.h"
-#include "client/joystick_controller.h"
-#include "util/string.h"
-#include "util/enriched_string.h"
-
-class InventoryManager;
-class ISimpleTextureSource;
-class Client;
-
-typedef enum {
-       f_Button,
-       f_Table,
-       f_TabHeader,
-       f_CheckBox,
-       f_DropDown,
-       f_ScrollBar,
-       f_Unknown
-} FormspecFieldType;
-
-typedef enum {
-       quit_mode_no,
-       quit_mode_accept,
-       quit_mode_cancel
-} FormspecQuitMode;
-
-struct TextDest
-{
-       virtual ~TextDest() = default;
-
-       // This is deprecated I guess? -celeron55
-       virtual void gotText(const std::wstring &text) {}
-       virtual void gotText(const StringMap &fields) = 0;
-
-       std::string m_formname;
-};
-
-class IFormSource
-{
-public:
-       virtual ~IFormSource() = default;
-       virtual const std::string &getForm() const = 0;
-       // Fill in variables in field text
-       virtual std::string resolveText(const std::string &str) { return str; }
-};
-
-class GUIFormSpecMenu : public GUIModalMenu
-{
-       struct ItemSpec
-       {
-               ItemSpec() = default;
-
-               ItemSpec(const InventoryLocation &a_inventoryloc,
-                               const std::string &a_listname,
-                               s32 a_i) :
-                       inventoryloc(a_inventoryloc),
-                       listname(a_listname),
-                       i(a_i)
-               {
-               }
-
-               bool isValid() const { return i != -1; }
-
-               InventoryLocation inventoryloc;
-               std::string listname;
-               s32 i = -1;
-       };
-
-       struct ListDrawSpec
-       {
-               ListDrawSpec() = default;
-
-               ListDrawSpec(const InventoryLocation &a_inventoryloc,
-                               const std::string &a_listname,
-                               v2s32 a_pos, v2s32 a_geom, s32 a_start_item_i):
-                       inventoryloc(a_inventoryloc),
-                       listname(a_listname),
-                       pos(a_pos),
-                       geom(a_geom),
-                       start_item_i(a_start_item_i)
-               {
-               }
-
-               InventoryLocation inventoryloc;
-               std::string listname;
-               v2s32 pos;
-               v2s32 geom;
-               s32 start_item_i;
-       };
-
-       struct ListRingSpec
-       {
-               ListRingSpec() = default;
-
-               ListRingSpec(const InventoryLocation &a_inventoryloc,
-                               const std::string &a_listname):
-                       inventoryloc(a_inventoryloc),
-                       listname(a_listname)
-               {
-               }
-
-               InventoryLocation inventoryloc;
-               std::string listname;
-       };
-
-       struct ImageDrawSpec
-       {
-               ImageDrawSpec():
-                       parent_button(NULL),
-                       clip(false)
-               {
-               }
-
-               ImageDrawSpec(const std::string &a_name,
-                               const std::string &a_item_name,
-                               gui::IGUIButton *a_parent_button,
-                               const v2s32 &a_pos, const v2s32 &a_geom):
-                       name(a_name),
-                       item_name(a_item_name),
-                       parent_button(a_parent_button),
-                       pos(a_pos),
-                       geom(a_geom),
-                       scale(true),
-                       clip(false)
-               {
-               }
-
-               ImageDrawSpec(const std::string &a_name,
-                               const std::string &a_item_name,
-                               const v2s32 &a_pos, const v2s32 &a_geom):
-                       name(a_name),
-                       item_name(a_item_name),
-                       parent_button(NULL),
-                       pos(a_pos),
-                       geom(a_geom),
-                       scale(true),
-                       clip(false)
-               {
-               }
-
-               ImageDrawSpec(const std::string &a_name,
-                               const v2s32 &a_pos, const v2s32 &a_geom, bool clip=false):
-                       name(a_name),
-                       parent_button(NULL),
-                       pos(a_pos),
-                       geom(a_geom),
-                       scale(true),
-                       clip(clip)
-               {
-               }
-
-               ImageDrawSpec(const std::string &a_name,
-                               const v2s32 &a_pos):
-                       name(a_name),
-                       parent_button(NULL),
-                       pos(a_pos),
-                       scale(false),
-                       clip(false)
-               {
-               }
-
-               std::string name;
-               std::string item_name;
-               gui::IGUIButton *parent_button;
-               v2s32 pos;
-               v2s32 geom;
-               bool scale;
-               bool clip;
-       };
-
-       struct FieldSpec
-       {
-               FieldSpec() = default;
-
-               FieldSpec(const std::string &name, const std::wstring &label,
-                               const std::wstring &default_text, int id) :
-                       fname(name),
-                       flabel(label),
-                       fdefault(unescape_enriched(translate_string(default_text))),
-                       fid(id),
-                       send(false),
-                       ftype(f_Unknown),
-                       is_exit(false)
-               {
-               }
-
-               std::string fname;
-               std::wstring flabel;
-               std::wstring fdefault;
-               int fid;
-               bool send;
-               FormspecFieldType ftype;
-               bool is_exit;
-               core::rect<s32> rect;
-       };
-
-       struct BoxDrawSpec
-       {
-               BoxDrawSpec(v2s32 a_pos, v2s32 a_geom,irr::video::SColor a_color):
-                       pos(a_pos),
-                       geom(a_geom),
-                       color(a_color)
-               {
-               }
-               v2s32 pos;
-               v2s32 geom;
-               irr::video::SColor color;
-       };
-
-       struct TooltipSpec
-       {
-               TooltipSpec() = default;
-               TooltipSpec(const std::wstring &a_tooltip, irr::video::SColor a_bgcolor,
-                               irr::video::SColor a_color):
-                       tooltip(translate_string(a_tooltip)),
-                       bgcolor(a_bgcolor),
-                       color(a_color)
-               {
-               }
-
-               std::wstring tooltip;
-               irr::video::SColor bgcolor;
-               irr::video::SColor color;
-       };
-
-       struct StaticTextSpec
-       {
-               StaticTextSpec():
-                       parent_button(NULL)
-               {
-               }
-
-               StaticTextSpec(const std::wstring &a_text,
-                               const core::rect<s32> &a_rect):
-                       text(a_text),
-                       rect(a_rect),
-                       parent_button(NULL)
-               {
-               }
-
-               StaticTextSpec(const std::wstring &a_text,
-                               const core::rect<s32> &a_rect,
-                               gui::IGUIButton *a_parent_button):
-                       text(a_text),
-                       rect(a_rect),
-                       parent_button(a_parent_button)
-               {
-               }
-
-               std::wstring text;
-               core::rect<s32> rect;
-               gui::IGUIButton *parent_button;
-       };
-
-public:
-       GUIFormSpecMenu(JoystickController *joystick,
-                       gui::IGUIElement* parent, s32 id,
-                       IMenuManager *menumgr,
-                       Client *client,
-                       ISimpleTextureSource *tsrc,
-                       IFormSource* fs_src,
-                       TextDest* txt_dst,
-                       bool remap_dbl_click = true);
-
-       ~GUIFormSpecMenu();
-
-       void setFormSpec(const std::string &formspec_string,
-                       const InventoryLocation &current_inventory_location)
-       {
-               m_formspec_string = formspec_string;
-               m_current_inventory_location = current_inventory_location;
-               regenerateGui(m_screensize_old);
-       }
-
-       // form_src is deleted by this GUIFormSpecMenu
-       void setFormSource(IFormSource *form_src)
-       {
-               delete m_form_src;
-               m_form_src = form_src;
-       }
-
-       // text_dst is deleted by this GUIFormSpecMenu
-       void setTextDest(TextDest *text_dst)
-       {
-               delete m_text_dst;
-               m_text_dst = text_dst;
-       }
-
-       void allowClose(bool value)
-       {
-               m_allowclose = value;
-       }
-
-       void lockSize(bool lock,v2u32 basescreensize=v2u32(0,0))
-       {
-               m_lock = lock;
-               m_lockscreensize = basescreensize;
-       }
-
-       void removeChildren();
-       void setInitialFocus();
-
-       void setFocus(const std::string &elementname)
-       {
-               m_focused_element = elementname;
-       }
-
-       /*
-               Remove and re-add (or reposition) stuff
-       */
-       void regenerateGui(v2u32 screensize);
-
-       ItemSpec getItemAtPos(v2s32 p) const;
-       void drawList(const ListDrawSpec &s, int phase, bool &item_hovered);
-       void drawSelectedItem();
-       void drawMenu();
-       void updateSelectedItem();
-       ItemStack verifySelectedItem();
-
-       void acceptInput(FormspecQuitMode quitmode);
-       bool preprocessEvent(const SEvent& event);
-       bool OnEvent(const SEvent& event);
-       bool doPause;
-       bool pausesGame() { return doPause; }
-
-       GUITable* getTable(const std::string &tablename);
-       std::vector<std::string>* getDropDownValues(const std::string &name);
-
-#ifdef __ANDROID__
-       bool getAndroidUIInput();
-#endif
-
-protected:
-       v2s32 getBasePos() const
-       {
-                       return padding + offset + AbsoluteRect.UpperLeftCorner;
-       }
-
-       v2s32 padding;
-       v2s32 spacing;
-       v2s32 imgsize;
-       v2s32 offset;
-       v2s32 pos_offset;
-       std::stack<v2s32> container_stack;
-
-       InventoryManager *m_invmgr;
-       ISimpleTextureSource *m_tsrc;
-       Client *m_client;
-
-       std::string m_formspec_string;
-       InventoryLocation m_current_inventory_location;
-
-       std::vector<ListDrawSpec> m_inventorylists;
-       std::vector<ListRingSpec> m_inventory_rings;
-       std::vector<ImageDrawSpec> m_backgrounds;
-       std::vector<ImageDrawSpec> m_images;
-       std::vector<ImageDrawSpec> m_itemimages;
-       std::vector<BoxDrawSpec> m_boxes;
-       std::unordered_map<std::string, bool> field_close_on_enter;
-       std::vector<FieldSpec> m_fields;
-       std::vector<StaticTextSpec> m_static_texts;
-       std::vector<std::pair<FieldSpec,GUITable*> > m_tables;
-       std::vector<std::pair<FieldSpec,gui::IGUICheckBox*> > m_checkboxes;
-       std::map<std::string, TooltipSpec> m_tooltips;
-       std::vector<std::pair<FieldSpec,gui::IGUIScrollBar*> > m_scrollbars;
-       std::vector<std::pair<FieldSpec, std::vector<std::string> > > m_dropdowns;
-
-       ItemSpec *m_selected_item = nullptr;
-       u32 m_selected_amount = 0;
-       bool m_selected_dragging = false;
-
-       // WARNING: BLACK MAGIC
-       // Used to guess and keep up with some special things the server can do.
-       // If name is "", no guess exists.
-       ItemStack m_selected_content_guess;
-       InventoryLocation m_selected_content_guess_inventory;
-
-       v2s32 m_pointer;
-       v2s32 m_old_pointer;  // Mouse position after previous mouse event
-       gui::IGUIStaticText *m_tooltip_element = nullptr;
-
-       u64 m_tooltip_show_delay;
-       bool m_tooltip_append_itemname;
-       u64 m_hovered_time = 0;
-       s32 m_old_tooltip_id = -1;
-
-       bool m_auto_place = false;
-
-       bool m_allowclose = true;
-       bool m_lock = false;
-       v2u32 m_lockscreensize;
-
-       bool m_bgfullscreen;
-       bool m_slotborder;
-       video::SColor m_bgcolor;
-       video::SColor m_fullscreen_bgcolor;
-       video::SColor m_slotbg_n;
-       video::SColor m_slotbg_h;
-       video::SColor m_slotbordercolor;
-       video::SColor m_default_tooltip_bgcolor;
-       video::SColor m_default_tooltip_color;
-
-private:
-       IFormSource        *m_form_src;
-       TextDest           *m_text_dst;
-       u32                 m_formspec_version = 0;
-       std::string         m_focused_element = "";
-       JoystickController *m_joystick;
-
-       typedef struct {
-               bool explicit_size;
-               v2f invsize;
-               v2s32 size;
-               v2f32 offset;
-               v2f32 anchor;
-               core::rect<s32> rect;
-               v2s32 basepos;
-               v2u32 screensize;
-               std::string focused_fieldname;
-               GUITable::TableOptions table_options;
-               GUITable::TableColumns table_columns;
-               // used to restore table selection/scroll/treeview state
-               std::unordered_map<std::string, GUITable::DynamicData> table_dyndata;
-       } parserData;
-
-       typedef struct {
-               bool key_up;
-               bool key_down;
-               bool key_enter;
-               bool key_escape;
-       } fs_key_pendig;
-
-       fs_key_pendig current_keys_pending;
-       std::string current_field_enter_pending = "";
-
-       void parseElement(parserData* data, const std::string &element);
-
-       void parseSize(parserData* data, const std::string &element);
-       void parseContainer(parserData* data, const std::string &element);
-       void parseContainerEnd(parserData* data);
-       void parseList(parserData* data, const std::string &element);
-       void parseListRing(parserData* data, const std::string &element);
-       void parseCheckbox(parserData* data, const std::string &element);
-       void parseImage(parserData* data, const std::string &element);
-       void parseItemImage(parserData* data, const std::string &element);
-       void parseButton(parserData* data, const std::string &element,
-                       const std::string &typ);
-       void parseBackground(parserData* data, const std::string &element);
-       void parseTableOptions(parserData* data, const std::string &element);
-       void parseTableColumns(parserData* data, const std::string &element);
-       void parseTable(parserData* data, const std::string &element);
-       void parseTextList(parserData* data, const std::string &element);
-       void parseDropDown(parserData* data, const std::string &element);
-       void parseFieldCloseOnEnter(parserData *data, const std::string &element);
-       void parsePwdField(parserData* data, const std::string &element);
-       void parseField(parserData* data, const std::string &element, const std::string &type);
-       void parseSimpleField(parserData* data,std::vector<std::string> &parts);
-       void parseTextArea(parserData* data,std::vector<std::string>& parts,
-                       const std::string &type);
-       void parseLabel(parserData* data, const std::string &element);
-       void parseVertLabel(parserData* data, const std::string &element);
-       void parseImageButton(parserData* data, const std::string &element,
-                       const std::string &type);
-       void parseItemImageButton(parserData* data, const std::string &element);
-       void parseTabHeader(parserData* data, const std::string &element);
-       void parseBox(parserData* data, const std::string &element);
-       void parseBackgroundColor(parserData* data, const std::string &element);
-       void parseListColors(parserData* data, const std::string &element);
-       void parseTooltip(parserData* data, const std::string &element);
-       bool parseVersionDirect(const std::string &data);
-       bool parseSizeDirect(parserData* data, const std::string &element);
-       void parseScrollBar(parserData* data, const std::string &element);
-       bool parsePositionDirect(parserData *data, const std::string &element);
-       void parsePosition(parserData *data, const std::string &element);
-       bool parseAnchorDirect(parserData *data, const std::string &element);
-       void parseAnchor(parserData *data, const std::string &element);
-
-       void tryClose();
-
-       void showTooltip(const std::wstring &text, const irr::video::SColor &color,
-               const irr::video::SColor &bgcolor);
-
-       /**
-        * check if event is part of a double click
-        * @param event event to evaluate
-        * @return true/false if a doubleclick was detected
-        */
-       bool DoubleClickDetection(const SEvent event);
-
-       struct clickpos
-       {
-               v2s32 pos;
-               s64 time;
-       };
-       clickpos m_doubleclickdetect[2];
-
-       int m_btn_height;
-       gui::IGUIFont *m_font = nullptr;
-
-       std::wstring getLabelByID(s32 id);
-       std::string getNameByID(s32 id);
-#ifdef __ANDROID__
-       v2s32 m_down_pos;
-       std::string m_JavaDialogFieldName;
-#endif
-
-       /* If true, remap a double-click (or double-tap) action to ESC. This is so
-        * that, for example, Android users can double-tap to close a formspec.
-       *
-        * This value can (currently) only be set by the class constructor
-        * and the default value for the setting is true.
-        */
-       bool m_remap_dbl_click;
-
-};
-
-class FormspecFormSource: public IFormSource
-{
-public:
-       FormspecFormSource(const std::string &formspec):
-               m_formspec(formspec)
-       {
-       }
-
-       ~FormspecFormSource() = default;
-
-       void setForm(const std::string &formspec)
-       {
-               m_formspec = FORMSPEC_VERSION_STRING + formspec;
-       }
-
-       const std::string &getForm() const
-       {
-               return m_formspec;
-       }
-
-       std::string m_formspec;
-};
diff --git a/src/guiKeyChangeMenu.cpp b/src/guiKeyChangeMenu.cpp
deleted file mode 100644 (file)
index 53677a5..0000000
+++ /dev/null
@@ -1,436 +0,0 @@
-/*
- Minetest
- Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
- Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
- Copyright (C) 2013 teddydestodes <derkomtur@schattengang.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser 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 <string>
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-#include "settings.h"
-#include <algorithm>
-
-#include "mainmenumanager.h"  // for g_gamecallback
-
-#define KMaxButtonPerColumns 12
-
-extern MainGameCallback *g_gamecallback;
-
-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_NOCLIP_BUTTON,
-       GUI_ID_KEY_CINEMATIC_BUTTON,
-       GUI_ID_KEY_CHAT_BUTTON,
-       GUI_ID_KEY_CMD_BUTTON,
-       GUI_ID_KEY_CMD_LOCAL_BUTTON,
-       GUI_ID_KEY_CONSOLE_BUTTON,
-       GUI_ID_KEY_SNEAK_BUTTON,
-       GUI_ID_KEY_DROP_BUTTON,
-       GUI_ID_KEY_INVENTORY_BUTTON,
-       GUI_ID_KEY_HOTBAR_PREV_BUTTON,
-       GUI_ID_KEY_HOTBAR_NEXT_BUTTON,
-       GUI_ID_KEY_MUTE_BUTTON,
-       GUI_ID_KEY_DEC_VOLUME_BUTTON,
-       GUI_ID_KEY_INC_VOLUME_BUTTON,
-       GUI_ID_KEY_RANGE_BUTTON,
-       GUI_ID_KEY_ZOOM_BUTTON,
-       GUI_ID_KEY_CAMERA_BUTTON,
-       GUI_ID_KEY_MINIMAP_BUTTON,
-       GUI_ID_KEY_SCREENSHOT_BUTTON,
-       GUI_ID_KEY_CHATLOG_BUTTON,
-       GUI_ID_KEY_HUD_BUTTON,
-       GUI_ID_KEY_FOG_BUTTON,
-       GUI_ID_KEY_DEC_RANGE_BUTTON,
-       GUI_ID_KEY_INC_RANGE_BUTTON,
-       GUI_ID_KEY_AUTOFWD_BUTTON,
-       // other
-       GUI_ID_CB_AUX1_DESCENDS,
-       GUI_ID_CB_DOUBLETAP_JUMP,
-};
-
-GUIKeyChangeMenu::GUIKeyChangeMenu(gui::IGUIEnvironment* env,
-                               gui::IGUIElement* parent, s32 id, IMenuManager *menumgr) :
-GUIModalMenu(env, parent, id, menumgr)
-{
-       init_keys();
-       for (key_setting *ks : key_settings)
-               key_used.push_back(ks->key);
-}
-
-GUIKeyChangeMenu::~GUIKeyChangeMenu()
-{
-       removeChildren();
-
-       for (key_setting *ks : key_settings) {
-               delete[] ks->button_name;
-               delete ks;
-       }
-       key_settings.clear();
-}
-
-void GUIKeyChangeMenu::removeChildren()
-{
-       const core::list<gui::IGUIElement*> &children = getChildren();
-       core::list<gui::IGUIElement*> children_copy;
-       for (gui::IGUIElement*i : children) {
-               children_copy.push_back(i);
-       }
-
-       for (gui::IGUIElement *i : children_copy) {
-               i->remove();
-       }
-}
-
-void GUIKeyChangeMenu::regenerateGui(v2u32 screensize)
-{
-       removeChildren();
-       v2s32 size(745, 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, 600, 40);
-               rect += topleft + v2s32(25, 3);
-               //gui::IGUIStaticText *t =
-               const wchar_t *text = wgettext("Keybindings. (If this menu screws up, remove stuff from minetest.conf)");
-               Environment->addStaticText(text,
-                                                                  rect, false, true, this, -1);
-               delete[] text;
-               //t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
-       }
-
-       // Build buttons
-
-       v2s32 offset(25, 60);
-
-       for(size_t i = 0; i < key_settings.size(); i++)
-       {
-               key_setting *k = key_settings.at(i);
-               {
-                       core::rect < s32 > rect(0, 0, 150, 20);
-                       rect += topleft + v2s32(offset.X, offset.Y);
-                       Environment->addStaticText(k->button_name, rect, false, true, this, -1);
-               }
-
-               {
-                       core::rect < s32 > rect(0, 0, 100, 30);
-                       rect += topleft + v2s32(offset.X + 120, offset.Y - 5);
-                       const wchar_t *text = wgettext(k->key.name());
-                       k->button = Environment->addButton(rect, this, k->id, text);
-                       delete[] text;
-               }
-               if ((i + 1) % KMaxButtonPerColumns == 0) {
-                       offset.X += 230;
-                       offset.Y = 60;
-               } else {
-                       offset += v2s32(0, 25);
-               }
-       }
-
-       {
-               s32 option_x = offset.X;
-               s32 option_y = offset.Y + 5;
-               u32 option_w = 180;
-               {
-                       core::rect<s32> rect(0, 0, option_w, 30);
-                       rect += topleft + v2s32(option_x, option_y);
-                       const wchar_t *text = wgettext("\"Special\" = climb down");
-                       Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this,
-                                       GUI_ID_CB_AUX1_DESCENDS, text);
-                       delete[] text;
-               }
-               offset += v2s32(0, 25);
-       }
-
-       {
-               s32 option_x = offset.X;
-               s32 option_y = offset.Y + 5;
-               u32 option_w = 280;
-               {
-                       core::rect<s32> rect(0, 0, option_w, 30);
-                       rect += topleft + v2s32(option_x, option_y);
-                       const wchar_t *text = wgettext("Double tap \"jump\" to toggle fly");
-                       Environment->addCheckBox(g_settings->getBool("doubletap_jump"), rect, this,
-                                       GUI_ID_CB_DOUBLETAP_JUMP, text);
-                       delete[] text;
-               }
-               offset += v2s32(0, 25);
-       }
-
-       {
-               core::rect < s32 > rect(0, 0, 100, 30);
-               rect += topleft + v2s32(size.X / 2 - 105, size.Y - 40);
-               const wchar_t *text =  wgettext("Save");
-               Environment->addButton(rect, this, GUI_ID_BACK_BUTTON,
-                                text);
-               delete[] text;
-       }
-       {
-               core::rect < s32 > rect(0, 0, 100, 30);
-               rect += topleft + v2s32(size.X / 2 + 5, size.Y - 40);
-               const wchar_t *text = wgettext("Cancel");
-               Environment->addButton(rect, this, GUI_ID_ABORT_BUTTON,
-                               text);
-               delete[] text;
-       }
-}
-
-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, 745, 620);
-               rect += AbsoluteRect.UpperLeftCorner;
-               driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect);
-       }
-
-       gui::IGUIElement::draw();
-}
-
-bool GUIKeyChangeMenu::acceptInput()
-{
-       for (key_setting *k : key_settings) {
-               g_settings->set(k->setting_name, k->key.sym());
-       }
-
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_CB_AUX1_DESCENDS);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       g_settings->setBool("aux1_descends", ((gui::IGUICheckBox*)e)->isChecked());
-       }
-       {
-               gui::IGUIElement *e = getElementFromId(GUI_ID_CB_DOUBLETAP_JUMP);
-               if(e != NULL && e->getType() == gui::EGUIET_CHECK_BOX)
-                       g_settings->setBool("doubletap_jump", ((gui::IGUICheckBox*)e)->isChecked());
-       }
-
-       clearKeyCache();
-
-       g_gamecallback->signalKeyConfigChange();
-
-       return true;
-}
-
-bool GUIKeyChangeMenu::resetMenu()
-{
-       if (activeKey >= 0)
-       {
-               for (key_setting *k : key_settings) {
-                       if (k->id == activeKey) {
-                               const wchar_t *text = wgettext(k->key.name());
-                               k->button->setText(text);
-                               delete[] text;
-                               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) {
-
-               bool prefer_character = shift_down;
-               KeyPress kp(event.KeyInput, prefer_character);
-
-               bool shift_went_down = false;
-               if(!shift_down &&
-                               (event.KeyInput.Key == irr::KEY_SHIFT ||
-                               event.KeyInput.Key == irr::KEY_LSHIFT ||
-                               event.KeyInput.Key == irr::KEY_RSHIFT))
-                       shift_went_down = true;
-
-               // Remove Key already in use message
-               if(this->key_used_text)
-               {
-                       this->key_used_text->remove();
-                       this->key_used_text = NULL;
-               }
-               // Display Key already in use message
-               if (std::find(this->key_used.begin(), this->key_used.end(), kp) != this->key_used.end())
-               {
-                       core::rect < s32 > rect(0, 0, 600, 40);
-                       rect += v2s32(0, 0) + v2s32(25, 30);
-                       const wchar_t *text = wgettext("Key already in use");
-                       this->key_used_text = Environment->addStaticText(text,
-                                       rect, false, true, this, -1);
-                       delete[] text;
-                       //infostream << "Key already in use" << std::endl;
-               }
-
-               // But go on
-               {
-                       key_setting *k = NULL;
-                       for (key_setting *ks : key_settings) {
-                               if (ks->id == activeKey) {
-                                       k = ks;
-                                       break;
-                               }
-                       }
-                       FATAL_ERROR_IF(k == NULL, "Key setting not found");
-                       k->key = kp;
-                       const wchar_t *text = wgettext(k->key.name());
-                       k->button->setText(text);
-                       delete[] text;
-
-                       this->key_used.push_back(kp);
-
-                       // Allow characters made with shift
-                       if(shift_went_down){
-                               shift_down = true;
-                               return false;
-                       }
-
-                       activeKey = -1;
-                       return true;
-               }
-       } else if (event.EventType == EET_KEY_INPUT_EVENT && activeKey < 0
-                       && event.KeyInput.PressedDown
-                       && event.KeyInput.Key == irr::KEY_ESCAPE) {
-               quitMenu();
-               return true;
-       } else 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;
-                               default:
-                                       key_setting *k = NULL;
-
-                                       for (key_setting *ks : key_settings) {
-                                               if (ks->id == event.GUIEvent.Caller->getID()) {
-                                                       k = ks;
-                                                       break;
-                                               }
-                                       }
-                                       FATAL_ERROR_IF(k == NULL, "Key setting not found");
-
-                                       resetMenu();
-                                       shift_down = false;
-                                       activeKey = event.GUIEvent.Caller->getID();
-                                       const wchar_t *text = wgettext("press key");
-                                       k->button->setText(text);
-                                       delete[] text;
-                                       this->key_used.erase(std::remove(this->key_used.begin(),
-                                                       this->key_used.end(), k->key), this->key_used.end());
-                                       break;
-                       }
-                       Environment->setFocus(this);
-               }
-       }
-       return Parent ? Parent->OnEvent(event) : false;
-}
-
-void GUIKeyChangeMenu::add_key(int id, const wchar_t *button_name, const std::string &setting_name)
-{
-       key_setting *k = new key_setting;
-       k->id = id;
-
-       k->button_name = button_name;
-       k->setting_name = setting_name;
-       k->key = getKeySetting(k->setting_name.c_str());
-       key_settings.push_back(k);
-}
-
-void GUIKeyChangeMenu::init_keys()
-{
-       this->add_key(GUI_ID_KEY_FORWARD_BUTTON,   wgettext("Forward"),          "keymap_forward");
-       this->add_key(GUI_ID_KEY_BACKWARD_BUTTON,  wgettext("Backward"),         "keymap_backward");
-       this->add_key(GUI_ID_KEY_LEFT_BUTTON,      wgettext("Left"),             "keymap_left");
-       this->add_key(GUI_ID_KEY_RIGHT_BUTTON,     wgettext("Right"),            "keymap_right");
-       this->add_key(GUI_ID_KEY_USE_BUTTON,       wgettext("Special"),          "keymap_special1");
-       this->add_key(GUI_ID_KEY_JUMP_BUTTON,      wgettext("Jump"),             "keymap_jump");
-       this->add_key(GUI_ID_KEY_SNEAK_BUTTON,     wgettext("Sneak"),            "keymap_sneak");
-       this->add_key(GUI_ID_KEY_DROP_BUTTON,      wgettext("Drop"),             "keymap_drop");
-       this->add_key(GUI_ID_KEY_INVENTORY_BUTTON, wgettext("Inventory"),        "keymap_inventory");
-       this->add_key(GUI_ID_KEY_HOTBAR_PREV_BUTTON,wgettext("Prev. item"),      "keymap_hotbar_previous");
-       this->add_key(GUI_ID_KEY_HOTBAR_NEXT_BUTTON,wgettext("Next item"),       "keymap_hotbar_next");
-       this->add_key(GUI_ID_KEY_ZOOM_BUTTON,      wgettext("Zoom"),             "keymap_zoom");
-       this->add_key(GUI_ID_KEY_CAMERA_BUTTON,    wgettext("Change camera"),    "keymap_camera_mode");
-       this->add_key(GUI_ID_KEY_CINEMATIC_BUTTON, wgettext("Toggle Cinematic"), "keymap_cinematic");
-       this->add_key(GUI_ID_KEY_MINIMAP_BUTTON,   wgettext("Toggle minimap"),   "keymap_minimap");
-       this->add_key(GUI_ID_KEY_FLY_BUTTON,       wgettext("Toggle fly"),       "keymap_freemove");
-       this->add_key(GUI_ID_KEY_FAST_BUTTON,      wgettext("Toggle fast"),      "keymap_fastmove");
-       this->add_key(GUI_ID_KEY_NOCLIP_BUTTON,    wgettext("Toggle noclip"),    "keymap_noclip");
-       this->add_key(GUI_ID_KEY_MUTE_BUTTON,      wgettext("Mute"),             "keymap_mute");
-       this->add_key(GUI_ID_KEY_DEC_VOLUME_BUTTON,wgettext("Dec. volume"),      "keymap_decrease_volume");
-       this->add_key(GUI_ID_KEY_INC_VOLUME_BUTTON,wgettext("Inc. volume"),      "keymap_increase_volume");
-       this->add_key(GUI_ID_KEY_AUTOFWD_BUTTON,   wgettext("Autoforward"),      "keymap_autoforward");
-       this->add_key(GUI_ID_KEY_CHAT_BUTTON,      wgettext("Chat"),             "keymap_chat");
-       this->add_key(GUI_ID_KEY_SCREENSHOT_BUTTON,wgettext("Screenshot"),       "keymap_screenshot");
-       this->add_key(GUI_ID_KEY_RANGE_BUTTON,     wgettext("Range select"),     "keymap_rangeselect");
-       this->add_key(GUI_ID_KEY_DEC_RANGE_BUTTON, wgettext("Dec. range"),       "keymap_decrease_viewing_range_min");
-       this->add_key(GUI_ID_KEY_INC_RANGE_BUTTON, wgettext("Inc. range"),       "keymap_increase_viewing_range_min");
-       this->add_key(GUI_ID_KEY_CONSOLE_BUTTON,   wgettext("Console"),          "keymap_console");
-       this->add_key(GUI_ID_KEY_CMD_BUTTON,       wgettext("Command"),          "keymap_cmd");
-       this->add_key(GUI_ID_KEY_CMD_LOCAL_BUTTON, wgettext("Local command"),    "keymap_cmd_local");
-       this->add_key(GUI_ID_KEY_HUD_BUTTON,       wgettext("Toggle HUD"),       "keymap_toggle_hud");
-       this->add_key(GUI_ID_KEY_CHATLOG_BUTTON,   wgettext("Toggle chat log"),  "keymap_toggle_chat");
-       this->add_key(GUI_ID_KEY_FOG_BUTTON,       wgettext("Toggle fog"),       "keymap_toggle_force_fog_off");
-}
-
diff --git a/src/guiKeyChangeMenu.h b/src/guiKeyChangeMenu.h
deleted file mode 100644 (file)
index 7cf11d3..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- Minetest
- Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
- Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
- Copyright (C) 2013 teddydestodes <derkomtur@schattengang.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser 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.
- */
-
-#pragma once
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include "gettext.h"
-#include "keycode.h"
-#include <string>
-#include <vector>
-
-struct key_setting
-{
-       int id;
-       const wchar_t *button_name;
-       KeyPress key;
-       std::string setting_name;
-       gui::IGUIButton *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);
-
-       bool pausesGame() { return true; }
-
-private:
-       void init_keys();
-
-       bool resetMenu();
-
-       void add_key(int id, const wchar_t *button_name, const std::string &setting_name);
-
-       bool shift_down = false;
-       s32 activeKey = -1;
-
-       std::vector<KeyPress> key_used;
-       gui::IGUIStaticText *key_used_text = nullptr;
-       std::vector<key_setting *> key_settings;
-};
diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h
deleted file mode 100644 (file)
index 43a3b1a..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include <string>
-#include <list>
-
-struct MainMenuDataForScript {
-
-       MainMenuDataForScript() = default;
-
-       // Whether the server has requested a reconnect
-       bool reconnect_requested = false;
-       std::string errormessage = "";
-};
-
-struct MainMenuData {
-       // Client options
-       std::string servername;
-       std::string serverdescription;
-       std::string address;
-       std::string port;
-       std::string name;
-       std::string password;
-       // Whether to reconnect
-       bool do_reconnect = false;
-
-       // Server options
-       int selected_world = 0;
-       bool simple_singleplayer_mode = false;
-
-       // Data to be passed to the script
-       MainMenuDataForScript script_data;
-
-       MainMenuData() = default;
-};
diff --git a/src/guiPasswordChange.cpp b/src/guiPasswordChange.cpp
deleted file mode 100644 (file)
index 46de202..0000000
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
-Part of Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
-
-Permission to use, copy, modify, and distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#include "guiPasswordChange.h"
-#include "client.h"
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-
-#include "gettext.h"
-
-const int ID_oldPassword = 256;
-const int ID_newPassword1 = 257;
-const int ID_newPassword2 = 258;
-const int ID_change = 259;
-const int ID_message = 260;
-const int ID_cancel = 261;
-
-GUIPasswordChange::GUIPasswordChange(gui::IGUIEnvironment* env,
-               gui::IGUIElement* parent, s32 id,
-               IMenuManager *menumgr,
-               Client* client
-):
-       GUIModalMenu(env, parent, id, menumgr),
-       m_client(client)
-{
-}
-
-GUIPasswordChange::~GUIPasswordChange()
-{
-       removeChildren();
-}
-
-void GUIPasswordChange::removeChildren()
-{
-       const core::list<gui::IGUIElement *> &children = getChildren();
-       core::list<gui::IGUIElement *> children_copy;
-       for (gui::IGUIElement *i : children) {
-               children_copy.push_back(i);
-       }
-
-       for (gui::IGUIElement *i : children_copy) {
-               i->remove();
-       }
-}
-void GUIPasswordChange::regenerateGui(v2u32 screensize)
-{
-       /*
-               save current input
-       */
-       acceptInput();
-
-       /*
-               Remove stuff
-       */
-       removeChildren();
-
-       /*
-               Calculate new sizes and positions
-       */
-       core::rect<s32> rect(
-                       screensize.X/2 - 580/2,
-                       screensize.Y/2 - 300/2,
-                       screensize.X/2 + 580/2,
-                       screensize.Y/2 + 300/2
-       );
-
-       DesiredRect = rect;
-       recalculateAbsolutePosition(false);
-
-       v2s32 size = rect.getSize();
-       v2s32 topleft_client(40, 0);
-
-       const wchar_t *text;
-
-       /*
-               Add stuff
-       */
-       s32 ypos = 50;
-       {
-               core::rect<s32> rect(0, 0, 150, 20);
-               rect += topleft_client + v2s32(25, ypos + 6);
-               text = wgettext("Old Password");
-               Environment->addStaticText(text, rect, false, true, this, -1);
-               delete[] text;
-       }
-       {
-               core::rect<s32> rect(0, 0, 230, 30);
-               rect += topleft_client + v2s32(160, ypos);
-               gui::IGUIEditBox *e = Environment->addEditBox(
-                               m_oldpass.c_str(), rect, true, this, ID_oldPassword);
-               Environment->setFocus(e);
-               e->setPasswordBox(true);
-       }
-       ypos += 50;
-       {
-               core::rect<s32> rect(0, 0, 150, 20);
-               rect += topleft_client + v2s32(25, ypos + 6);
-               text = wgettext("New Password");
-               Environment->addStaticText(text, rect, false, true, this, -1);
-               delete[] text;
-       }
-       {
-               core::rect<s32> rect(0, 0, 230, 30);
-               rect += topleft_client + v2s32(160, ypos);
-               gui::IGUIEditBox *e = Environment->addEditBox(
-                               m_newpass.c_str(), rect, true, this, ID_newPassword1);
-               e->setPasswordBox(true);
-       }
-       ypos += 50;
-       {
-               core::rect<s32> rect(0, 0, 150, 20);
-               rect += topleft_client + v2s32(25, ypos + 6);
-               text = wgettext("Confirm Password");
-               Environment->addStaticText(text, rect, false, true, this, -1);
-               delete[] text;
-       }
-       {
-               core::rect<s32> rect(0, 0, 230, 30);
-               rect += topleft_client + v2s32(160, ypos);
-               gui::IGUIEditBox *e = Environment->addEditBox(
-                               m_newpass_confirm.c_str(), rect, true, this, ID_newPassword2);
-               e->setPasswordBox(true);
-       }
-
-       ypos += 50;
-       {
-               core::rect<s32> rect(0, 0, 100, 30);
-               rect = rect + v2s32(size.X / 4 + 56, ypos);
-               text = wgettext("Change");
-               Environment->addButton(rect, this, ID_change, text);
-               delete[] text;
-       }
-       {
-               core::rect<s32> rect(0, 0, 100, 30);
-               rect = rect + v2s32(size.X / 4 + 185, ypos);
-               text = wgettext("Cancel");
-               Environment->addButton(rect, this, ID_cancel, text);
-               delete[] text;
-       }
-
-       ypos += 50;
-       {
-               core::rect<s32> rect(0, 0, 300, 20);
-               rect += topleft_client + v2s32(35, ypos);
-               text = wgettext("Passwords do not match!");
-               IGUIElement *e =
-                       Environment->addStaticText(
-                       text, rect, false, true, this, ID_message);
-               e->setVisible(false);
-               delete[] text;
-       }
-}
-
-void GUIPasswordChange::drawMenu()
-{
-       gui::IGUISkin *skin = Environment->getSkin();
-       if (!skin)
-               return;
-       video::IVideoDriver *driver = Environment->getVideoDriver();
-
-       video::SColor bgcolor(140, 0, 0, 0);
-       driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
-
-       gui::IGUIElement::draw();
-}
-
-void GUIPasswordChange::acceptInput()
-{
-       gui::IGUIElement *e;
-       e = getElementFromId(ID_oldPassword);
-       if (e != NULL)
-               m_oldpass = e->getText();
-       e = getElementFromId(ID_newPassword1);
-       if (e != NULL)
-               m_newpass = e->getText();
-       e = getElementFromId(ID_newPassword2);
-       if (e != NULL)
-               m_newpass_confirm = e->getText();
-}
-
-bool GUIPasswordChange::processInput()
-{
-       if (m_newpass != m_newpass_confirm) {
-               gui::IGUIElement *e = getElementFromId(ID_message);
-               if (e != NULL)
-                       e->setVisible(true);
-               return false;
-       }
-       m_client->sendChangePassword(wide_to_utf8(m_oldpass), wide_to_utf8(m_newpass));
-       return true;
-}
-
-bool GUIPasswordChange::OnEvent(const SEvent &event)
-{
-       if (event.EventType == EET_KEY_INPUT_EVENT) {
-               if (event.KeyInput.Key == KEY_ESCAPE && event.KeyInput.PressedDown) {
-                       quitMenu();
-                       return true;
-               }
-               if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
-                       acceptInput();
-                       if (processInput())
-                               quitMenu();
-                       return true;
-               }
-       }
-       if (event.EventType == EET_GUI_EVENT) {
-               if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST &&
-                               isVisible()) {
-                       if (!canTakeFocus(event.GUIEvent.Element)) {
-                               dstream << "GUIPasswordChange: 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 ID_change:
-                               acceptInput();
-                               if (processInput())
-                                       quitMenu();
-                               return true;
-                       case ID_cancel:
-                               quitMenu();
-                               return true;
-                       }
-               }
-               if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
-                       switch (event.GUIEvent.Caller->getID()) {
-                       case ID_oldPassword:
-                       case ID_newPassword1:
-                       case ID_newPassword2:
-                               acceptInput();
-                               if (processInput())
-                                       quitMenu();
-                               return true;
-                       }
-               }
-       }
-
-       return Parent ? Parent->OnEvent(event) : false;
-}
diff --git a/src/guiPasswordChange.h b/src/guiPasswordChange.h
deleted file mode 100644 (file)
index 59f3513..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
-Part of Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
-
-Permission to use, copy, modify, and distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#pragma once
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include <string>
-
-class Client;
-
-class GUIPasswordChange : public GUIModalMenu
-{
-public:
-       GUIPasswordChange(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
-                       IMenuManager *menumgr, Client *client);
-       ~GUIPasswordChange();
-
-       void removeChildren();
-       /*
-               Remove and re-add (or reposition) stuff
-       */
-       void regenerateGui(v2u32 screensize);
-
-       void drawMenu();
-
-       void acceptInput();
-
-       bool processInput();
-
-       bool OnEvent(const SEvent &event);
-
-private:
-       Client *m_client;
-       std::wstring m_oldpass = L"";
-       std::wstring m_newpass = L"";
-       std::wstring m_newpass_confirm = L"";
-};
diff --git a/src/guiPathSelectMenu.cpp b/src/guiPathSelectMenu.cpp
deleted file mode 100644 (file)
index b999f0a..0000000
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- Minetest
- Copyright (C) 2013 sapier
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser 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 "guiPathSelectMenu.h"
-
-GUIFileSelectMenu::GUIFileSelectMenu(gui::IGUIEnvironment* env,
-               gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
-               const std::string &title, const std::string &formname,
-               bool is_file_select) :
-       GUIModalMenu(env, parent, id, menumgr),
-       m_title(utf8_to_wide(title)),
-       m_formname(formname),
-       m_file_select_dialog(is_file_select)
-{
-}
-
-GUIFileSelectMenu::~GUIFileSelectMenu()
-{
-       removeChildren();
-       setlocale(LC_NUMERIC, "C");
-}
-
-void GUIFileSelectMenu::regenerateGui(v2u32 screensize)
-{
-       removeChildren();
-       m_fileOpenDialog = 0;
-
-       core::dimension2du size(600, 400);
-       core::rect<s32> rect(0, 0, screensize.X, screensize.Y);
-
-       DesiredRect = rect;
-       recalculateAbsolutePosition(false);
-
-       m_fileOpenDialog =
-                       Environment->addFileOpenDialog(m_title.c_str(), false, this, -1);
-
-       core::position2di pos = core::position2di(screensize.X / 2 - size.Width / 2,
-                       screensize.Y / 2 - size.Height / 2);
-       m_fileOpenDialog->setRelativePosition(pos);
-       m_fileOpenDialog->setMinSize(size);
-}
-
-void GUIFileSelectMenu::drawMenu()
-{
-       gui::IGUISkin *skin = Environment->getSkin();
-       if (!skin)
-               return;
-
-       gui::IGUIElement::draw();
-}
-
-void GUIFileSelectMenu::acceptInput()
-{
-       if (m_text_dst && !m_formname.empty()) {
-               StringMap fields;
-               if (m_accepted) {
-                       std::string path;
-                       if (!m_file_select_dialog) {
-                               core::string<fschar_t> string =
-                                               m_fileOpenDialog->getDirectoryName();
-                               path = std::string(string.c_str());
-                       } else {
-                               path = wide_to_utf8(m_fileOpenDialog->getFileName());
-                       }
-                       fields[m_formname + "_accepted"] = path;
-               } else {
-                       fields[m_formname + "_canceled"] = m_formname;
-               }
-               m_text_dst->gotText(fields);
-       }
-       quitMenu();
-}
-
-bool GUIFileSelectMenu::OnEvent(const SEvent &event)
-{
-       if (event.EventType == irr::EET_GUI_EVENT) {
-               switch (event.GUIEvent.EventType) {
-               case gui::EGET_ELEMENT_CLOSED:
-               case gui::EGET_FILE_CHOOSE_DIALOG_CANCELLED:
-                       m_accepted = false;
-                       acceptInput();
-                       return true;
-               case gui::EGET_DIRECTORY_SELECTED:
-                       m_accepted = !m_file_select_dialog;
-                       acceptInput();
-                       return true;
-               case gui::EGET_FILE_SELECTED:
-                       m_accepted = m_file_select_dialog;
-                       acceptInput();
-                       return true;
-               default:
-                       // ignore this event
-                       break;
-               }
-       }
-       return Parent ? Parent->OnEvent(event) : false;
-}
diff --git a/src/guiPathSelectMenu.h b/src/guiPathSelectMenu.h
deleted file mode 100644 (file)
index f69d0ac..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- Minetest
- Copyright (C) 2013 sapier
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser 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.
- */
-
-#pragma once
-
-#include <string>
-
-#include "modalMenu.h"
-#include "IGUIFileOpenDialog.h"
-#include "guiFormSpecMenu.h" //required because of TextDest only !!!
-
-class GUIFileSelectMenu : public GUIModalMenu
-{
-public:
-       GUIFileSelectMenu(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
-                       IMenuManager *menumgr, const std::string &title,
-                       const std::string &formid, bool is_file_select);
-       ~GUIFileSelectMenu();
-
-       /*
-        Remove and re-add (or reposition) stuff
-        */
-       void regenerateGui(v2u32 screensize);
-
-       void drawMenu();
-
-       bool OnEvent(const SEvent &event);
-
-       void setTextDest(TextDest *dest) { m_text_dst = dest; }
-
-private:
-       void acceptInput();
-
-       std::wstring m_title;
-       bool m_accepted = false;
-
-       gui::IGUIFileOpenDialog *m_fileOpenDialog = nullptr;
-
-       TextDest *m_text_dst = nullptr;
-
-       std::string m_formname;
-       bool m_file_select_dialog;
-};
diff --git a/src/guiTable.cpp b/src/guiTable.cpp
deleted file mode 100644 (file)
index a2738af..0000000
+++ /dev/null
@@ -1,1261 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "guiTable.h"
-#include <queue>
-#include <sstream>
-#include <utility>
-#include <cstring>
-#include <IGUISkin.h>
-#include <IGUIFont.h>
-#include <IGUIScrollBar.h>
-#include "client/renderingengine.h"
-#include "debug.h"
-#include "log.h"
-#include "client/tile.h"
-#include "gettime.h"
-#include "util/string.h"
-#include "util/numeric.h"
-#include "util/string.h" // for parseColorString()
-#include "settings.h" // for settings
-#include "porting.h" // for dpi
-#include "guiscalingfilter.h"
-
-/*
-       GUITable
-*/
-
-GUITable::GUITable(gui::IGUIEnvironment *env,
-               gui::IGUIElement* parent, s32 id,
-               core::rect<s32> rectangle,
-               ISimpleTextureSource *tsrc
-):
-       gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle),
-       m_tsrc(tsrc)
-{
-       assert(tsrc != NULL);
-
-       gui::IGUISkin* skin = Environment->getSkin();
-
-       m_font = skin->getFont();
-       if (m_font) {
-               m_font->grab();
-               m_rowheight = m_font->getDimension(L"A").Height + 4;
-               m_rowheight = MYMAX(m_rowheight, 1);
-       }
-
-       const s32 s = skin->getSize(gui::EGDS_SCROLLBAR_SIZE);
-       m_scrollbar = Environment->addScrollBar(false,
-                       core::rect<s32>(RelativeRect.getWidth() - s,
-                                       0,
-                                       RelativeRect.getWidth(),
-                                       RelativeRect.getHeight()),
-                       this, -1);
-       m_scrollbar->setSubElement(true);
-       m_scrollbar->setTabStop(false);
-       m_scrollbar->setAlignment(gui::EGUIA_LOWERRIGHT, gui::EGUIA_LOWERRIGHT,
-                       gui::EGUIA_UPPERLEFT, gui::EGUIA_LOWERRIGHT);
-       m_scrollbar->setVisible(false);
-       m_scrollbar->setPos(0);
-
-       setTabStop(true);
-       setTabOrder(-1);
-       updateAbsolutePosition();
-
-       core::rect<s32> relative_rect = m_scrollbar->getRelativePosition();
-       s32 width = (relative_rect.getWidth()/(2.0/3.0)) *
-                       RenderingEngine::getDisplayDensity() *
-                       g_settings->getFloat("gui_scaling");
-       m_scrollbar->setRelativePosition(core::rect<s32>(
-                       relative_rect.LowerRightCorner.X-width,relative_rect.UpperLeftCorner.Y,
-                       relative_rect.LowerRightCorner.X,relative_rect.LowerRightCorner.Y
-                       ));
-}
-
-GUITable::~GUITable()
-{
-       for (GUITable::Row &row : m_rows)
-               delete[] row.cells;
-
-       if (m_font)
-               m_font->drop();
-
-       m_scrollbar->remove();
-}
-
-GUITable::Option GUITable::splitOption(const std::string &str)
-{
-       size_t equal_pos = str.find('=');
-       if (equal_pos == std::string::npos)
-               return GUITable::Option(str, "");
-
-       return GUITable::Option(str.substr(0, equal_pos),
-                       str.substr(equal_pos + 1));
-}
-
-void GUITable::setTextList(const std::vector<std::string> &content,
-               bool transparent)
-{
-       clear();
-
-       if (transparent) {
-               m_background.setAlpha(0);
-               m_border = false;
-       }
-
-       m_is_textlist = true;
-
-       s32 empty_string_index = allocString("");
-
-       m_rows.resize(content.size());
-       for (s32 i = 0; i < (s32) content.size(); ++i) {
-               Row *row = &m_rows[i];
-               row->cells = new Cell[1];
-               row->cellcount = 1;
-               row->indent = 0;
-               row->visible_index = i;
-               m_visible_rows.push_back(i);
-
-               Cell *cell = row->cells;
-               cell->xmin = 0;
-               cell->xmax = 0x7fff;  // something large enough
-               cell->xpos = 6;
-               cell->content_type = COLUMN_TYPE_TEXT;
-               cell->content_index = empty_string_index;
-               cell->tooltip_index = empty_string_index;
-               cell->color.set(255, 255, 255, 255);
-               cell->color_defined = false;
-               cell->reported_column = 1;
-
-               // parse row content (color)
-               const std::string &s = content[i];
-               if (s[0] == '#' && s[1] == '#') {
-                       // double # to escape
-                       cell->content_index = allocString(s.substr(2));
-               }
-               else if (s[0] == '#' && s.size() >= 7 &&
-                               parseColorString(
-                                       s.substr(0,7), cell->color, false)) {
-                       // single # for color
-                       cell->color_defined = true;
-                       cell->content_index = allocString(s.substr(7));
-               }
-               else {
-                       // no #, just text
-                       cell->content_index = allocString(s);
-               }
-
-       }
-
-       allocationComplete();
-
-       // Clamp scroll bar position
-       updateScrollBar();
-}
-
-void GUITable::setTable(const TableOptions &options,
-               const TableColumns &columns,
-               std::vector<std::string> &content)
-{
-       clear();
-
-       // Naming conventions:
-       // i is always a row index, 0-based
-       // j is always a column index, 0-based
-       // k is another index, for example an option index
-
-       // Handle a stupid error case... (issue #1187)
-       if (columns.empty()) {
-               TableColumn text_column;
-               text_column.type = "text";
-               TableColumns new_columns;
-               new_columns.push_back(text_column);
-               setTable(options, new_columns, content);
-               return;
-       }
-
-       // Handle table options
-       video::SColor default_color(255, 255, 255, 255);
-       s32 opendepth = 0;
-       for (const Option &option : options) {
-               const std::string &name = option.name;
-               const std::string &value = option.value;
-               if (name == "color")
-                       parseColorString(value, m_color, false);
-               else if (name == "background")
-                       parseColorString(value, m_background, false);
-               else if (name == "border")
-                       m_border = is_yes(value);
-               else if (name == "highlight")
-                       parseColorString(value, m_highlight, false);
-               else if (name == "highlight_text")
-                       parseColorString(value, m_highlight_text, false);
-               else if (name == "opendepth")
-                       opendepth = stoi(value);
-               else
-                       errorstream<<"Invalid table option: \""<<name<<"\""
-                               <<" (value=\""<<value<<"\")"<<std::endl;
-       }
-
-       // Get number of columns and rows
-       // note: error case columns.size() == 0 was handled above
-       s32 colcount = columns.size();
-       assert(colcount >= 1);
-       // rowcount = ceil(cellcount / colcount) but use integer arithmetic
-       s32 rowcount = (content.size() + colcount - 1) / colcount;
-       assert(rowcount >= 0);
-       // Append empty strings to content if there is an incomplete row
-       s32 cellcount = rowcount * colcount;
-       while (content.size() < (u32) cellcount)
-               content.emplace_back("");
-
-       // Create temporary rows (for processing columns)
-       struct TempRow {
-               // Current horizontal position (may different between rows due
-               // to indent/tree columns, or text/image columns with width<0)
-               s32 x;
-               // Tree indentation level
-               s32 indent;
-               // Next cell: Index into m_strings or m_images
-               s32 content_index;
-               // Next cell: Width in pixels
-               s32 content_width;
-               // Vector of completed cells in this row
-               std::vector<Cell> cells;
-               // Stores colors and how long they last (maximum column index)
-               std::vector<std::pair<video::SColor, s32> > colors;
-
-               TempRow(): x(0), indent(0), content_index(0), content_width(0) {}
-       };
-       TempRow *rows = new TempRow[rowcount];
-
-       // Get em width. Pedantically speaking, the width of "M" is not
-       // necessarily the same as the em width, but whatever, close enough.
-       s32 em = 6;
-       if (m_font)
-               em = m_font->getDimension(L"M").Width;
-
-       s32 default_tooltip_index = allocString("");
-
-       std::map<s32, s32> active_image_indices;
-
-       // Process content in column-major order
-       for (s32 j = 0; j < colcount; ++j) {
-               // Check column type
-               ColumnType columntype = COLUMN_TYPE_TEXT;
-               if (columns[j].type == "text")
-                       columntype = COLUMN_TYPE_TEXT;
-               else if (columns[j].type == "image")
-                       columntype = COLUMN_TYPE_IMAGE;
-               else if (columns[j].type == "color")
-                       columntype = COLUMN_TYPE_COLOR;
-               else if (columns[j].type == "indent")
-                       columntype = COLUMN_TYPE_INDENT;
-               else if (columns[j].type == "tree")
-                       columntype = COLUMN_TYPE_TREE;
-               else
-                       errorstream<<"Invalid table column type: \""
-                               <<columns[j].type<<"\""<<std::endl;
-
-               // Process column options
-               s32 padding = myround(0.5 * em);
-               s32 tooltip_index = default_tooltip_index;
-               s32 align = 0;
-               s32 width = 0;
-               s32 span = colcount;
-
-               if (columntype == COLUMN_TYPE_INDENT) {
-                       padding = 0; // default indent padding
-               }
-               if (columntype == COLUMN_TYPE_INDENT ||
-                               columntype == COLUMN_TYPE_TREE) {
-                       width = myround(em * 1.5); // default indent width
-               }
-
-               for (const Option &option : columns[j].options) {
-                       const std::string &name = option.name;
-                       const std::string &value = option.value;
-                       if (name == "padding")
-                               padding = myround(stof(value) * em);
-                       else if (name == "tooltip")
-                               tooltip_index = allocString(value);
-                       else if (name == "align" && value == "left")
-                               align = 0;
-                       else if (name == "align" && value == "center")
-                               align = 1;
-                       else if (name == "align" && value == "right")
-                               align = 2;
-                       else if (name == "align" && value == "inline")
-                               align = 3;
-                       else if (name == "width")
-                               width = myround(stof(value) * em);
-                       else if (name == "span" && columntype == COLUMN_TYPE_COLOR)
-                               span = stoi(value);
-                       else if (columntype == COLUMN_TYPE_IMAGE &&
-                                       !name.empty() &&
-                                       string_allowed(name, "0123456789")) {
-                               s32 content_index = allocImage(value);
-                               active_image_indices.insert(std::make_pair(
-                                                       stoi(name),
-                                                       content_index));
-                       }
-                       else {
-                               errorstream<<"Invalid table column option: \""<<name<<"\""
-                                       <<" (value=\""<<value<<"\")"<<std::endl;
-                       }
-               }
-
-               // If current column type can use information from "color" columns,
-               // find out which of those is currently active
-               if (columntype == COLUMN_TYPE_TEXT) {
-                       for (s32 i = 0; i < rowcount; ++i) {
-                               TempRow *row = &rows[i];
-                               while (!row->colors.empty() && row->colors.back().second < j)
-                                       row->colors.pop_back();
-                       }
-               }
-
-               // Make template for new cells
-               Cell newcell;
-               memset(&newcell, 0, sizeof newcell);
-               newcell.content_type = columntype;
-               newcell.tooltip_index = tooltip_index;
-               newcell.reported_column = j+1;
-
-               if (columntype == COLUMN_TYPE_TEXT) {
-                       // Find right edge of column
-                       s32 xmax = 0;
-                       for (s32 i = 0; i < rowcount; ++i) {
-                               TempRow *row = &rows[i];
-                               row->content_index = allocString(content[i * colcount + j]);
-                               const core::stringw &text = m_strings[row->content_index];
-                               row->content_width = m_font ?
-                                       m_font->getDimension(text.c_str()).Width : 0;
-                               row->content_width = MYMAX(row->content_width, width);
-                               s32 row_xmax = row->x + padding + row->content_width;
-                               xmax = MYMAX(xmax, row_xmax);
-                       }
-                       // Add a new cell (of text type) to each row
-                       for (s32 i = 0; i < rowcount; ++i) {
-                               newcell.xmin = rows[i].x + padding;
-                               alignContent(&newcell, xmax, rows[i].content_width, align);
-                               newcell.content_index = rows[i].content_index;
-                               newcell.color_defined = !rows[i].colors.empty();
-                               if (newcell.color_defined)
-                                       newcell.color = rows[i].colors.back().first;
-                               rows[i].cells.push_back(newcell);
-                               rows[i].x = newcell.xmax;
-                       }
-               }
-               else if (columntype == COLUMN_TYPE_IMAGE) {
-                       // Find right edge of column
-                       s32 xmax = 0;
-                       for (s32 i = 0; i < rowcount; ++i) {
-                               TempRow *row = &rows[i];
-                               row->content_index = -1;
-
-                               // Find content_index. Image indices are defined in
-                               // column options so check active_image_indices.
-                               s32 image_index = stoi(content[i * colcount + j]);
-                               std::map<s32, s32>::iterator image_iter =
-                                       active_image_indices.find(image_index);
-                               if (image_iter != active_image_indices.end())
-                                       row->content_index = image_iter->second;
-
-                               // Get texture object (might be NULL)
-                               video::ITexture *image = NULL;
-                               if (row->content_index >= 0)
-                                       image = m_images[row->content_index];
-
-                               // Get content width and update xmax
-                               row->content_width = image ? image->getOriginalSize().Width : 0;
-                               row->content_width = MYMAX(row->content_width, width);
-                               s32 row_xmax = row->x + padding + row->content_width;
-                               xmax = MYMAX(xmax, row_xmax);
-                       }
-                       // Add a new cell (of image type) to each row
-                       for (s32 i = 0; i < rowcount; ++i) {
-                               newcell.xmin = rows[i].x + padding;
-                               alignContent(&newcell, xmax, rows[i].content_width, align);
-                               newcell.content_index = rows[i].content_index;
-                               rows[i].cells.push_back(newcell);
-                               rows[i].x = newcell.xmax;
-                       }
-                       active_image_indices.clear();
-               }
-               else if (columntype == COLUMN_TYPE_COLOR) {
-                       for (s32 i = 0; i < rowcount; ++i) {
-                               video::SColor cellcolor(255, 255, 255, 255);
-                               if (parseColorString(content[i * colcount + j], cellcolor, true))
-                                       rows[i].colors.emplace_back(cellcolor, j+span);
-                       }
-               }
-               else if (columntype == COLUMN_TYPE_INDENT ||
-                               columntype == COLUMN_TYPE_TREE) {
-                       // For column type "tree", reserve additional space for +/-
-                       // Also enable special processing for treeview-type tables
-                       s32 content_width = 0;
-                       if (columntype == COLUMN_TYPE_TREE) {
-                               content_width = m_font ? m_font->getDimension(L"+").Width : 0;
-                               m_has_tree_column = true;
-                       }
-                       // Add a new cell (of indent or tree type) to each row
-                       for (s32 i = 0; i < rowcount; ++i) {
-                               TempRow *row = &rows[i];
-
-                               s32 indentlevel = stoi(content[i * colcount + j]);
-                               indentlevel = MYMAX(indentlevel, 0);
-                               if (columntype == COLUMN_TYPE_TREE)
-                                       row->indent = indentlevel;
-
-                               newcell.xmin = row->x + padding;
-                               newcell.xpos = newcell.xmin + indentlevel * width;
-                               newcell.xmax = newcell.xpos + content_width;
-                               newcell.content_index = 0;
-                               newcell.color_defined = !rows[i].colors.empty();
-                               if (newcell.color_defined)
-                                       newcell.color = rows[i].colors.back().first;
-                               row->cells.push_back(newcell);
-                               row->x = newcell.xmax;
-                       }
-               }
-       }
-
-       // Copy temporary rows to not so temporary rows
-       if (rowcount >= 1) {
-               m_rows.resize(rowcount);
-               for (s32 i = 0; i < rowcount; ++i) {
-                       Row *row = &m_rows[i];
-                       row->cellcount = rows[i].cells.size();
-                       row->cells = new Cell[row->cellcount];
-                       memcpy((void*) row->cells, (void*) &rows[i].cells[0],
-                                       row->cellcount * sizeof(Cell));
-                       row->indent = rows[i].indent;
-                       row->visible_index = i;
-                       m_visible_rows.push_back(i);
-               }
-       }
-
-       if (m_has_tree_column) {
-               // Treeview: convert tree to indent cells on leaf rows
-               for (s32 i = 0; i < rowcount; ++i) {
-                       if (i == rowcount-1 || m_rows[i].indent >= m_rows[i+1].indent)
-                               for (s32 j = 0; j < m_rows[i].cellcount; ++j)
-                                       if (m_rows[i].cells[j].content_type == COLUMN_TYPE_TREE)
-                                               m_rows[i].cells[j].content_type = COLUMN_TYPE_INDENT;
-               }
-
-               // Treeview: close rows according to opendepth option
-               std::set<s32> opened_trees;
-               for (s32 i = 0; i < rowcount; ++i)
-                       if (m_rows[i].indent < opendepth)
-                               opened_trees.insert(i);
-               setOpenedTrees(opened_trees);
-       }
-
-       // Delete temporary information used only during setTable()
-       delete[] rows;
-       allocationComplete();
-
-       // Clamp scroll bar position
-       updateScrollBar();
-}
-
-void GUITable::clear()
-{
-       // Clean up cells and rows
-       for (GUITable::Row &row : m_rows)
-               delete[] row.cells;
-       m_rows.clear();
-       m_visible_rows.clear();
-
-       // Get colors from skin
-       gui::IGUISkin *skin = Environment->getSkin();
-       m_color          = skin->getColor(gui::EGDC_BUTTON_TEXT);
-       m_background     = skin->getColor(gui::EGDC_3D_HIGH_LIGHT);
-       m_highlight      = skin->getColor(gui::EGDC_HIGH_LIGHT);
-       m_highlight_text = skin->getColor(gui::EGDC_HIGH_LIGHT_TEXT);
-
-       // Reset members
-       m_is_textlist = false;
-       m_has_tree_column = false;
-       m_selected = -1;
-       m_sel_column = 0;
-       m_sel_doubleclick = false;
-       m_keynav_time = 0;
-       m_keynav_buffer = L"";
-       m_border = true;
-       m_strings.clear();
-       m_images.clear();
-       m_alloc_strings.clear();
-       m_alloc_images.clear();
-}
-
-std::string GUITable::checkEvent()
-{
-       s32 sel = getSelected();
-       assert(sel >= 0);
-
-       if (sel == 0) {
-               return "INV";
-       }
-
-       std::ostringstream os(std::ios::binary);
-       if (m_sel_doubleclick) {
-               os<<"DCL:";
-               m_sel_doubleclick = false;
-       }
-       else {
-               os<<"CHG:";
-       }
-       os<<sel;
-       if (!m_is_textlist) {
-               os<<":"<<m_sel_column;
-       }
-       return os.str();
-}
-
-s32 GUITable::getSelected() const
-{
-       if (m_selected < 0)
-               return 0;
-
-       assert(m_selected >= 0 && m_selected < (s32) m_visible_rows.size());
-       return m_visible_rows[m_selected] + 1;
-}
-
-void GUITable::setSelected(s32 index)
-{
-       s32 old_selected = m_selected;
-
-       m_selected = -1;
-       m_sel_column = 0;
-       m_sel_doubleclick = false;
-
-       --index; // Switch from 1-based indexing to 0-based indexing
-
-       s32 rowcount = m_rows.size();
-       if (rowcount == 0 || index < 0) {
-               return;
-       }
-
-       if (index >= rowcount) {
-               index = rowcount - 1;
-       }
-
-       // If the selected row is not visible, open its ancestors to make it visible
-       bool selection_invisible = m_rows[index].visible_index < 0;
-       if (selection_invisible) {
-               std::set<s32> opened_trees;
-               getOpenedTrees(opened_trees);
-               s32 indent = m_rows[index].indent;
-               for (s32 j = index - 1; j >= 0; --j) {
-                       if (m_rows[j].indent < indent) {
-                               opened_trees.insert(j);
-                               indent = m_rows[j].indent;
-                       }
-               }
-               setOpenedTrees(opened_trees);
-       }
-
-       if (index >= 0) {
-               m_selected = m_rows[index].visible_index;
-               assert(m_selected >= 0 && m_selected < (s32) m_visible_rows.size());
-       }
-
-       if (m_selected != old_selected || selection_invisible) {
-               autoScroll();
-       }
-}
-
-GUITable::DynamicData GUITable::getDynamicData() const
-{
-       DynamicData dyndata;
-       dyndata.selected = getSelected();
-       dyndata.scrollpos = m_scrollbar->getPos();
-       dyndata.keynav_time = m_keynav_time;
-       dyndata.keynav_buffer = m_keynav_buffer;
-       if (m_has_tree_column)
-               getOpenedTrees(dyndata.opened_trees);
-       return dyndata;
-}
-
-void GUITable::setDynamicData(const DynamicData &dyndata)
-{
-       if (m_has_tree_column)
-               setOpenedTrees(dyndata.opened_trees);
-
-       m_keynav_time = dyndata.keynav_time;
-       m_keynav_buffer = dyndata.keynav_buffer;
-
-       setSelected(dyndata.selected);
-       m_sel_column = 0;
-       m_sel_doubleclick = false;
-
-       m_scrollbar->setPos(dyndata.scrollpos);
-}
-
-const c8* GUITable::getTypeName() const
-{
-       return "GUITable";
-}
-
-void GUITable::updateAbsolutePosition()
-{
-       IGUIElement::updateAbsolutePosition();
-       updateScrollBar();
-}
-
-void GUITable::draw()
-{
-       if (!IsVisible)
-               return;
-
-       gui::IGUISkin *skin = Environment->getSkin();
-
-       // draw background
-
-       bool draw_background = m_background.getAlpha() > 0;
-       if (m_border)
-               skin->draw3DSunkenPane(this, m_background,
-                               true, draw_background,
-                               AbsoluteRect, &AbsoluteClippingRect);
-       else if (draw_background)
-               skin->draw2DRectangle(this, m_background,
-                               AbsoluteRect, &AbsoluteClippingRect);
-
-       // get clipping rect
-
-       core::rect<s32> client_clip(AbsoluteRect);
-       client_clip.UpperLeftCorner.Y += 1;
-       client_clip.UpperLeftCorner.X += 1;
-       client_clip.LowerRightCorner.Y -= 1;
-       client_clip.LowerRightCorner.X -= 1;
-       if (m_scrollbar->isVisible()) {
-               client_clip.LowerRightCorner.X =
-                               m_scrollbar->getAbsolutePosition().UpperLeftCorner.X;
-       }
-       client_clip.clipAgainst(AbsoluteClippingRect);
-
-       // draw visible rows
-
-       s32 scrollpos = m_scrollbar->getPos();
-       s32 row_min = scrollpos / m_rowheight;
-       s32 row_max = (scrollpos + AbsoluteRect.getHeight() - 1)
-                       / m_rowheight + 1;
-       row_max = MYMIN(row_max, (s32) m_visible_rows.size());
-
-       core::rect<s32> row_rect(AbsoluteRect);
-       if (m_scrollbar->isVisible())
-               row_rect.LowerRightCorner.X -=
-                       skin->getSize(gui::EGDS_SCROLLBAR_SIZE);
-       row_rect.UpperLeftCorner.Y += row_min * m_rowheight - scrollpos;
-       row_rect.LowerRightCorner.Y = row_rect.UpperLeftCorner.Y + m_rowheight;
-
-       for (s32 i = row_min; i < row_max; ++i) {
-               Row *row = &m_rows[m_visible_rows[i]];
-               bool is_sel = i == m_selected;
-               video::SColor color = m_color;
-
-               if (is_sel) {
-                       skin->draw2DRectangle(this, m_highlight, row_rect, &client_clip);
-                       color = m_highlight_text;
-               }
-
-               for (s32 j = 0; j < row->cellcount; ++j)
-                       drawCell(&row->cells[j], color, row_rect, client_clip);
-
-               row_rect.UpperLeftCorner.Y += m_rowheight;
-               row_rect.LowerRightCorner.Y += m_rowheight;
-       }
-
-       // Draw children
-       IGUIElement::draw();
-}
-
-void GUITable::drawCell(const Cell *cell, video::SColor color,
-               const core::rect<s32> &row_rect,
-               const core::rect<s32> &client_clip)
-{
-       if ((cell->content_type == COLUMN_TYPE_TEXT)
-                       || (cell->content_type == COLUMN_TYPE_TREE)) {
-
-               core::rect<s32> text_rect = row_rect;
-               text_rect.UpperLeftCorner.X = row_rect.UpperLeftCorner.X
-                               + cell->xpos;
-               text_rect.LowerRightCorner.X = row_rect.UpperLeftCorner.X
-                               + cell->xmax;
-
-               if (cell->color_defined)
-                       color = cell->color;
-
-               if (m_font) {
-                       if (cell->content_type == COLUMN_TYPE_TEXT)
-                               m_font->draw(m_strings[cell->content_index],
-                                               text_rect, color,
-                                               false, true, &client_clip);
-                       else // tree
-                               m_font->draw(cell->content_index ? L"+" : L"-",
-                                               text_rect, color,
-                                               false, true, &client_clip);
-               }
-       }
-       else if (cell->content_type == COLUMN_TYPE_IMAGE) {
-
-               if (cell->content_index < 0)
-                       return;
-
-               video::IVideoDriver *driver = Environment->getVideoDriver();
-               video::ITexture *image = m_images[cell->content_index];
-
-               if (image) {
-                       core::position2d<s32> dest_pos =
-                                       row_rect.UpperLeftCorner;
-                       dest_pos.X += cell->xpos;
-                       core::rect<s32> source_rect(
-                                       core::position2d<s32>(0, 0),
-                                       image->getOriginalSize());
-                       s32 imgh = source_rect.LowerRightCorner.Y;
-                       s32 rowh = row_rect.getHeight();
-                       if (imgh < rowh)
-                               dest_pos.Y += (rowh - imgh) / 2;
-                       else
-                               source_rect.LowerRightCorner.Y = rowh;
-
-                       video::SColor color(255, 255, 255, 255);
-
-                       driver->draw2DImage(image, dest_pos, source_rect,
-                                       &client_clip, color, true);
-               }
-       }
-}
-
-bool GUITable::OnEvent(const SEvent &event)
-{
-       if (!isEnabled())
-               return IGUIElement::OnEvent(event);
-
-       if (event.EventType == EET_KEY_INPUT_EVENT) {
-               if (event.KeyInput.PressedDown && (
-                               event.KeyInput.Key == KEY_DOWN ||
-                               event.KeyInput.Key == KEY_UP   ||
-                               event.KeyInput.Key == KEY_HOME ||
-                               event.KeyInput.Key == KEY_END  ||
-                               event.KeyInput.Key == KEY_NEXT ||
-                               event.KeyInput.Key == KEY_PRIOR)) {
-                       s32 offset = 0;
-                       switch (event.KeyInput.Key) {
-                               case KEY_DOWN:
-                                       offset = 1;
-                                       break;
-                               case KEY_UP:
-                                       offset = -1;
-                                       break;
-                               case KEY_HOME:
-                                       offset = - (s32) m_visible_rows.size();
-                                       break;
-                               case KEY_END:
-                                       offset = m_visible_rows.size();
-                                       break;
-                               case KEY_NEXT:
-                                       offset = AbsoluteRect.getHeight() / m_rowheight;
-                                       break;
-                               case KEY_PRIOR:
-                                       offset = - (s32) (AbsoluteRect.getHeight() / m_rowheight);
-                                       break;
-                               default:
-                                       break;
-                       }
-                       s32 old_selected = m_selected;
-                       s32 rowcount = m_visible_rows.size();
-                       if (rowcount != 0) {
-                               m_selected = rangelim(m_selected + offset, 0, rowcount-1);
-                               autoScroll();
-                       }
-
-                       if (m_selected != old_selected)
-                               sendTableEvent(0, false);
-
-                       return true;
-               }
-
-               if (event.KeyInput.PressedDown && (
-                               event.KeyInput.Key == KEY_LEFT ||
-                               event.KeyInput.Key == KEY_RIGHT)) {
-                       // Open/close subtree via keyboard
-                       if (m_selected >= 0) {
-                               int dir = event.KeyInput.Key == KEY_LEFT ? -1 : 1;
-                               toggleVisibleTree(m_selected, dir, true);
-                       }
-                       return true;
-               }
-               else if (!event.KeyInput.PressedDown && (
-                               event.KeyInput.Key == KEY_RETURN ||
-                               event.KeyInput.Key == KEY_SPACE)) {
-                       sendTableEvent(0, true);
-                       return true;
-               }
-               else if (event.KeyInput.Key == KEY_ESCAPE ||
-                               event.KeyInput.Key == KEY_SPACE) {
-                       // pass to parent
-               }
-               else if (event.KeyInput.PressedDown && event.KeyInput.Char) {
-                       // change selection based on text as it is typed
-                       u64 now = porting::getTimeMs();
-                       if (now - m_keynav_time >= 500)
-                               m_keynav_buffer = L"";
-                       m_keynav_time = now;
-
-                       // add to key buffer if not a key repeat
-                       if (!(m_keynav_buffer.size() == 1 &&
-                                       m_keynav_buffer[0] == event.KeyInput.Char)) {
-                               m_keynav_buffer.append(event.KeyInput.Char);
-                       }
-
-                       // find the selected item, starting at the current selection
-                       // don't change selection if the key buffer matches the current item
-                       s32 old_selected = m_selected;
-                       s32 start = MYMAX(m_selected, 0);
-                       s32 rowcount = m_visible_rows.size();
-                       for (s32 k = 1; k < rowcount; ++k) {
-                               s32 current = start + k;
-                               if (current >= rowcount)
-                                       current -= rowcount;
-                               if (doesRowStartWith(getRow(current), m_keynav_buffer)) {
-                                       m_selected = current;
-                                       break;
-                               }
-                       }
-                       autoScroll();
-                       if (m_selected != old_selected)
-                               sendTableEvent(0, false);
-
-                       return true;
-               }
-       }
-       if (event.EventType == EET_MOUSE_INPUT_EVENT) {
-               core::position2d<s32> p(event.MouseInput.X, event.MouseInput.Y);
-
-               if (event.MouseInput.Event == EMIE_MOUSE_WHEEL) {
-                       m_scrollbar->setPos(m_scrollbar->getPos() +
-                                       (event.MouseInput.Wheel < 0 ? -3 : 3) *
-                                       - (s32) m_rowheight / 2);
-                       return true;
-               }
-
-               // Find hovered row and cell
-               bool really_hovering = false;
-               s32 row_i = getRowAt(p.Y, really_hovering);
-               const Cell *cell = NULL;
-               if (really_hovering) {
-                       s32 cell_j = getCellAt(p.X, row_i);
-                       if (cell_j >= 0)
-                               cell = &(getRow(row_i)->cells[cell_j]);
-               }
-
-               // Update tooltip
-               setToolTipText(cell ? m_strings[cell->tooltip_index].c_str() : L"");
-
-               // Fix for #1567/#1806:
-               // IGUIScrollBar passes double click events to its parent,
-               // which we don't want. Detect this case and discard the event
-               if (event.MouseInput.Event != EMIE_MOUSE_MOVED &&
-                               m_scrollbar->isVisible() &&
-                               m_scrollbar->isPointInside(p))
-                       return true;
-
-               if (event.MouseInput.isLeftPressed() &&
-                               (isPointInside(p) ||
-                                event.MouseInput.Event == EMIE_MOUSE_MOVED)) {
-                       s32 sel_column = 0;
-                       bool sel_doubleclick = (event.MouseInput.Event
-                                       == EMIE_LMOUSE_DOUBLE_CLICK);
-                       bool plusminus_clicked = false;
-
-                       // For certain events (left click), report column
-                       // Also open/close subtrees when the +/- is clicked
-                       if (cell && (
-                                       event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN ||
-                                       event.MouseInput.Event == EMIE_LMOUSE_DOUBLE_CLICK ||
-                                       event.MouseInput.Event == EMIE_LMOUSE_TRIPLE_CLICK)) {
-                               sel_column = cell->reported_column;
-                               if (cell->content_type == COLUMN_TYPE_TREE)
-                                       plusminus_clicked = true;
-                       }
-
-                       if (plusminus_clicked) {
-                               if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
-                                       toggleVisibleTree(row_i, 0, false);
-                               }
-                       }
-                       else {
-                               // Normal selection
-                               s32 old_selected = m_selected;
-                               m_selected = row_i;
-                               autoScroll();
-
-                               if (m_selected != old_selected ||
-                                               sel_column >= 1 ||
-                                               sel_doubleclick) {
-                                       sendTableEvent(sel_column, sel_doubleclick);
-                               }
-
-                               // Treeview: double click opens/closes trees
-                               if (m_has_tree_column && sel_doubleclick) {
-                                       toggleVisibleTree(m_selected, 0, false);
-                               }
-                       }
-               }
-               return true;
-       }
-       if (event.EventType == EET_GUI_EVENT &&
-                       event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED &&
-                       event.GUIEvent.Caller == m_scrollbar) {
-               // Don't pass events from our scrollbar to the parent
-               return true;
-       }
-
-       return IGUIElement::OnEvent(event);
-}
-
-/******************************************************************************/
-/* GUITable helper functions                                                  */
-/******************************************************************************/
-
-s32 GUITable::allocString(const std::string &text)
-{
-       std::map<std::string, s32>::iterator it = m_alloc_strings.find(text);
-       if (it == m_alloc_strings.end()) {
-               s32 id = m_strings.size();
-               std::wstring wtext = utf8_to_wide(text);
-               m_strings.emplace_back(wtext.c_str());
-               m_alloc_strings.insert(std::make_pair(text, id));
-               return id;
-       }
-
-       return it->second;
-}
-
-s32 GUITable::allocImage(const std::string &imagename)
-{
-       std::map<std::string, s32>::iterator it = m_alloc_images.find(imagename);
-       if (it == m_alloc_images.end()) {
-               s32 id = m_images.size();
-               m_images.push_back(m_tsrc->getTexture(imagename));
-               m_alloc_images.insert(std::make_pair(imagename, id));
-               return id;
-       }
-
-       return it->second;
-}
-
-void GUITable::allocationComplete()
-{
-       // Called when done with creating rows and cells from table data,
-       // i.e. when allocString and allocImage won't be called anymore
-       m_alloc_strings.clear();
-       m_alloc_images.clear();
-}
-
-const GUITable::Row* GUITable::getRow(s32 i) const
-{
-       if (i >= 0 && i < (s32) m_visible_rows.size())
-               return &m_rows[m_visible_rows[i]];
-
-       return NULL;
-}
-
-bool GUITable::doesRowStartWith(const Row *row, const core::stringw &str) const
-{
-       if (row == NULL)
-               return false;
-
-       for (s32 j = 0; j < row->cellcount; ++j) {
-               Cell *cell = &row->cells[j];
-               if (cell->content_type == COLUMN_TYPE_TEXT) {
-                       const core::stringw &cellstr = m_strings[cell->content_index];
-                       if (cellstr.size() >= str.size() &&
-                                       str.equals_ignore_case(cellstr.subString(0, str.size())))
-                               return true;
-               }
-       }
-       return false;
-}
-
-s32 GUITable::getRowAt(s32 y, bool &really_hovering) const
-{
-       really_hovering = false;
-
-       s32 rowcount = m_visible_rows.size();
-       if (rowcount == 0)
-               return -1;
-
-       // Use arithmetic to find row
-       s32 rel_y = y - AbsoluteRect.UpperLeftCorner.Y - 1;
-       s32 i = (rel_y + m_scrollbar->getPos()) / m_rowheight;
-
-       if (i >= 0 && i < rowcount) {
-               really_hovering = true;
-               return i;
-       }
-       if (i < 0)
-               return 0;
-
-       return rowcount - 1;
-}
-
-s32 GUITable::getCellAt(s32 x, s32 row_i) const
-{
-       const Row *row = getRow(row_i);
-       if (row == NULL)
-               return -1;
-
-       // Use binary search to find cell in row
-       s32 rel_x = x - AbsoluteRect.UpperLeftCorner.X - 1;
-       s32 jmin = 0;
-       s32 jmax = row->cellcount - 1;
-       while (jmin < jmax) {
-               s32 pivot = jmin + (jmax - jmin) / 2;
-               assert(pivot >= 0 && pivot < row->cellcount);
-               const Cell *cell = &row->cells[pivot];
-
-               if (rel_x >= cell->xmin && rel_x <= cell->xmax)
-                       return pivot;
-
-               if (rel_x < cell->xmin)
-                       jmax = pivot - 1;
-               else
-                       jmin = pivot + 1;
-       }
-
-       if (jmin >= 0 && jmin < row->cellcount &&
-                       rel_x >= row->cells[jmin].xmin &&
-                       rel_x <= row->cells[jmin].xmax)
-               return jmin;
-
-       return -1;
-}
-
-void GUITable::autoScroll()
-{
-       if (m_selected >= 0) {
-               s32 pos = m_scrollbar->getPos();
-               s32 maxpos = m_selected * m_rowheight;
-               s32 minpos = maxpos - (AbsoluteRect.getHeight() - m_rowheight);
-               if (pos > maxpos)
-                       m_scrollbar->setPos(maxpos);
-               else if (pos < minpos)
-                       m_scrollbar->setPos(minpos);
-       }
-}
-
-void GUITable::updateScrollBar()
-{
-       s32 totalheight = m_rowheight * m_visible_rows.size();
-       s32 scrollmax = MYMAX(0, totalheight - AbsoluteRect.getHeight());
-       m_scrollbar->setVisible(scrollmax > 0);
-       m_scrollbar->setMax(scrollmax);
-       m_scrollbar->setSmallStep(m_rowheight);
-       m_scrollbar->setLargeStep(2 * m_rowheight);
-}
-
-void GUITable::sendTableEvent(s32 column, bool doubleclick)
-{
-       m_sel_column = column;
-       m_sel_doubleclick = doubleclick;
-       if (Parent) {
-               SEvent e;
-               memset(&e, 0, sizeof e);
-               e.EventType = EET_GUI_EVENT;
-               e.GUIEvent.Caller = this;
-               e.GUIEvent.Element = 0;
-               e.GUIEvent.EventType = gui::EGET_TABLE_CHANGED;
-               Parent->OnEvent(e);
-       }
-}
-
-void GUITable::getOpenedTrees(std::set<s32> &opened_trees) const
-{
-       opened_trees.clear();
-       s32 rowcount = m_rows.size();
-       for (s32 i = 0; i < rowcount - 1; ++i) {
-               if (m_rows[i].indent < m_rows[i+1].indent &&
-                               m_rows[i+1].visible_index != -2)
-                       opened_trees.insert(i);
-       }
-}
-
-void GUITable::setOpenedTrees(const std::set<s32> &opened_trees)
-{
-       s32 old_selected = -1;
-       if (m_selected >= 0)
-               old_selected = m_visible_rows[m_selected];
-
-       std::vector<s32> parents;
-       std::vector<s32> closed_parents;
-
-       m_visible_rows.clear();
-
-       for (size_t i = 0; i < m_rows.size(); ++i) {
-               Row *row = &m_rows[i];
-
-               // Update list of ancestors
-               while (!parents.empty() && m_rows[parents.back()].indent >= row->indent)
-                       parents.pop_back();
-               while (!closed_parents.empty() &&
-                               m_rows[closed_parents.back()].indent >= row->indent)
-                       closed_parents.pop_back();
-
-               assert(closed_parents.size() <= parents.size());
-
-               if (closed_parents.empty()) {
-                       // Visible row
-                       row->visible_index = m_visible_rows.size();
-                       m_visible_rows.push_back(i);
-               }
-               else if (parents.back() == closed_parents.back()) {
-                       // Invisible row, direct parent is closed
-                       row->visible_index = -2;
-               }
-               else {
-                       // Invisible row, direct parent is open, some ancestor is closed
-                       row->visible_index = -1;
-               }
-
-               // If not a leaf, add to parents list
-               if (i < m_rows.size()-1 && row->indent < m_rows[i+1].indent) {
-                       parents.push_back(i);
-
-                       s32 content_index = 0; // "-", open
-                       if (opened_trees.count(i) == 0) {
-                               closed_parents.push_back(i);
-                               content_index = 1; // "+", closed
-                       }
-
-                       // Update all cells of type "tree"
-                       for (s32 j = 0; j < row->cellcount; ++j)
-                               if (row->cells[j].content_type == COLUMN_TYPE_TREE)
-                                       row->cells[j].content_index = content_index;
-               }
-       }
-
-       updateScrollBar();
-
-       // m_selected must be updated since it is a visible row index
-       if (old_selected >= 0)
-               m_selected = m_rows[old_selected].visible_index;
-}
-
-void GUITable::openTree(s32 to_open)
-{
-       std::set<s32> opened_trees;
-       getOpenedTrees(opened_trees);
-       opened_trees.insert(to_open);
-       setOpenedTrees(opened_trees);
-}
-
-void GUITable::closeTree(s32 to_close)
-{
-       std::set<s32> opened_trees;
-       getOpenedTrees(opened_trees);
-       opened_trees.erase(to_close);
-       setOpenedTrees(opened_trees);
-}
-
-// The following function takes a visible row index (hidden rows skipped)
-// dir: -1 = left (close), 0 = auto (toggle), 1 = right (open)
-void GUITable::toggleVisibleTree(s32 row_i, int dir, bool move_selection)
-{
-       // Check if the chosen tree is currently open
-       const Row *row = getRow(row_i);
-       if (row == NULL)
-               return;
-
-       bool was_open = false;
-       for (s32 j = 0; j < row->cellcount; ++j) {
-               if (row->cells[j].content_type == COLUMN_TYPE_TREE) {
-                       was_open = row->cells[j].content_index == 0;
-                       break;
-               }
-       }
-
-       // Check if the chosen tree should be opened
-       bool do_open = !was_open;
-       if (dir < 0)
-               do_open = false;
-       else if (dir > 0)
-               do_open = true;
-
-       // Close or open the tree; the heavy lifting is done by setOpenedTrees
-       if (was_open && !do_open)
-               closeTree(m_visible_rows[row_i]);
-       else if (!was_open && do_open)
-               openTree(m_visible_rows[row_i]);
-
-       // Change selected row if requested by caller,
-       // this is useful for keyboard navigation
-       if (move_selection) {
-               s32 sel = row_i;
-               if (was_open && do_open) {
-                       // Move selection to first child
-                       const Row *maybe_child = getRow(sel + 1);
-                       if (maybe_child && maybe_child->indent > row->indent)
-                               sel++;
-               }
-               else if (!was_open && !do_open) {
-                       // Move selection to parent
-                       assert(getRow(sel) != NULL);
-                       while (sel > 0 && getRow(sel - 1)->indent >= row->indent)
-                               sel--;
-                       sel--;
-                       if (sel < 0)  // was root already selected?
-                               sel = row_i;
-               }
-               if (sel != m_selected) {
-                       m_selected = sel;
-                       autoScroll();
-                       sendTableEvent(0, false);
-               }
-       }
-}
-
-void GUITable::alignContent(Cell *cell, s32 xmax, s32 content_width, s32 align)
-{
-       // requires that cell.xmin, cell.xmax are properly set
-       // align = 0: left aligned, 1: centered, 2: right aligned, 3: inline
-       if (align == 0) {
-               cell->xpos = cell->xmin;
-               cell->xmax = xmax;
-       }
-       else if (align == 1) {
-               cell->xpos = (cell->xmin + xmax - content_width) / 2;
-               cell->xmax = xmax;
-       }
-       else if (align == 2) {
-               cell->xpos = xmax - content_width;
-               cell->xmax = xmax;
-       }
-       else {
-               // inline alignment: the cells of the column don't have an aligned
-               // right border, the right border of each cell depends on the content
-               cell->xpos = cell->xmin;
-               cell->xmax = cell->xmin + content_width;
-       }
-}
diff --git a/src/guiTable.h b/src/guiTable.h
deleted file mode 100644 (file)
index f9337ff..0000000
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-#include <iostream>
-
-#include "irrlichttypes_extrabloated.h"
-
-class ISimpleTextureSource;
-
-/*
-       A table GUI element for GUIFormSpecMenu.
-
-       Sends a EGET_TABLE_CHANGED event to the parent when
-       an item is selected or double-clicked.
-       Call checkEvent() to get info.
-
-       Credits: The interface and implementation of this class are (very)
-       loosely based on the Irrlicht classes CGUITable and CGUIListBox.
-       CGUITable and CGUIListBox are licensed under the Irrlicht license;
-       they are Copyright (C) 2002-2012 Nikolaus Gebhardt
-*/
-class GUITable : public gui::IGUIElement
-{
-public:
-       /*
-               Stores dynamic data that should be preserved
-               when updating a formspec
-       */
-       struct DynamicData
-       {
-               s32 selected = 0;
-               s32 scrollpos = 0;
-               s32 keynav_time = 0;
-               core::stringw keynav_buffer;
-               std::set<s32> opened_trees;
-       };
-
-       /*
-               An option of the form <name>=<value>
-       */
-       struct Option
-       {
-               std::string name;
-               std::string value;
-
-               Option(const std::string &name_, const std::string &value_) :
-                       name(name_),
-                       value(value_)
-               {}
-       };
-
-       /*
-               A list of options that concern the entire table
-       */
-       typedef std::vector<Option> TableOptions;
-
-       /*
-               A column with options
-       */
-       struct TableColumn
-       {
-               std::string type;
-               std::vector<Option> options;
-       };
-       typedef std::vector<TableColumn> TableColumns;
-
-
-       GUITable(gui::IGUIEnvironment *env,
-                       gui::IGUIElement *parent, s32 id,
-                       core::rect<s32> rectangle,
-                       ISimpleTextureSource *tsrc);
-
-       virtual ~GUITable();
-
-       /* Split a string of the form "name=value" into name and value */
-       static Option splitOption(const std::string &str);
-
-       /* Set textlist-like options, columns and data */
-       void setTextList(const std::vector<std::string> &content,
-                       bool transparent);
-
-       /* Set generic table options, columns and content */
-       // Adds empty strings to end of content if there is an incomplete row
-       void setTable(const TableOptions &options,
-                       const TableColumns &columns,
-                       std::vector<std::string> &content);
-
-       /* Clear the table */
-       void clear();
-
-       /* Get info about last event (string such as "CHG:1:2") */
-       // Call this after EGET_TABLE_CHANGED
-       std::string checkEvent();
-
-       /* Get index of currently selected row (first=1; 0 if none selected) */
-       s32 getSelected() const;
-
-       /* Set currently selected row (first=1; 0 if none selected) */
-       // If given index is not visible at the moment, select its parent
-       // Autoscroll to make the selected row fully visible
-       void setSelected(s32 index);
-
-       /* Get selection, scroll position and opened (sub)trees */
-       DynamicData getDynamicData() const;
-
-       /* Set selection, scroll position and opened (sub)trees */
-       void setDynamicData(const DynamicData &dyndata);
-
-       /* Returns "GUITable" */
-       virtual const c8* getTypeName() const;
-
-       /* Must be called when position or size changes */
-       virtual void updateAbsolutePosition();
-
-       /* Irrlicht draw method */
-       virtual void draw();
-
-       /* Irrlicht event handler */
-       virtual bool OnEvent(const SEvent &event);
-
-protected:
-       enum ColumnType {
-               COLUMN_TYPE_TEXT,
-               COLUMN_TYPE_IMAGE,
-               COLUMN_TYPE_COLOR,
-               COLUMN_TYPE_INDENT,
-               COLUMN_TYPE_TREE,
-       };
-
-       struct Cell {
-               s32 xmin;
-               s32 xmax;
-               s32 xpos;
-               ColumnType content_type;
-               s32 content_index;
-               s32 tooltip_index;
-               video::SColor color;
-               bool color_defined;
-               s32 reported_column;
-       };
-
-       struct Row {
-               Cell *cells;
-               s32 cellcount;
-               s32 indent;
-               // visible_index >= 0: is index of row in m_visible_rows
-               // visible_index == -1: parent open but other ancestor closed
-               // visible_index == -2: parent closed
-               s32 visible_index;
-       };
-
-       // Texture source
-       ISimpleTextureSource *m_tsrc;
-
-       // Table content (including hidden rows)
-       std::vector<Row> m_rows;
-       // Table content (only visible; indices into m_rows)
-       std::vector<s32> m_visible_rows;
-       bool m_is_textlist = false;
-       bool m_has_tree_column = false;
-
-       // Selection status
-       s32 m_selected = -1; // index of row (1...n), or 0 if none selected
-       s32 m_sel_column = 0;
-       bool m_sel_doubleclick = false;
-
-       // Keyboard navigation stuff
-       u64 m_keynav_time = 0;
-       core::stringw m_keynav_buffer = L"";
-
-       // Drawing and geometry information
-       bool m_border = true;
-       video::SColor m_color = video::SColor(255, 255, 255, 255);
-       video::SColor m_background = video::SColor(255, 0, 0, 0);
-       video::SColor m_highlight = video::SColor(255, 70, 100, 50);
-       video::SColor m_highlight_text = video::SColor(255, 255, 255, 255);
-       s32 m_rowheight = 1;
-       gui::IGUIFont *m_font = nullptr;
-       gui::IGUIScrollBar *m_scrollbar = nullptr;
-
-       // Allocated strings and images
-       std::vector<core::stringw> m_strings;
-       std::vector<video::ITexture*> m_images;
-       std::map<std::string, s32> m_alloc_strings;
-       std::map<std::string, s32> m_alloc_images;
-
-       s32 allocString(const std::string &text);
-       s32 allocImage(const std::string &imagename);
-       void allocationComplete();
-
-       // Helper for draw() that draws a single cell
-       void drawCell(const Cell *cell, video::SColor color,
-                       const core::rect<s32> &rowrect,
-                       const core::rect<s32> &client_clip);
-
-       // Returns the i-th visible row (NULL if i is invalid)
-       const Row *getRow(s32 i) const;
-
-       // Key navigation helper
-       bool doesRowStartWith(const Row *row, const core::stringw &str) const;
-
-       // Returns the row at a given screen Y coordinate
-       // Returns index i such that m_rows[i] is valid (or -1 on error)
-       s32 getRowAt(s32 y, bool &really_hovering) const;
-
-       // Returns the cell at a given screen X coordinate within m_rows[row_i]
-       // Returns index j such that m_rows[row_i].cells[j] is valid
-       // (or -1 on error)
-       s32 getCellAt(s32 x, s32 row_i) const;
-
-       // Make the selected row fully visible
-       void autoScroll();
-
-       // Should be called when m_rowcount or m_rowheight changes
-       void updateScrollBar();
-
-       // Sends EET_GUI_EVENT / EGET_TABLE_CHANGED to parent
-       void sendTableEvent(s32 column, bool doubleclick);
-
-       // Functions that help deal with hidden rows
-       // The following functions take raw row indices (hidden rows not skipped)
-       void getOpenedTrees(std::set<s32> &opened_trees) const;
-       void setOpenedTrees(const std::set<s32> &opened_trees);
-       void openTree(s32 to_open);
-       void closeTree(s32 to_close);
-       // The following function takes a visible row index (hidden rows skipped)
-       // dir: -1 = left (close), 0 = auto (toggle), 1 = right (open)
-       void toggleVisibleTree(s32 row_i, int dir, bool move_selection);
-
-       // Aligns cell content in column according to alignment specification
-       // align = 0: left aligned, 1: centered, 2: right aligned, 3: inline
-       static void alignContent(Cell *cell, s32 xmax, s32 content_width,
-                       s32 align);
-};
diff --git a/src/guiVolumeChange.cpp b/src/guiVolumeChange.cpp
deleted file mode 100644 (file)
index 8c46231..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
-Part of Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
-Copyright (C) 2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
-
-Permission to use, copy, modify, and distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#include "guiVolumeChange.h"
-#include "debug.h"
-#include "serialization.h"
-#include <string>
-#include <IGUICheckBox.h>
-#include <IGUIButton.h>
-#include <IGUIScrollBar.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-#include "settings.h"
-
-#include "gettext.h"
-
-const int ID_soundText = 263;
-const int ID_soundExitButton = 264;
-const int ID_soundSlider = 265;
-const int ID_soundMuteButton = 266;
-
-GUIVolumeChange::GUIVolumeChange(gui::IGUIEnvironment* env,
-               gui::IGUIElement* parent, s32 id,
-               IMenuManager *menumgr
-):
-       GUIModalMenu(env, parent, id, menumgr)
-{
-}
-
-GUIVolumeChange::~GUIVolumeChange()
-{
-       removeChildren();
-}
-
-void GUIVolumeChange::removeChildren()
-{
-       if (gui::IGUIElement *e = getElementFromId(ID_soundText))
-               e->remove();
-
-       if (gui::IGUIElement *e = getElementFromId(ID_soundExitButton))
-               e->remove();
-
-       if (gui::IGUIElement *e = getElementFromId(ID_soundSlider))
-               e->remove();
-}
-
-void GUIVolumeChange::regenerateGui(v2u32 screensize)
-{
-       /*
-               Remove stuff
-       */
-       removeChildren();
-
-       /*
-               Calculate new sizes and positions
-       */
-       DesiredRect = core::rect<s32>(
-               screensize.X/2 - 380/2,
-               screensize.Y/2 - 200/2,
-               screensize.X/2 + 380/2,
-               screensize.Y/2 + 200/2
-       );
-       recalculateAbsolutePosition(false);
-
-       v2s32 size = DesiredRect.getSize();
-       int volume = (int)(g_settings->getFloat("sound_volume") * 100);
-
-       /*
-               Add stuff
-       */
-       {
-               core::rect<s32> rect(0, 0, 160, 20);
-               rect = rect + v2s32(size.X / 2 - 80, size.Y / 2 - 70);
-
-               const wchar_t *text = wgettext("Sound Volume: ");
-               core::stringw volume_text = text;
-               delete [] text;
-
-               volume_text += core::stringw(volume) + core::stringw("%");
-               Environment->addStaticText(volume_text.c_str(), rect, false,
-                               true, this, ID_soundText);
-       }
-       {
-               core::rect<s32> rect(0, 0, 80, 30);
-               rect = rect + v2s32(size.X/2-80/2, size.Y/2+55);
-               const wchar_t *text = wgettext("Exit");
-               Environment->addButton(rect, this, ID_soundExitButton,
-                       text);
-               delete[] text;
-       }
-       {
-               core::rect<s32> rect(0, 0, 300, 20);
-               rect = rect + v2s32(size.X / 2 - 150, size.Y / 2);
-               gui::IGUIScrollBar *e = Environment->addScrollBar(true,
-                       rect, this, ID_soundSlider);
-               e->setMax(100);
-               e->setPos(volume);
-       }
-       {
-               core::rect<s32> rect(0, 0, 160, 20);
-               rect = rect + v2s32(size.X / 2 - 80, size.Y / 2 - 35);
-               const wchar_t *text = wgettext("Muted");
-               Environment->addCheckBox(g_settings->getBool("mute_sound"), rect, this,
-                               ID_soundMuteButton, text);
-               delete[] text;
-       }
-}
-
-void GUIVolumeChange::drawMenu()
-{
-       gui::IGUISkin* skin = Environment->getSkin();
-       if (!skin)
-               return;
-       video::IVideoDriver* driver = Environment->getVideoDriver();
-       video::SColor bgcolor(140, 0, 0, 0);
-       driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
-       gui::IGUIElement::draw();
-}
-
-bool GUIVolumeChange::OnEvent(const SEvent& event)
-{
-       if (event.EventType == EET_KEY_INPUT_EVENT) {
-               if (event.KeyInput.Key == KEY_ESCAPE && event.KeyInput.PressedDown) {
-                       quitMenu();
-                       return true;
-               }
-
-               if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
-                       quitMenu();
-                       return true;
-               }
-       } else if (event.EventType == EET_GUI_EVENT) {
-               if (event.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED) {
-                       gui::IGUIElement *e = getElementFromId(ID_soundMuteButton);
-                       if (e != NULL && e->getType() == gui::EGUIET_CHECK_BOX) {
-                               g_settings->setBool("mute_sound", ((gui::IGUICheckBox*)e)->isChecked());
-                       }
-
-                       Environment->setFocus(this);
-                       return true;
-               }
-
-               if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) {
-                       if (event.GUIEvent.Caller->getID() == ID_soundExitButton) {
-                               quitMenu();
-                               return true;
-                       }
-                       Environment->setFocus(this);
-               }
-
-               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_SCROLL_BAR_CHANGED) {
-                       if (event.GUIEvent.Caller->getID() == ID_soundSlider) {
-                               s32 pos = ((gui::IGUIScrollBar*)event.GUIEvent.Caller)->getPos();
-                               g_settings->setFloat("sound_volume", (float) pos / 100);
-
-                               gui::IGUIElement *e = getElementFromId(ID_soundText);
-                               const wchar_t *text = wgettext("Sound Volume: ");
-                               core::stringw volume_text = text;
-                               delete [] text;
-
-                               volume_text += core::stringw(pos) + core::stringw("%");
-                               e->setText(volume_text.c_str());
-                               return true;
-                       }
-               }
-
-       }
-
-       return Parent ? Parent->OnEvent(event) : false;
-}
-
diff --git a/src/guiVolumeChange.h b/src/guiVolumeChange.h
deleted file mode 100644 (file)
index 7c7e19a..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
-Part of Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2013 Ciaran Gultnieks <ciaran@ciarang.com>
-Copyright (C) 2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
-
-Permission to use, copy, modify, and distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-*/
-
-#pragma once
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include <string>
-
-class GUIVolumeChange : public GUIModalMenu
-{
-public:
-       GUIVolumeChange(gui::IGUIEnvironment* env,
-                       gui::IGUIElement* parent, s32 id,
-                       IMenuManager *menumgr);
-       ~GUIVolumeChange();
-
-       void removeChildren();
-       /*
-               Remove and re-add (or reposition) stuff
-       */
-       void regenerateGui(v2u32 screensize);
-
-       void drawMenu();
-
-       bool OnEvent(const SEvent& event);
-
-       bool pausesGame() { return true; }
-};
index 29beef16aba10991b98d4b0f8f6857b1832dd8ff..ecd2a521d9991de0eac5513bde9bd85b0ce68acc 100644 (file)
@@ -37,7 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "client/renderingengine.h"
 
 #ifdef HAVE_TOUCHSCREENGUI
-#include "touchscreengui.h"
+#include "gui/touchscreengui.h"
 #endif
 
 Hud::Hud(gui::IGUIEnvironment *guienv, Client *client, LocalPlayer *player,
diff --git a/src/intlGUIEditBox.cpp b/src/intlGUIEditBox.cpp
deleted file mode 100644 (file)
index 279e7a4..0000000
+++ /dev/null
@@ -1,1601 +0,0 @@
-// 11.11.2011 11:11 ValkaTR
-//
-// This is a copy of intlGUIEditBox from the irrlicht, but with a
-// fix in the OnEvent function, which doesn't allowed input of
-// other keyboard layouts than latin-1
-//
-// Characters like: ä ö ü õ ы й ю я ъ № € ° ...
-//
-// This fix is only needed for linux, because of a bug
-// in the CIrrDeviceLinux.cpp:1014-1015 of the irrlicht
-//
-// Also locale in the programm should not be changed to
-// a "C", "POSIX" or whatever, it should be set to "",
-// or XLookupString will return nothing for the international
-// characters.
-//
-// From the "man setlocale":
-//
-// On startup of the main program, the portable "C" locale
-// is selected as default.  A  program  may  be  made
-// portable to all locales by calling:
-//
-//           setlocale(LC_ALL, "");
-//
-//       after  program initialization....
-//
-
-// Copyright (C) 2002-2013 Nikolaus Gebhardt
-// This file is part of the "Irrlicht Engine".
-// For conditions of distribution and use, see copyright notice in irrlicht.h
-
-#include <util/numeric.h>
-#include "intlGUIEditBox.h"
-
-#if defined(_IRR_COMPILE_WITH_GUI_) && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
-
-#include "IGUISkin.h"
-#include "IGUIEnvironment.h"
-#include "IGUIFont.h"
-#include "IVideoDriver.h"
-//#include "rect.h"
-//#include "irrlicht/os.cpp"
-#include "porting.h"
-//#include "Keycodes.h"
-#include "log.h"
-
-/*
-       todo:
-       optional scrollbars
-       ctrl+left/right to select word
-       double click/ctrl click: word select + drag to select whole words, triple click to select line
-       optional? dragging selected text
-       numerical
-*/
-
-namespace irr
-{
-namespace gui
-{
-
-//! constructor
-intlGUIEditBox::intlGUIEditBox(const wchar_t* text, bool border,
-               IGUIEnvironment* environment, IGUIElement* parent, s32 id,
-               const core::rect<s32>& rectangle, bool writable, bool has_vscrollbar)
-       : IGUIEditBox(environment, parent, id, rectangle),
-       Border(border), FrameRect(rectangle),
-       m_scrollbar_width(0), m_vscrollbar(NULL), m_writable(writable)
-{
-       #ifdef _DEBUG
-       setDebugName("intlintlGUIEditBox");
-       #endif
-
-       Text = text;
-
-       if (Environment)
-               Operator = Environment->getOSOperator();
-
-       if (Operator)
-               Operator->grab();
-
-       // this element can be tabbed to
-       setTabStop(true);
-       setTabOrder(-1);
-
-       IGUISkin *skin = 0;
-       if (Environment)
-               skin = Environment->getSkin();
-       if (Border && skin)
-       {
-               FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
-               FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
-               FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
-               FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
-       }
-
-       if (skin && has_vscrollbar) {
-               m_scrollbar_width = skin->getSize(gui::EGDS_SCROLLBAR_SIZE);
-
-               if (m_scrollbar_width > 0) {
-                       createVScrollBar();
-               }
-       }
-
-       breakText();
-
-       calculateScrollPos();
-       setWritable(writable);
-}
-
-
-//! destructor
-intlGUIEditBox::~intlGUIEditBox()
-{
-       if (OverrideFont)
-               OverrideFont->drop();
-
-       if (Operator)
-               Operator->drop();
-}
-
-
-//! Sets another skin independent font.
-void intlGUIEditBox::setOverrideFont(IGUIFont* font)
-{
-       if (OverrideFont == font)
-               return;
-
-       if (OverrideFont)
-               OverrideFont->drop();
-
-       OverrideFont = font;
-
-       if (OverrideFont)
-               OverrideFont->grab();
-
-       breakText();
-}
-
-IGUIFont * intlGUIEditBox::getOverrideFont() const
-{
-       return OverrideFont;
-}
-
-//! Get the font which is used right now for drawing
-IGUIFont* intlGUIEditBox::getActiveFont() const
-{
-       if ( OverrideFont )
-               return OverrideFont;
-       IGUISkin* skin = Environment->getSkin();
-       if (skin)
-               return skin->getFont();
-       return 0;
-}
-
-//! Sets another color for the text.
-void intlGUIEditBox::setOverrideColor(video::SColor color)
-{
-       OverrideColor = color;
-       OverrideColorEnabled = true;
-}
-
-video::SColor intlGUIEditBox::getOverrideColor() const
-{
-       return OverrideColor;
-}
-
-//! Turns the border on or off
-void intlGUIEditBox::setDrawBorder(bool border)
-{
-       Border = border;
-}
-
-//! Sets whether to draw the background
-void intlGUIEditBox::setDrawBackground(bool draw)
-{
-}
-
-//! Sets if the text should use the overide color or the color in the gui skin.
-void intlGUIEditBox::enableOverrideColor(bool enable)
-{
-       OverrideColorEnabled = enable;
-}
-
-bool intlGUIEditBox::isOverrideColorEnabled() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return OverrideColorEnabled;
-}
-
-//! Enables or disables word wrap
-void intlGUIEditBox::setWordWrap(bool enable)
-{
-       WordWrap = enable;
-       breakText();
-}
-
-
-void intlGUIEditBox::updateAbsolutePosition()
-{
-    core::rect<s32> oldAbsoluteRect(AbsoluteRect);
-       IGUIElement::updateAbsolutePosition();
-       if ( oldAbsoluteRect != AbsoluteRect )
-       {
-        breakText();
-       }
-}
-
-
-//! Checks if word wrap is enabled
-bool intlGUIEditBox::isWordWrapEnabled() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return WordWrap;
-}
-
-
-//! Enables or disables newlines.
-void intlGUIEditBox::setMultiLine(bool enable)
-{
-       MultiLine = enable;
-}
-
-
-//! Checks if multi line editing is enabled
-bool intlGUIEditBox::isMultiLineEnabled() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return MultiLine;
-}
-
-
-void intlGUIEditBox::setPasswordBox(bool passwordBox, wchar_t passwordChar)
-{
-       PasswordBox = passwordBox;
-       if (PasswordBox)
-       {
-               PasswordChar = passwordChar;
-               setMultiLine(false);
-               setWordWrap(false);
-               BrokenText.clear();
-       }
-}
-
-
-bool intlGUIEditBox::isPasswordBox() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return PasswordBox;
-}
-
-
-//! Sets text justification
-void intlGUIEditBox::setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical)
-{
-       HAlign = horizontal;
-       VAlign = vertical;
-}
-
-
-//! called if an event happened.
-bool intlGUIEditBox::OnEvent(const SEvent& event)
-{
-       if (IsEnabled)
-       {
-
-               switch(event.EventType)
-               {
-               case EET_GUI_EVENT:
-                       if (event.GUIEvent.EventType == EGET_ELEMENT_FOCUS_LOST)
-                       {
-                               if (event.GUIEvent.Caller == this)
-                               {
-                                       MouseMarking = false;
-                                       setTextMarkers(0,0);
-                               }
-                       }
-                       break;
-               case EET_KEY_INPUT_EVENT:
-        {
-#if (defined(__linux__) || defined(__FreeBSD__))
-            // ################################################################
-                       // ValkaTR:
-            // This part is the difference from the original intlGUIEditBox
-            // It converts UTF-8 character into a UCS-2 (wchar_t)
-            wchar_t wc = L'_';
-            mbtowc( &wc, (char *) &event.KeyInput.Char, sizeof(event.KeyInput.Char) );
-
-            //printf( "char: %lc (%u)  \r\n", wc, wc );
-
-            SEvent irrevent(event);
-            irrevent.KeyInput.Char = wc;
-            // ################################################################
-
-                       if (processKey(irrevent))
-                               return true;
-#else
-                       if (processKey(event))
-                               return true;
-#endif // defined(linux)
-
-                       break;
-        }
-               case EET_MOUSE_INPUT_EVENT:
-                       if (processMouse(event))
-                               return true;
-                       break;
-               default:
-                       break;
-               }
-       }
-
-       return IGUIElement::OnEvent(event);
-}
-
-
-bool intlGUIEditBox::processKey(const SEvent& event)
-{
-       if (!event.KeyInput.PressedDown)
-               return false;
-
-       bool textChanged = false;
-       s32 newMarkBegin = MarkBegin;
-       s32 newMarkEnd = MarkEnd;
-
-       // control shortcut handling
-
-       if (event.KeyInput.Control)
-       {
-               // german backlash '\' entered with control + '?'
-               if ( event.KeyInput.Char == '\\' )
-               {
-                       inputChar(event.KeyInput.Char);
-                       return true;
-               }
-
-               switch(event.KeyInput.Key)
-               {
-               case KEY_KEY_A:
-                       // select all
-                       newMarkBegin = 0;
-                       newMarkEnd = Text.size();
-                       break;
-               case KEY_KEY_C:
-                       // copy to clipboard
-                       if (!PasswordBox && Operator && MarkBegin != MarkEnd)
-                       {
-                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
-                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
-                               core::stringc s;
-                               s = Text.subString(realmbgn, realmend - realmbgn).c_str();
-                               Operator->copyToClipboard(s.c_str());
-                       }
-                       break;
-               case KEY_KEY_X:
-                       // cut to the clipboard
-                       if (!PasswordBox && Operator && MarkBegin != MarkEnd)
-                       {
-                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
-                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
-                               // copy
-                               core::stringc sc;
-                               sc = Text.subString(realmbgn, realmend - realmbgn).c_str();
-                               Operator->copyToClipboard(sc.c_str());
-
-                               if (IsEnabled)
-                               {
-                                       // delete
-                                       core::stringw s;
-                                       s = Text.subString(0, realmbgn);
-                                       s.append( Text.subString(realmend, Text.size()-realmend) );
-                                       Text = s;
-
-                                       CursorPos = realmbgn;
-                                       newMarkBegin = 0;
-                                       newMarkEnd = 0;
-                                       textChanged = true;
-                               }
-                       }
-                       break;
-               case KEY_KEY_V:
-                       if ( !IsEnabled )
-                               break;
-
-                       // paste from the clipboard
-                       if (Operator)
-                       {
-                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
-                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
-                               // add new character
-                               const c8* p = Operator->getTextFromClipboard();
-                               if (p)
-                               {
-                                       if (MarkBegin == MarkEnd)
-                                       {
-                                               // insert text
-                                               core::stringw s = Text.subString(0, CursorPos);
-                                               s.append(p);
-                                               s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
-
-                                               if (!Max || s.size()<=Max) // thx to Fish FH for fix
-                                               {
-                                                       Text = s;
-                                                       s = p;
-                                                       CursorPos += s.size();
-                                               }
-                                       }
-                                       else
-                                       {
-                                               // replace text
-
-                                               core::stringw s = Text.subString(0, realmbgn);
-                                               s.append(p);
-                                               s.append( Text.subString(realmend, Text.size()-realmend) );
-
-                                               if (!Max || s.size()<=Max)  // thx to Fish FH for fix
-                                               {
-                                                       Text = s;
-                                                       s = p;
-                                                       CursorPos = realmbgn + s.size();
-                                               }
-                                       }
-                               }
-
-                               newMarkBegin = 0;
-                               newMarkEnd = 0;
-                               textChanged = true;
-                       }
-                       break;
-               case KEY_HOME:
-                       // move/highlight to start of text
-                       if (event.KeyInput.Shift)
-                       {
-                               newMarkEnd = CursorPos;
-                               newMarkBegin = 0;
-                               CursorPos = 0;
-                       }
-                       else
-                       {
-                               CursorPos = 0;
-                               newMarkBegin = 0;
-                               newMarkEnd = 0;
-                       }
-                       break;
-               case KEY_END:
-                       // move/highlight to end of text
-                       if (event.KeyInput.Shift)
-                       {
-                               newMarkBegin = CursorPos;
-                               newMarkEnd = Text.size();
-                               CursorPos = 0;
-                       }
-                       else
-                       {
-                               CursorPos = Text.size();
-                               newMarkBegin = 0;
-                               newMarkEnd = 0;
-                       }
-                       break;
-               default:
-                       return false;
-               }
-       }
-       // default keyboard handling
-       else
-       switch(event.KeyInput.Key)
-       {
-       case KEY_END:
-               {
-                       s32 p = Text.size();
-                       if (WordWrap || MultiLine)
-                       {
-                               p = getLineFromPos(CursorPos);
-                               p = BrokenTextPositions[p] + (s32)BrokenText[p].size();
-                               if (p > 0 && (Text[p-1] == L'\r' || Text[p-1] == L'\n' ))
-                                       p-=1;
-                       }
-
-                       if (event.KeyInput.Shift)
-                       {
-                               if (MarkBegin == MarkEnd)
-                                       newMarkBegin = CursorPos;
-
-                               newMarkEnd = p;
-                       }
-                       else
-                       {
-                               newMarkBegin = 0;
-                               newMarkEnd = 0;
-                       }
-                       CursorPos = p;
-                       BlinkStartTime = porting::getTimeMs();
-               }
-               break;
-       case KEY_HOME:
-               {
-
-                       s32 p = 0;
-                       if (WordWrap || MultiLine)
-                       {
-                               p = getLineFromPos(CursorPos);
-                               p = BrokenTextPositions[p];
-                       }
-
-                       if (event.KeyInput.Shift)
-                       {
-                               if (MarkBegin == MarkEnd)
-                                       newMarkBegin = CursorPos;
-                               newMarkEnd = p;
-                       }
-                       else
-                       {
-                               newMarkBegin = 0;
-                               newMarkEnd = 0;
-                       }
-                       CursorPos = p;
-                       BlinkStartTime = porting::getTimeMs();
-               }
-               break;
-       case KEY_RETURN:
-               if (MultiLine)
-               {
-                       inputChar(L'\n');
-                       return true;
-               }
-               else
-               {
-                   sendGuiEvent( EGET_EDITBOX_ENTER );
-               }
-               break;
-       case KEY_LEFT:
-
-               if (event.KeyInput.Shift)
-               {
-                       if (CursorPos > 0)
-                       {
-                               if (MarkBegin == MarkEnd)
-                                       newMarkBegin = CursorPos;
-
-                               newMarkEnd = CursorPos-1;
-                       }
-               }
-               else
-               {
-                       newMarkBegin = 0;
-                       newMarkEnd = 0;
-               }
-
-               if (CursorPos > 0) CursorPos--;
-               BlinkStartTime = porting::getTimeMs();
-               break;
-
-       case KEY_RIGHT:
-               if (event.KeyInput.Shift)
-               {
-                       if (Text.size() > (u32)CursorPos)
-                       {
-                               if (MarkBegin == MarkEnd)
-                                       newMarkBegin = CursorPos;
-
-                               newMarkEnd = CursorPos+1;
-                       }
-               }
-               else
-               {
-                       newMarkBegin = 0;
-                       newMarkEnd = 0;
-               }
-
-               if (Text.size() > (u32)CursorPos) CursorPos++;
-               BlinkStartTime = porting::getTimeMs();
-               break;
-       case KEY_UP:
-               if (MultiLine || (WordWrap && BrokenText.size() > 1) )
-               {
-                       s32 lineNo = getLineFromPos(CursorPos);
-                       s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin > MarkEnd ? MarkBegin : MarkEnd);
-                       if (lineNo > 0)
-                       {
-                               s32 cp = CursorPos - BrokenTextPositions[lineNo];
-                               if ((s32)BrokenText[lineNo-1].size() < cp)
-                                       CursorPos = BrokenTextPositions[lineNo-1] + (s32)BrokenText[lineNo-1].size()-1;
-                               else
-                                       CursorPos = BrokenTextPositions[lineNo-1] + cp;
-                       }
-
-                       if (event.KeyInput.Shift)
-                       {
-                               newMarkBegin = mb;
-                               newMarkEnd = CursorPos;
-                       }
-                       else
-                       {
-                               newMarkBegin = 0;
-                               newMarkEnd = 0;
-                       }
-
-               }
-               else
-               {
-                       return false;
-               }
-               break;
-       case KEY_DOWN:
-               if (MultiLine || (WordWrap && BrokenText.size() > 1) )
-               {
-                       s32 lineNo = getLineFromPos(CursorPos);
-                       s32 mb = (MarkBegin == MarkEnd) ? CursorPos : (MarkBegin < MarkEnd ? MarkBegin : MarkEnd);
-                       if (lineNo < (s32)BrokenText.size()-1)
-                       {
-                               s32 cp = CursorPos - BrokenTextPositions[lineNo];
-                               if ((s32)BrokenText[lineNo+1].size() < cp)
-                                       CursorPos = BrokenTextPositions[lineNo+1] + BrokenText[lineNo+1].size()-1;
-                               else
-                                       CursorPos = BrokenTextPositions[lineNo+1] + cp;
-                       }
-
-                       if (event.KeyInput.Shift)
-                       {
-                               newMarkBegin = mb;
-                               newMarkEnd = CursorPos;
-                       }
-                       else
-                       {
-                               newMarkBegin = 0;
-                               newMarkEnd = 0;
-                       }
-
-               }
-               else
-               {
-                       return false;
-               }
-               break;
-
-       case KEY_BACK:
-               if ( !this->IsEnabled )
-                       break;
-
-               if (!Text.empty()) {
-                       core::stringw s;
-
-                       if (MarkBegin != MarkEnd)
-                       {
-                               // delete marked text
-                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
-                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
-                               s = Text.subString(0, realmbgn);
-                               s.append( Text.subString(realmend, Text.size()-realmend) );
-                               Text = s;
-
-                               CursorPos = realmbgn;
-                       }
-                       else
-                       {
-                               // delete text behind cursor
-                               if (CursorPos>0)
-                                       s = Text.subString(0, CursorPos-1);
-                               else
-                                       s = L"";
-                               s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
-                               Text = s;
-                               --CursorPos;
-                       }
-
-                       if (CursorPos < 0)
-                               CursorPos = 0;
-                       BlinkStartTime = porting::getTimeMs();
-                       newMarkBegin = 0;
-                       newMarkEnd = 0;
-                       textChanged = true;
-               }
-               break;
-       case KEY_DELETE:
-               if ( !this->IsEnabled )
-                       break;
-
-               if (!Text.empty()) {
-                       core::stringw s;
-
-                       if (MarkBegin != MarkEnd)
-                       {
-                               // delete marked text
-                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
-                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
-                               s = Text.subString(0, realmbgn);
-                               s.append( Text.subString(realmend, Text.size()-realmend) );
-                               Text = s;
-
-                               CursorPos = realmbgn;
-                       }
-                       else
-                       {
-                               // delete text before cursor
-                               s = Text.subString(0, CursorPos);
-                               s.append( Text.subString(CursorPos+1, Text.size()-CursorPos-1) );
-                               Text = s;
-                       }
-
-                       if (CursorPos > (s32)Text.size())
-                               CursorPos = (s32)Text.size();
-
-                       BlinkStartTime = porting::getTimeMs();
-                       newMarkBegin = 0;
-                       newMarkEnd = 0;
-                       textChanged = true;
-               }
-               break;
-
-       case KEY_ESCAPE:
-       case KEY_TAB:
-       case KEY_SHIFT:
-       case KEY_F1:
-       case KEY_F2:
-       case KEY_F3:
-       case KEY_F4:
-       case KEY_F5:
-       case KEY_F6:
-       case KEY_F7:
-       case KEY_F8:
-       case KEY_F9:
-       case KEY_F10:
-       case KEY_F11:
-       case KEY_F12:
-       case KEY_F13:
-       case KEY_F14:
-       case KEY_F15:
-       case KEY_F16:
-       case KEY_F17:
-       case KEY_F18:
-       case KEY_F19:
-       case KEY_F20:
-       case KEY_F21:
-       case KEY_F22:
-       case KEY_F23:
-       case KEY_F24:
-               // ignore these keys
-               return false;
-
-       default:
-               inputChar(event.KeyInput.Char);
-               return true;
-       }
-
-    // Set new text markers
-    setTextMarkers( newMarkBegin, newMarkEnd );
-
-       // break the text if it has changed
-       if (textChanged)
-       {
-               breakText();
-               sendGuiEvent(EGET_EDITBOX_CHANGED);
-       }
-
-       calculateScrollPos();
-
-       return true;
-}
-
-
-//! draws the element and its children
-void intlGUIEditBox::draw()
-{
-       if (!IsVisible)
-               return;
-
-       const bool focus = Environment->hasFocus(this);
-
-       IGUISkin* skin = Environment->getSkin();
-       if (!skin)
-               return;
-
-       FrameRect = AbsoluteRect;
-
-       // draw the border
-
-       if (Border)
-       {
-               if (m_writable) {
-                       skin->draw3DSunkenPane(this, skin->getColor(EGDC_WINDOW),
-                               false, true, FrameRect, &AbsoluteClippingRect);
-               }
-
-               FrameRect.UpperLeftCorner.X += skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
-               FrameRect.UpperLeftCorner.Y += skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
-               FrameRect.LowerRightCorner.X -= skin->getSize(EGDS_TEXT_DISTANCE_X)+1;
-               FrameRect.LowerRightCorner.Y -= skin->getSize(EGDS_TEXT_DISTANCE_Y)+1;
-       }
-
-       updateVScrollBar();
-       core::rect<s32> localClipRect = FrameRect;
-       localClipRect.clipAgainst(AbsoluteClippingRect);
-
-       // draw the text
-
-       IGUIFont* font = OverrideFont;
-       if (!OverrideFont)
-               font = skin->getFont();
-
-       s32 cursorLine = 0;
-       s32 charcursorpos = 0;
-
-       if (font)
-       {
-               if (LastBreakFont != font)
-               {
-                       breakText();
-               }
-
-               // calculate cursor pos
-
-               core::stringw *txtLine = &Text;
-               s32 startPos = 0;
-
-               core::stringw s, s2;
-
-               // get mark position
-               const bool ml = (!PasswordBox && (WordWrap || MultiLine));
-               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
-               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-               const s32 hlineStart = ml ? getLineFromPos(realmbgn) : 0;
-               const s32 hlineCount = ml ? getLineFromPos(realmend) - hlineStart + 1 : 1;
-               const s32 lineCount = ml ? BrokenText.size() : 1;
-
-               // Save the override color information.
-               // Then, alter it if the edit box is disabled.
-               const bool prevOver = OverrideColorEnabled;
-               const video::SColor prevColor = OverrideColor;
-
-               if (!Text.empty()) {
-                       if (!IsEnabled && !OverrideColorEnabled)
-                       {
-                               OverrideColorEnabled = true;
-                               OverrideColor = skin->getColor(EGDC_GRAY_TEXT);
-                       }
-
-                       for (s32 i=0; i < lineCount; ++i)
-                       {
-                               setTextRect(i);
-
-                               // clipping test - don't draw anything outside the visible area
-                               core::rect<s32> c = localClipRect;
-                               c.clipAgainst(CurrentTextRect);
-                               if (!c.isValid())
-                                       continue;
-
-                               // get current line
-                               if (PasswordBox)
-                               {
-                                       if (BrokenText.size() != 1)
-                                       {
-                                               BrokenText.clear();
-                                               BrokenText.push_back(core::stringw());
-                                       }
-                                       if (BrokenText[0].size() != Text.size())
-                                       {
-                                               BrokenText[0] = Text;
-                                               for (u32 q = 0; q < Text.size(); ++q)
-                                               {
-                                                       BrokenText[0] [q] = PasswordChar;
-                                               }
-                                       }
-                                       txtLine = &BrokenText[0];
-                                       startPos = 0;
-                               }
-                               else
-                               {
-                                       txtLine = ml ? &BrokenText[i] : &Text;
-                                       startPos = ml ? BrokenTextPositions[i] : 0;
-                               }
-
-
-                               // draw normal text
-                               font->draw(txtLine->c_str(), CurrentTextRect,
-                                       OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
-                                       false, true, &localClipRect);
-
-                               // draw mark and marked text
-                               if (focus && MarkBegin != MarkEnd && i >= hlineStart && i < hlineStart + hlineCount)
-                               {
-
-                                       s32 mbegin = 0, mend = 0;
-                                       s32 lineStartPos = 0, lineEndPos = txtLine->size();
-
-                                       if (i == hlineStart)
-                                       {
-                                               // highlight start is on this line
-                                               s = txtLine->subString(0, realmbgn - startPos);
-                                               mbegin = font->getDimension(s.c_str()).Width;
-
-                                               // deal with kerning
-                                               mbegin += font->getKerningWidth(
-                                                       &((*txtLine)[realmbgn - startPos]),
-                                                       realmbgn - startPos > 0 ? &((*txtLine)[realmbgn - startPos - 1]) : 0);
-
-                                               lineStartPos = realmbgn - startPos;
-                                       }
-                                       if (i == hlineStart + hlineCount - 1)
-                                       {
-                                               // highlight end is on this line
-                                               s2 = txtLine->subString(0, realmend - startPos);
-                                               mend = font->getDimension(s2.c_str()).Width;
-                                               lineEndPos = (s32)s2.size();
-                                       }
-                                       else
-                                               mend = font->getDimension(txtLine->c_str()).Width;
-
-                                       CurrentTextRect.UpperLeftCorner.X += mbegin;
-                                       CurrentTextRect.LowerRightCorner.X = CurrentTextRect.UpperLeftCorner.X + mend - mbegin;
-
-                                       // draw mark
-                                       skin->draw2DRectangle(this, skin->getColor(EGDC_HIGH_LIGHT), CurrentTextRect, &localClipRect);
-
-                                       // draw marked text
-                                       s = txtLine->subString(lineStartPos, lineEndPos - lineStartPos);
-
-                                       if (!s.empty())
-                                               font->draw(s.c_str(), CurrentTextRect,
-                                                       OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_HIGH_LIGHT_TEXT),
-                                                       false, true, &localClipRect);
-
-                               }
-                       }
-
-                       // Return the override color information to its previous settings.
-                       OverrideColorEnabled = prevOver;
-                       OverrideColor = prevColor;
-               }
-
-               // draw cursor
-
-               if (WordWrap || MultiLine)
-               {
-                       cursorLine = getLineFromPos(CursorPos);
-                       txtLine = &BrokenText[cursorLine];
-                       startPos = BrokenTextPositions[cursorLine];
-               }
-               s = txtLine->subString(0,CursorPos-startPos);
-               charcursorpos = font->getDimension(s.c_str()).Width +
-                       font->getKerningWidth(L"_", CursorPos-startPos > 0 ? &((*txtLine)[CursorPos-startPos-1]) : 0);
-
-               if (m_writable) {
-                       if (focus && (porting::getTimeMs() - BlinkStartTime) % 700 < 350) {
-                               setTextRect(cursorLine);
-                               CurrentTextRect.UpperLeftCorner.X += charcursorpos;
-
-                               font->draw(L"_", CurrentTextRect,
-                                       OverrideColorEnabled ? OverrideColor : skin->getColor(EGDC_BUTTON_TEXT),
-                                       false, true, &localClipRect);
-                       }
-               }
-       }
-
-       // draw children
-       IGUIElement::draw();
-}
-
-
-//! Sets the new caption of this element.
-void intlGUIEditBox::setText(const wchar_t* text)
-{
-       Text = text;
-       if (u32(CursorPos) > Text.size())
-               CursorPos = Text.size();
-       HScrollPos = 0;
-       breakText();
-}
-
-
-//! Enables or disables automatic scrolling with cursor position
-//! \param enable: If set to true, the text will move around with the cursor position
-void intlGUIEditBox::setAutoScroll(bool enable)
-{
-       AutoScroll = enable;
-}
-
-
-//! Checks to see if automatic scrolling is enabled
-//! \return true if automatic scrolling is enabled, false if not
-bool intlGUIEditBox::isAutoScrollEnabled() const
-{
-       _IRR_IMPLEMENT_MANAGED_MARSHALLING_BUGFIX;
-       return AutoScroll;
-}
-
-
-//! Gets the area of the text in the edit box
-//! \return Returns the size in pixels of the text
-core::dimension2du intlGUIEditBox::getTextDimension()
-{
-       core::rect<s32> ret;
-
-       setTextRect(0);
-       ret = CurrentTextRect;
-
-       for (u32 i=1; i < BrokenText.size(); ++i)
-       {
-               setTextRect(i);
-               ret.addInternalPoint(CurrentTextRect.UpperLeftCorner);
-               ret.addInternalPoint(CurrentTextRect.LowerRightCorner);
-       }
-
-       return core::dimension2du(ret.getSize());
-}
-
-
-//! Sets the maximum amount of characters which may be entered in the box.
-//! \param max: Maximum amount of characters. If 0, the character amount is
-//! infinity.
-void intlGUIEditBox::setMax(u32 max)
-{
-       Max = max;
-
-       if (Text.size() > Max && Max != 0)
-               Text = Text.subString(0, Max);
-}
-
-
-//! Returns maximum amount of characters, previously set by setMax();
-u32 intlGUIEditBox::getMax() const
-{
-       return Max;
-}
-
-
-bool intlGUIEditBox::processMouse(const SEvent& event)
-{
-       switch(event.MouseInput.Event)
-       {
-       case irr::EMIE_LMOUSE_LEFT_UP:
-               if (Environment->hasFocus(this))
-               {
-                       CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-                       if (MouseMarking)
-                       {
-                           setTextMarkers( MarkBegin, CursorPos );
-                       }
-                       MouseMarking = false;
-                       calculateScrollPos();
-                       return true;
-               }
-               break;
-       case irr::EMIE_MOUSE_MOVED:
-               {
-                       if (MouseMarking)
-                       {
-                               CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-                               setTextMarkers( MarkBegin, CursorPos );
-                               calculateScrollPos();
-                               return true;
-                       }
-               }
-               break;
-       case EMIE_LMOUSE_PRESSED_DOWN:
-               if (!Environment->hasFocus(this))
-               {
-                       BlinkStartTime = porting::getTimeMs();
-                       MouseMarking = true;
-                       CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-                       setTextMarkers(CursorPos, CursorPos );
-                       calculateScrollPos();
-                       return true;
-               }
-               else
-               {
-                       if (!AbsoluteClippingRect.isPointInside(
-                               core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y))) {
-                               return false;
-                       }
-
-
-                       // move cursor
-                       CursorPos = getCursorPos(event.MouseInput.X, event.MouseInput.Y);
-
-                       s32 newMarkBegin = MarkBegin;
-                       if (!MouseMarking)
-                               newMarkBegin = CursorPos;
-
-                       MouseMarking = true;
-                       setTextMarkers( newMarkBegin, CursorPos);
-                       calculateScrollPos();
-                       return true;
-               }
-               break;
-       case EMIE_MOUSE_WHEEL:
-               if (m_vscrollbar) {
-                       s32 pos = m_vscrollbar->getPos();
-                       s32 step = m_vscrollbar->getSmallStep();
-                       m_vscrollbar->setPos(pos - event.MouseInput.Wheel * step);
-               }
-               break;
-       default:
-               break;
-       }
-
-       return false;
-}
-
-
-s32 intlGUIEditBox::getCursorPos(s32 x, s32 y)
-{
-       IGUIFont* font = OverrideFont;
-       IGUISkin* skin = Environment->getSkin();
-       if (!OverrideFont)
-               font = skin->getFont();
-
-       const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
-
-       core::stringw *txtLine = NULL;
-       s32 startPos = 0;
-       u32 curr_line_idx = 0;
-       x += 3;
-
-       for (; curr_line_idx < lineCount; ++curr_line_idx) {
-               setTextRect(curr_line_idx);
-               if (curr_line_idx == 0 && y < CurrentTextRect.UpperLeftCorner.Y)
-                       y = CurrentTextRect.UpperLeftCorner.Y;
-               if (curr_line_idx == lineCount - 1 && y > CurrentTextRect.LowerRightCorner.Y)
-                       y = CurrentTextRect.LowerRightCorner.Y;
-
-               // is it inside this region?
-               if (y >= CurrentTextRect.UpperLeftCorner.Y && y <= CurrentTextRect.LowerRightCorner.Y) {
-                       // we've found the clicked line
-                       txtLine = (WordWrap || MultiLine) ? &BrokenText[curr_line_idx] : &Text;
-                       startPos = (WordWrap || MultiLine) ? BrokenTextPositions[curr_line_idx] : 0;
-                       break;
-               }
-       }
-
-       if (x < CurrentTextRect.UpperLeftCorner.X)
-               x = CurrentTextRect.UpperLeftCorner.X;
-       else if (x > CurrentTextRect.LowerRightCorner.X)
-               x = CurrentTextRect.LowerRightCorner.X;
-
-       s32 idx = font->getCharacterFromPos(txtLine->c_str(), x - CurrentTextRect.UpperLeftCorner.X);
-       // Special handling for last line, if we are on limits, add 1 extra shift because idx
-       // will be the last char, not null char of the wstring
-       if (curr_line_idx == lineCount - 1 && x == CurrentTextRect.LowerRightCorner.X)
-               idx++;
-
-       return rangelim(idx + startPos, 0, S32_MAX);
-}
-
-
-//! Breaks the single text line.
-void intlGUIEditBox::breakText()
-{
-       IGUISkin* skin = Environment->getSkin();
-
-       if ((!WordWrap && !MultiLine) || !skin)
-               return;
-
-       BrokenText.clear(); // need to reallocate :/
-       BrokenTextPositions.set_used(0);
-
-       IGUIFont* font = OverrideFont;
-       if (!OverrideFont)
-               font = skin->getFont();
-
-       if (!font)
-               return;
-
-       LastBreakFont = font;
-
-       core::stringw line;
-       core::stringw word;
-       core::stringw whitespace;
-       s32 lastLineStart = 0;
-       s32 size = Text.size();
-       s32 length = 0;
-       s32 elWidth = RelativeRect.getWidth() - 6;
-       wchar_t c;
-
-       for (s32 i=0; i<size; ++i)
-       {
-               c = Text[i];
-               bool lineBreak = false;
-
-               if (c == L'\r') // Mac or Windows breaks
-               {
-                       lineBreak = true;
-                       c = ' ';
-                       if (Text[i+1] == L'\n') // Windows breaks
-                       {
-                               Text.erase(i+1);
-                               --size;
-                       }
-               }
-               else if (c == L'\n') // Unix breaks
-               {
-                       lineBreak = true;
-                       c = ' ';
-               }
-
-               // don't break if we're not a multi-line edit box
-               if (!MultiLine)
-                       lineBreak = false;
-
-               if (c == L' ' || c == 0 || i == (size-1))
-               {
-                       if (!word.empty()) {
-                               // here comes the next whitespace, look if
-                               // we can break the last word to the next line.
-                               s32 whitelgth = font->getDimension(whitespace.c_str()).Width;
-                               s32 worldlgth = font->getDimension(word.c_str()).Width;
-
-                               if (WordWrap && length + worldlgth + whitelgth > elWidth)
-                               {
-                                       // break to next line
-                                       length = worldlgth;
-                                       BrokenText.push_back(line);
-                                       BrokenTextPositions.push_back(lastLineStart);
-                                       lastLineStart = i - (s32)word.size();
-                                       line = word;
-                               }
-                               else
-                               {
-                                       // add word to line
-                                       line += whitespace;
-                                       line += word;
-                                       length += whitelgth + worldlgth;
-                               }
-
-                               word = L"";
-                               whitespace = L"";
-                       }
-
-                       whitespace += c;
-
-                       // compute line break
-                       if (lineBreak)
-                       {
-                               line += whitespace;
-                               line += word;
-                               BrokenText.push_back(line);
-                               BrokenTextPositions.push_back(lastLineStart);
-                               lastLineStart = i+1;
-                               line = L"";
-                               word = L"";
-                               whitespace = L"";
-                               length = 0;
-                       }
-               }
-               else
-               {
-                       // yippee this is a word..
-                       word += c;
-               }
-       }
-
-       line += whitespace;
-       line += word;
-       BrokenText.push_back(line);
-       BrokenTextPositions.push_back(lastLineStart);
-}
-
-
-void intlGUIEditBox::setTextRect(s32 line)
-{
-       core::dimension2du d;
-
-       IGUISkin* skin = Environment->getSkin();
-       if (!skin)
-               return;
-
-       IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
-
-       if (!font)
-               return;
-
-       // get text dimension
-       const u32 lineCount = (WordWrap || MultiLine) ? BrokenText.size() : 1;
-       if (WordWrap || MultiLine)
-       {
-               d = font->getDimension(BrokenText[line].c_str());
-       }
-       else
-       {
-               d = font->getDimension(Text.c_str());
-               d.Height = AbsoluteRect.getHeight();
-       }
-       d.Height += font->getKerningHeight();
-
-       // justification
-       switch (HAlign)
-       {
-       case EGUIA_CENTER:
-               // align to h centre
-               CurrentTextRect.UpperLeftCorner.X = (FrameRect.getWidth()/2) - (d.Width/2);
-               CurrentTextRect.LowerRightCorner.X = (FrameRect.getWidth()/2) + (d.Width/2);
-               break;
-       case EGUIA_LOWERRIGHT:
-               // align to right edge
-               CurrentTextRect.UpperLeftCorner.X = FrameRect.getWidth() - d.Width;
-               CurrentTextRect.LowerRightCorner.X = FrameRect.getWidth();
-               break;
-       default:
-               // align to left edge
-               CurrentTextRect.UpperLeftCorner.X = 0;
-               CurrentTextRect.LowerRightCorner.X = d.Width;
-
-       }
-
-       switch (VAlign)
-       {
-       case EGUIA_CENTER:
-               // align to v centre
-               CurrentTextRect.UpperLeftCorner.Y =
-                       (FrameRect.getHeight()/2) - (lineCount*d.Height)/2 + d.Height*line;
-               break;
-       case EGUIA_LOWERRIGHT:
-               // align to bottom edge
-               CurrentTextRect.UpperLeftCorner.Y =
-                       FrameRect.getHeight() - lineCount*d.Height + d.Height*line;
-               break;
-       default:
-               // align to top edge
-               CurrentTextRect.UpperLeftCorner.Y = d.Height*line;
-               break;
-       }
-
-       CurrentTextRect.UpperLeftCorner.X  -= HScrollPos;
-       CurrentTextRect.LowerRightCorner.X -= HScrollPos;
-       CurrentTextRect.UpperLeftCorner.Y  -= VScrollPos;
-       CurrentTextRect.LowerRightCorner.Y = CurrentTextRect.UpperLeftCorner.Y + d.Height;
-
-       CurrentTextRect += FrameRect.UpperLeftCorner;
-
-}
-
-
-s32 intlGUIEditBox::getLineFromPos(s32 pos)
-{
-       if (!WordWrap && !MultiLine)
-               return 0;
-
-       s32 i=0;
-       while (i < (s32)BrokenTextPositions.size())
-       {
-               if (BrokenTextPositions[i] > pos)
-                       return i-1;
-               ++i;
-       }
-       return (s32)BrokenTextPositions.size() - 1;
-}
-
-
-void intlGUIEditBox::inputChar(wchar_t c)
-{
-       if (!IsEnabled)
-               return;
-
-       if (c != 0)
-       {
-               if (Text.size() < Max || Max == 0)
-               {
-                       core::stringw s;
-
-                       if (MarkBegin != MarkEnd)
-                       {
-                               // replace marked text
-                               const s32 realmbgn = MarkBegin < MarkEnd ? MarkBegin : MarkEnd;
-                               const s32 realmend = MarkBegin < MarkEnd ? MarkEnd : MarkBegin;
-
-                               s = Text.subString(0, realmbgn);
-                               s.append(c);
-                               s.append( Text.subString(realmend, Text.size()-realmend) );
-                               Text = s;
-                               CursorPos = realmbgn+1;
-                       }
-                       else
-                       {
-                               // add new character
-                               s = Text.subString(0, CursorPos);
-                               s.append(c);
-                               s.append( Text.subString(CursorPos, Text.size()-CursorPos) );
-                               Text = s;
-                               ++CursorPos;
-                       }
-
-                       BlinkStartTime = porting::getTimeMs();
-                       setTextMarkers(0, 0);
-               }
-       }
-       breakText();
-       sendGuiEvent(EGET_EDITBOX_CHANGED);
-       calculateScrollPos();
-}
-
-
-void intlGUIEditBox::calculateScrollPos()
-{
-       if (!AutoScroll)
-               return;
-
-       // calculate horizontal scroll position
-       s32 cursLine = getLineFromPos(CursorPos);
-       setTextRect(cursLine);
-
-       // don't do horizontal scrolling when wordwrap is enabled.
-       if (!WordWrap)
-       {
-               // get cursor position
-               IGUISkin* skin = Environment->getSkin();
-               if (!skin)
-                       return;
-               IGUIFont* font = OverrideFont ? OverrideFont : skin->getFont();
-               if (!font)
-                       return;
-
-               core::stringw *txtLine = MultiLine ? &BrokenText[cursLine] : &Text;
-               s32 cPos = MultiLine ? CursorPos - BrokenTextPositions[cursLine] : CursorPos;
-
-               s32 cStart = CurrentTextRect.UpperLeftCorner.X + HScrollPos +
-                       font->getDimension(txtLine->subString(0, cPos).c_str()).Width;
-
-               s32 cEnd = cStart + font->getDimension(L"_ ").Width;
-
-               if (FrameRect.LowerRightCorner.X < cEnd)
-                       HScrollPos = cEnd - FrameRect.LowerRightCorner.X;
-               else if (FrameRect.UpperLeftCorner.X > cStart)
-                       HScrollPos = cStart - FrameRect.UpperLeftCorner.X;
-               else
-                       HScrollPos = 0;
-
-               // todo: adjust scrollbar
-       }
-
-       // vertical scroll position
-       if (FrameRect.LowerRightCorner.Y < CurrentTextRect.LowerRightCorner.Y + VScrollPos)
-               VScrollPos = CurrentTextRect.LowerRightCorner.Y - FrameRect.LowerRightCorner.Y + VScrollPos;
-
-       else if (FrameRect.UpperLeftCorner.Y > CurrentTextRect.UpperLeftCorner.Y + VScrollPos)
-               VScrollPos = CurrentTextRect.UpperLeftCorner.Y - FrameRect.UpperLeftCorner.Y + VScrollPos;
-       else
-               VScrollPos = 0;
-
-       // todo: adjust scrollbar
-       if (m_vscrollbar)
-               m_vscrollbar->setPos(VScrollPos);
-}
-
-//! set text markers
-void intlGUIEditBox::setTextMarkers(s32 begin, s32 end)
-{
-    if ( begin != MarkBegin || end != MarkEnd )
-    {
-        MarkBegin = begin;
-        MarkEnd = end;
-        sendGuiEvent(EGET_EDITBOX_MARKING_CHANGED);
-    }
-}
-
-//! send some gui event to parent
-void intlGUIEditBox::sendGuiEvent(EGUI_EVENT_TYPE type)
-{
-       if ( Parent )
-       {
-        SEvent e;
-        e.EventType = EET_GUI_EVENT;
-        e.GUIEvent.Caller = this;
-        e.GUIEvent.Element = 0;
-        e.GUIEvent.EventType = type;
-
-        Parent->OnEvent(e);
-       }
-}
-
-//! Create a vertical scrollbar
-void intlGUIEditBox::createVScrollBar()
-{
-       s32 fontHeight = 1;
-
-       if (OverrideFont) {
-               fontHeight = OverrideFont->getDimension(L"").Height;
-       } else {
-               if (IGUISkin* skin = Environment->getSkin()) {
-                       if (IGUIFont* font = skin->getFont()) {
-                               fontHeight = font->getDimension(L"").Height;
-                       }
-               }
-       }
-
-       irr::core::rect<s32> scrollbarrect = FrameRect;
-       scrollbarrect.UpperLeftCorner.X += FrameRect.getWidth() - m_scrollbar_width;
-       m_vscrollbar = Environment->addScrollBar(false, scrollbarrect, getParent(), getID());
-       m_vscrollbar->setVisible(false);
-       m_vscrollbar->setSmallStep(3 * fontHeight);
-       m_vscrollbar->setLargeStep(10 * fontHeight);
-}
-
-//! Update the vertical scrollbar (visibilty & scroll position)
-void intlGUIEditBox::updateVScrollBar()
-{
-       if (!m_vscrollbar)
-               return;
-
-       // OnScrollBarChanged(...)
-       if (m_vscrollbar->getPos() != VScrollPos) {
-               s32 deltaScrollY = m_vscrollbar->getPos() - VScrollPos;
-               CurrentTextRect.UpperLeftCorner.Y -= deltaScrollY;
-               CurrentTextRect.LowerRightCorner.Y -= deltaScrollY;
-
-               s32 scrollymax = getTextDimension().Height - FrameRect.getHeight();
-               if (scrollymax != m_vscrollbar->getMax()) {
-                       // manage a newline or a deleted line
-                       m_vscrollbar->setMax(scrollymax);
-                       calculateScrollPos();
-               } else {
-                       // manage a newline or a deleted line
-                       VScrollPos = m_vscrollbar->getPos();
-               }
-       }
-
-       // check if a vertical scrollbar is needed ?
-       if (getTextDimension().Height > (u32) FrameRect.getHeight()) {
-               s32 scrollymax = getTextDimension().Height - FrameRect.getHeight();
-               if (scrollymax != m_vscrollbar->getMax()) {
-                       m_vscrollbar->setMax(scrollymax);
-               }
-
-               if (!m_vscrollbar->isVisible() && MultiLine) {
-                       AbsoluteRect.LowerRightCorner.X -= m_scrollbar_width;
-
-                       m_vscrollbar->setVisible(true);
-               }
-       } else {
-               if (m_vscrollbar->isVisible()) {
-                       AbsoluteRect.LowerRightCorner.X += m_scrollbar_width;
-
-                       VScrollPos = 0;
-                       m_vscrollbar->setPos(0);
-                       m_vscrollbar->setMax(1);
-                       m_vscrollbar->setVisible(false);
-               }
-       }
-}
-
-void intlGUIEditBox::setWritable(bool can_write_text)
-{
-       m_writable = can_write_text;
-}
-
-//! Writes attributes of the element.
-void intlGUIEditBox::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options=0) const
-{
-       // IGUIEditBox::serializeAttributes(out,options);
-
-       out->addBool  ("OverrideColorEnabled",OverrideColorEnabled );
-       out->addColor ("OverrideColor",       OverrideColor);
-       // out->addFont("OverrideFont",OverrideFont);
-       out->addInt   ("MaxChars",            Max);
-       out->addBool  ("WordWrap",            WordWrap);
-       out->addBool  ("MultiLine",           MultiLine);
-       out->addBool  ("AutoScroll",          AutoScroll);
-       out->addBool  ("PasswordBox",         PasswordBox);
-       core::stringw ch = L" ";
-       ch[0] = PasswordChar;
-       out->addString("PasswordChar",        ch.c_str());
-       out->addEnum  ("HTextAlign",          HAlign, GUIAlignmentNames);
-       out->addEnum  ("VTextAlign",          VAlign, GUIAlignmentNames);
-       out->addBool  ("Writable",            m_writable);
-
-       IGUIEditBox::serializeAttributes(out,options);
-}
-
-
-//! Reads attributes of the element
-void intlGUIEditBox::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options=0)
-{
-       IGUIEditBox::deserializeAttributes(in,options);
-
-       setOverrideColor(in->getAttributeAsColor("OverrideColor"));
-       enableOverrideColor(in->getAttributeAsBool("OverrideColorEnabled"));
-       setMax(in->getAttributeAsInt("MaxChars"));
-       setWordWrap(in->getAttributeAsBool("WordWrap"));
-       setMultiLine(in->getAttributeAsBool("MultiLine"));
-       setAutoScroll(in->getAttributeAsBool("AutoScroll"));
-       core::stringw ch = in->getAttributeAsStringW("PasswordChar");
-
-       if (ch.empty())
-               setPasswordBox(in->getAttributeAsBool("PasswordBox"));
-       else
-               setPasswordBox(in->getAttributeAsBool("PasswordBox"), ch[0]);
-
-       setTextAlignment( (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("HTextAlign", GUIAlignmentNames),
-                       (EGUI_ALIGNMENT) in->getAttributeAsEnumeration("VTextAlign", GUIAlignmentNames));
-
-       setWritable(in->getAttributeAsBool("Writable"));
-       // setOverrideFont(in->getAttributeAsFont("OverrideFont"));
-}
-
-
-} // end namespace gui
-} // end namespace irr
-
-#endif // _IRR_COMPILE_WITH_GUI_
diff --git a/src/intlGUIEditBox.h b/src/intlGUIEditBox.h
deleted file mode 100644 (file)
index aa35e2e..0000000
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright (C) 2002-2013 Nikolaus Gebhardt
-// This file is part of the "Irrlicht Engine".
-// For conditions of distribution and use, see copyright notice in irrlicht.h
-
-#pragma once
-
-#include "IrrCompileConfig.h"
-//#ifdef _IRR_COMPILE_WITH_GUI_
-
-#include "IGUIEditBox.h"
-#include "irrArray.h"
-#include "IOSOperator.h"
-#include "IGUIScrollBar.h"
-
-namespace irr
-{
-namespace gui
-{
-       class intlGUIEditBox : public IGUIEditBox
-       {
-       public:
-
-               //! constructor
-               intlGUIEditBox(const wchar_t* text, bool border, IGUIEnvironment* environment,
-                       IGUIElement* parent, s32 id, const core::rect<s32>& rectangle,
-                       bool writable = true, bool has_vscrollbar = false);
-
-               //! destructor
-               virtual ~intlGUIEditBox();
-
-               //! Sets another skin independent font.
-               virtual void setOverrideFont(IGUIFont* font=0);
-
-               //! Gets the override font (if any)
-               /** \return The override font (may be 0) */
-               virtual IGUIFont* getOverrideFont() const;
-
-               //! Get the font which is used right now for drawing
-               /** Currently this is the override font when one is set and the
-               font of the active skin otherwise */
-               virtual IGUIFont* getActiveFont() const;
-
-               //! Sets another color for the text.
-               virtual void setOverrideColor(video::SColor color);
-
-               //! Gets the override color
-               virtual video::SColor getOverrideColor() const;
-
-               //! Sets if the text should use the overide color or the
-               //! color in the gui skin.
-               virtual void enableOverrideColor(bool enable);
-
-               //! Checks if an override color is enabled
-               /** \return true if the override color is enabled, false otherwise */
-               virtual bool isOverrideColorEnabled(void) const;
-
-               //! Sets whether to draw the background
-               virtual void setDrawBackground(bool draw);
-
-               //! Turns the border on or off
-               virtual void setDrawBorder(bool border);
-
-               //! Enables or disables word wrap for using the edit box as multiline text editor.
-               virtual void setWordWrap(bool enable);
-
-               //! Checks if word wrap is enabled
-               //! \return true if word wrap is enabled, false otherwise
-               virtual bool isWordWrapEnabled() const;
-
-               //! Enables or disables newlines.
-               /** \param enable: If set to true, the EGET_EDITBOX_ENTER event will not be fired,
-               instead a newline character will be inserted. */
-               virtual void setMultiLine(bool enable);
-
-               //! Checks if multi line editing is enabled
-               //! \return true if mult-line is enabled, false otherwise
-               virtual bool isMultiLineEnabled() const;
-
-               //! Enables or disables automatic scrolling with cursor position
-               //! \param enable: If set to true, the text will move around with the cursor position
-               virtual void setAutoScroll(bool enable);
-
-               //! Checks to see if automatic scrolling is enabled
-               //! \return true if automatic scrolling is enabled, false if not
-               virtual bool isAutoScrollEnabled() const;
-
-               //! Gets the size area of the text in the edit box
-               //! \return Returns the size in pixels of the text
-               virtual core::dimension2du getTextDimension();
-
-               //! Sets text justification
-               virtual void setTextAlignment(EGUI_ALIGNMENT horizontal, EGUI_ALIGNMENT vertical);
-
-               //! called if an event happened.
-               virtual bool OnEvent(const SEvent& event);
-
-               //! draws the element and its children
-               virtual void draw();
-
-               //! Sets the new caption of this element.
-               virtual void setText(const wchar_t* text);
-
-               //! Sets the maximum amount of characters which may be entered in the box.
-               //! \param max: Maximum amount of characters. If 0, the character amount is
-               //! infinity.
-               virtual void setMax(u32 max);
-
-               //! Returns maximum amount of characters, previously set by setMax();
-               virtual u32 getMax() const;
-
-               //! Sets whether the edit box is a password box. Setting this to true will
-               /** disable MultiLine, WordWrap and the ability to copy with ctrl+c or ctrl+x
-               \param passwordBox: true to enable password, false to disable
-               \param passwordChar: the character that is displayed instead of letters */
-               virtual void setPasswordBox(bool passwordBox, wchar_t passwordChar = L'*');
-
-               //! Returns true if the edit box is currently a password box.
-               virtual bool isPasswordBox() const;
-
-               //! Updates the absolute position, splits text if required
-               virtual void updateAbsolutePosition();
-
-               //! set true if this EditBox is writable
-               virtual void setWritable(bool can_write_text);
-
-               //! Writes attributes of the element.
-               virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const;
-
-               //! Reads attributes of the element
-               virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options);
-
-       protected:
-               //! Breaks the single text line.
-               void breakText();
-               //! sets the area of the given line
-               void setTextRect(s32 line);
-               //! returns the line number that the cursor is on
-               s32 getLineFromPos(s32 pos);
-               //! adds a letter to the edit box
-               void inputChar(wchar_t c);
-               //! calculates the current scroll position
-               void calculateScrollPos();
-               //! send some gui event to parent
-               void sendGuiEvent(EGUI_EVENT_TYPE type);
-               //! set text markers
-               void setTextMarkers(s32 begin, s32 end);
-
-               bool processKey(const SEvent& event);
-               bool processMouse(const SEvent& event);
-               s32 getCursorPos(s32 x, s32 y);
-
-               //! Create a vertical scrollbar
-               void createVScrollBar();
-
-               //! Update the vertical scrollbar (visibilty & scroll position)
-               void updateVScrollBar();
-
-               bool MouseMarking = false;
-               bool Border;
-               bool OverrideColorEnabled = false;
-               s32 MarkBegin = 0;
-               s32 MarkEnd = 0;
-
-               video::SColor OverrideColor = video::SColor(101,255,255,255);
-               gui::IGUIFont *OverrideFont = nullptr;
-               gui::IGUIFont *LastBreakFont = nullptr;
-               IOSOperator *Operator = nullptr;
-
-               u64 BlinkStartTime = 0;
-               s32 CursorPos = 0;
-               s32 HScrollPos = 0;
-               s32 VScrollPos = 0; // scroll position in characters
-               u32 Max = 0;
-
-               bool WordWrap = false;
-               bool MultiLine = false;
-               bool AutoScroll = true;
-               bool PasswordBox = false;
-               wchar_t PasswordChar = L'*';
-               EGUI_ALIGNMENT HAlign = EGUIA_UPPERLEFT;
-               EGUI_ALIGNMENT VAlign = EGUIA_CENTER;
-
-               core::array<core::stringw> BrokenText;
-               core::array<s32> BrokenTextPositions;
-
-               core::rect<s32> CurrentTextRect = core::rect<s32>(0,0,1,1);
-               core::rect<s32> FrameRect; // temporary values
-               u32 m_scrollbar_width;
-               IGUIScrollBar *m_vscrollbar;
-               bool m_writable;
-
-       };
-
-
-} // end namespace gui
-} // end namespace irr
-
-//#endif // _IRR_COMPILE_WITH_GUI_
index 792cd104dd97ab070a455c9585c0eb0f047b771a..8fce83dcbb52c54aa9f5e471d5d9d4bc825c0bd7 100644 (file)
@@ -32,7 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "quicktune.h"
 #include "httpfetch.h"
 #include "gameparams.h"
-#include "database.h"
+#include "database/database.h"
 #include "config.h"
 #include "player.h"
 #include "porting.h"
@@ -41,14 +41,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
        #include "terminal_chat_console.h"
 #endif
 #ifndef SERVER
-#include "guiMainMenu.h"
+#include "gui/guiMainMenu.h"
 #include "client/clientlauncher.h"
-#include "guiEngine.h"
-#include "mainmenumanager.h"
+#include "gui/guiEngine.h"
+#include "gui/mainmenumanager.h"
 #endif
 
 #ifdef HAVE_TOUCHSCREENGUI
-       #include "touchscreengui.h"
+       #include "gui/touchscreengui.h"
 #endif
 
 #if !defined(SERVER) && \
diff --git a/src/mainmenumanager.h b/src/mainmenumanager.h
deleted file mode 100644 (file)
index ea93278..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-/*
-       All kinds of stuff that needs to be exposed from main.cpp
-*/
-#include "modalMenu.h"
-#include <cassert>
-#include <list>
-
-class IGameCallback
-{
-public:
-       virtual void exitToOS() = 0;
-       virtual void keyConfig() = 0;
-       virtual void disconnect() = 0;
-       virtual void changePassword() = 0;
-       virtual void changeVolume() = 0;
-
-       virtual void signalKeyConfigChange() = 0;
-};
-
-extern gui::IGUIEnvironment *guienv;
-extern gui::IGUIStaticText *guiroot;
-
-// Handler for the modal menus
-
-class MainMenuManager : public IMenuManager
-{
-public:
-       virtual void createdMenu(gui::IGUIElement *menu)
-       {
-#ifndef NDEBUG
-               for (gui::IGUIElement *i : m_stack) {
-                       assert(i != menu);
-               }
-#endif
-
-               if(!m_stack.empty())
-                       m_stack.back()->setVisible(false);
-               m_stack.push_back(menu);
-       }
-
-       virtual void deletingMenu(gui::IGUIElement *menu)
-       {
-               // Remove all entries if there are duplicates
-               bool removed_entry;
-               do{
-                       removed_entry = false;
-                       for(std::list<gui::IGUIElement*>::iterator
-                                       i = m_stack.begin();
-                                       i != m_stack.end(); ++i)
-                       {
-                               if(*i == menu)
-                               {
-                                       m_stack.erase(i);
-                                       removed_entry = true;
-                                       break;
-                               }
-                       }
-               }while(removed_entry);
-
-               /*core::list<GUIModalMenu*>::Iterator i = m_stack.getLast();
-               assert(*i == menu);
-               m_stack.erase(i);*/
-
-               if(!m_stack.empty())
-                       m_stack.back()->setVisible(true);
-       }
-
-       // Returns true to prevent further processing
-       virtual bool preprocessEvent(const SEvent& event)
-       {
-               if (m_stack.empty())
-                       return false;
-               GUIModalMenu *mm = dynamic_cast<GUIModalMenu*>(m_stack.back());
-               return mm && mm->preprocessEvent(event);
-       }
-
-       u32 menuCount()
-       {
-               return m_stack.size();
-       }
-
-       bool pausesGame()
-       {
-               for (gui::IGUIElement *i : m_stack) {
-                       GUIModalMenu *mm = dynamic_cast<GUIModalMenu*>(i);
-                       if (mm && mm->pausesGame())
-                               return true;
-               }
-               return false;
-       }
-
-       std::list<gui::IGUIElement*> m_stack;
-};
-
-extern MainMenuManager g_menumgr;
-
-extern bool isMenuActive();
-
-class MainGameCallback : public IGameCallback
-{
-public:
-       MainGameCallback() = default;
-       virtual ~MainGameCallback() = default;
-
-       virtual void exitToOS()
-       {
-               shutdown_requested = true;
-       }
-
-       virtual void disconnect()
-       {
-               disconnect_requested = true;
-       }
-
-       virtual void changePassword()
-       {
-               changepassword_requested = true;
-       }
-
-       virtual void changeVolume()
-       {
-               changevolume_requested = true;
-       }
-
-       virtual void keyConfig()
-       {
-               keyconfig_requested = true;
-       }
-
-       virtual void signalKeyConfigChange()
-       {
-               keyconfig_changed = true;
-       }
-
-
-       bool disconnect_requested = false;
-       bool changepassword_requested = false;
-       bool changevolume_requested = false;
-       bool keyconfig_requested = false;
-       bool shutdown_requested = false;
-
-       bool keyconfig_changed = false;
-};
-
-extern MainGameCallback *g_gamecallback;
index 9bcbc6c00cb8b8d65327701e80977ab29166d681..256c542a123383938a756b48a9267ae9aa56bbd5 100644 (file)
@@ -37,24 +37,24 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "environment.h"
 #include "reflowscan.h"
 #include "emerge.h"
-#include "mapgen_v6.h"
-#include "mg_biome.h"
+#include "mapgen/mapgen_v6.h"
+#include "mapgen/mg_biome.h"
 #include "config.h"
 #include "server.h"
-#include "database.h"
-#include "database-dummy.h"
-#include "database-sqlite3.h"
+#include "database/database.h"
+#include "database/database-dummy.h"
+#include "database/database-sqlite3.h"
 #include "script/scripting_server.h"
 #include <deque>
 #include <queue>
 #if USE_LEVELDB
-#include "database-leveldb.h"
+#include "database/database-leveldb.h"
 #endif
 #if USE_REDIS
-#include "database-redis.h"
+#include "database/database-redis.h"
 #endif
 #if USE_POSTGRESQL
-#include "database-postgresql.h"
+#include "database/database-postgresql.h"
 #endif
 
 
index c1dcf52156c3ff765a4c7674349988391ac38eeb..16e7d8230bd3b8778dce8a4081b0f264e5d534f0 100644 (file)
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "debug.h"
 #include "filesys.h"
 #include "log.h"
-#include "mapgen.h"
+#include "mapgen/mapgen.h"
 #include "settings.h"
 
 #include "map_settings_manager.h"
index b93007d7212fcbd125f76ada184bb606ecbd826a..3871c656d642f825e8aef355e182d0937e1bc45b 100644 (file)
@@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "modifiedstate.h"
 #include "util/numeric.h" // getContainerPos
 #include "settings.h"
-#include "mapgen.h"
+#include "mapgen/mapgen.h"
 
 class Map;
 class NodeMetadataList;
diff --git a/src/mapgen.cpp b/src/mapgen.cpp
deleted file mode 100644 (file)
index f73e16d..0000000
+++ /dev/null
@@ -1,1140 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2015-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "mapgen.h"
-#include "voxel.h"
-#include "noise.h"
-#include "gamedef.h"
-#include "mg_biome.h"
-#include "mapblock.h"
-#include "mapnode.h"
-#include "map.h"
-#include "content_sao.h"
-#include "nodedef.h"
-#include "emerge.h"
-#include "voxelalgorithms.h"
-#include "porting.h"
-#include "profiler.h"
-#include "settings.h"
-#include "treegen.h"
-#include "serialization.h"
-#include "util/serialize.h"
-#include "util/numeric.h"
-#include "filesys.h"
-#include "log.h"
-#include "mapgen_carpathian.h"
-#include "mapgen_flat.h"
-#include "mapgen_fractal.h"
-#include "mapgen_v5.h"
-#include "mapgen_v6.h"
-#include "mapgen_v7.h"
-#include "mapgen_valleys.h"
-#include "mapgen_singlenode.h"
-#include "cavegen.h"
-#include "dungeongen.h"
-
-FlagDesc flagdesc_mapgen[] = {
-       {"caves",       MG_CAVES},
-       {"dungeons",    MG_DUNGEONS},
-       {"light",       MG_LIGHT},
-       {"decorations", MG_DECORATIONS},
-       {NULL,       0}
-};
-
-FlagDesc flagdesc_gennotify[] = {
-       {"dungeon",          1 << GENNOTIFY_DUNGEON},
-       {"temple",           1 << GENNOTIFY_TEMPLE},
-       {"cave_begin",       1 << GENNOTIFY_CAVE_BEGIN},
-       {"cave_end",         1 << GENNOTIFY_CAVE_END},
-       {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN},
-       {"large_cave_end",   1 << GENNOTIFY_LARGECAVE_END},
-       {"decoration",       1 << GENNOTIFY_DECORATION},
-       {NULL,               0}
-};
-
-struct MapgenDesc {
-       const char *name;
-       bool is_user_visible;
-};
-
-////
-//// Built-in mapgens
-////
-
-static MapgenDesc g_reg_mapgens[] = {
-       {"v5",         true},
-       {"v6",         true},
-       {"v7",         true},
-       {"flat",       true},
-       {"fractal",    true},
-       {"valleys",    true},
-       {"singlenode", true},
-       {"carpathian", true},
-};
-
-STATIC_ASSERT(
-       ARRLEN(g_reg_mapgens) == MAPGEN_INVALID,
-       registered_mapgens_is_wrong_size);
-
-////
-//// Mapgen
-////
-
-Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) :
-       gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids)
-{
-       id           = mapgenid;
-       water_level  = params->water_level;
-       mapgen_limit = params->mapgen_limit;
-       flags        = params->flags;
-       csize        = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
-
-       /*
-               We are losing half our entropy by doing this, but it is necessary to
-               preserve reverse compatibility.  If the top half of our current 64 bit
-               seeds ever starts getting used, existing worlds will break due to a
-               different hash outcome and no way to differentiate between versions.
-
-               A solution could be to add a new bit to designate that the top half of
-               the seed value should be used, essentially a 1-bit version code, but
-               this would require increasing the total size of a seed to 9 bytes (yuck)
-
-               It's probably okay if this never gets fixed.  4.2 billion possibilities
-               ought to be enough for anyone.
-       */
-       seed = (s32)params->seed;
-
-       ndef      = emerge->ndef;
-}
-
-
-MapgenType Mapgen::getMapgenType(const std::string &mgname)
-{
-       for (size_t i = 0; i != ARRLEN(g_reg_mapgens); i++) {
-               if (mgname == g_reg_mapgens[i].name)
-                       return (MapgenType)i;
-       }
-
-       return MAPGEN_INVALID;
-}
-
-
-const char *Mapgen::getMapgenName(MapgenType mgtype)
-{
-       size_t index = (size_t)mgtype;
-       if (index == MAPGEN_INVALID || index >= ARRLEN(g_reg_mapgens))
-               return "invalid";
-
-       return g_reg_mapgens[index].name;
-}
-
-
-Mapgen *Mapgen::createMapgen(MapgenType mgtype, int mgid,
-       MapgenParams *params, EmergeManager *emerge)
-{
-       switch (mgtype) {
-       case MAPGEN_CARPATHIAN:
-               return new MapgenCarpathian(mgid, (MapgenCarpathianParams *)params, emerge);
-       case MAPGEN_FLAT:
-               return new MapgenFlat(mgid, (MapgenFlatParams *)params, emerge);
-       case MAPGEN_FRACTAL:
-               return new MapgenFractal(mgid, (MapgenFractalParams *)params, emerge);
-       case MAPGEN_SINGLENODE:
-               return new MapgenSinglenode(mgid, (MapgenSinglenodeParams *)params, emerge);
-       case MAPGEN_V5:
-               return new MapgenV5(mgid, (MapgenV5Params *)params, emerge);
-       case MAPGEN_V6:
-               return new MapgenV6(mgid, (MapgenV6Params *)params, emerge);
-       case MAPGEN_V7:
-               return new MapgenV7(mgid, (MapgenV7Params *)params, emerge);
-       case MAPGEN_VALLEYS:
-               return new MapgenValleys(mgid, (MapgenValleysParams *)params, emerge);
-       default:
-               return NULL;
-       }
-}
-
-
-MapgenParams *Mapgen::createMapgenParams(MapgenType mgtype)
-{
-       switch (mgtype) {
-       case MAPGEN_CARPATHIAN:
-               return new MapgenCarpathianParams;
-       case MAPGEN_FLAT:
-               return new MapgenFlatParams;
-       case MAPGEN_FRACTAL:
-               return new MapgenFractalParams;
-       case MAPGEN_SINGLENODE:
-               return new MapgenSinglenodeParams;
-       case MAPGEN_V5:
-               return new MapgenV5Params;
-       case MAPGEN_V6:
-               return new MapgenV6Params;
-       case MAPGEN_V7:
-               return new MapgenV7Params;
-       case MAPGEN_VALLEYS:
-               return new MapgenValleysParams;
-       default:
-               return NULL;
-       }
-}
-
-
-void Mapgen::getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden)
-{
-       for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) {
-               if (include_hidden || g_reg_mapgens[i].is_user_visible)
-                       mgnames->push_back(g_reg_mapgens[i].name);
-       }
-}
-
-
-u32 Mapgen::getBlockSeed(v3s16 p, s32 seed)
-{
-       return (u32)seed   +
-               p.Z * 38134234 +
-               p.Y * 42123    +
-               p.X * 23;
-}
-
-
-u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed)
-{
-       u32 n = 1619 * p.X + 31337 * p.Y + 52591 * p.Z + 1013 * seed;
-       n = (n >> 13) ^ n;
-       return (n * (n * n * 60493 + 19990303) + 1376312589);
-}
-
-
-// Returns Y one under area minimum if not found
-s16 Mapgen::findGroundLevelFull(v2s16 p2d)
-{
-       const v3s16 &em = vm->m_area.getExtent();
-       s16 y_nodes_max = vm->m_area.MaxEdge.Y;
-       s16 y_nodes_min = vm->m_area.MinEdge.Y;
-       u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
-       s16 y;
-
-       for (y = y_nodes_max; y >= y_nodes_min; y--) {
-               MapNode &n = vm->m_data[i];
-               if (ndef->get(n).walkable)
-                       break;
-
-               vm->m_area.add_y(em, i, -1);
-       }
-       return (y >= y_nodes_min) ? y : y_nodes_min - 1;
-}
-
-
-// Returns -MAX_MAP_GENERATION_LIMIT if not found
-s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
-{
-       const v3s16 &em = vm->m_area.getExtent();
-       u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
-       s16 y;
-
-       for (y = ymax; y >= ymin; y--) {
-               MapNode &n = vm->m_data[i];
-               if (ndef->get(n).walkable)
-                       break;
-
-               vm->m_area.add_y(em, i, -1);
-       }
-       return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
-}
-
-
-// Returns -MAX_MAP_GENERATION_LIMIT if not found or if ground is found first
-s16 Mapgen::findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax)
-{
-       const v3s16 &em = vm->m_area.getExtent();
-       u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
-       s16 y;
-
-       for (y = ymax; y >= ymin; y--) {
-               MapNode &n = vm->m_data[i];
-               if (ndef->get(n).walkable)
-                       return -MAX_MAP_GENERATION_LIMIT;
-
-               if (ndef->get(n).isLiquid())
-                       break;
-
-               vm->m_area.add_y(em, i, -1);
-       }
-       return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
-}
-
-
-void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
-{
-       if (!heightmap)
-               return;
-
-       //TimeTaker t("Mapgen::updateHeightmap", NULL, PRECISION_MICRO);
-       int index = 0;
-       for (s16 z = nmin.Z; z <= nmax.Z; z++) {
-               for (s16 x = nmin.X; x <= nmax.X; x++, index++) {
-                       s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
-
-                       heightmap[index] = y;
-               }
-       }
-}
-
-
-void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
-       s16 *floors, s16 *ceilings, u16 *num_floors, u16 *num_ceilings)
-{
-       u16 floor_i = 0;
-       u16 ceiling_i = 0;
-       const v3s16 &em = vm->m_area.getExtent();
-
-       bool is_walkable = false;
-       u32 vi = vm->m_area.index(p2d.X, ymax, p2d.Y);
-       MapNode mn_max = vm->m_data[vi];
-       bool walkable_above = ndef->get(mn_max).walkable;
-       vm->m_area.add_y(em, vi, -1);
-
-       for (s16 y = ymax - 1; y >= ymin; y--) {
-               MapNode mn = vm->m_data[vi];
-               is_walkable = ndef->get(mn).walkable;
-
-               if (is_walkable && !walkable_above) {
-                       floors[floor_i] = y;
-                       floor_i++;
-               } else if (!is_walkable && walkable_above) {
-                       ceilings[ceiling_i] = y + 1;
-                       ceiling_i++;
-               }
-
-               vm->m_area.add_y(em, vi, -1);
-               walkable_above = is_walkable;
-       }
-
-       *num_floors = floor_i;
-       *num_ceilings = ceiling_i;
-}
-
-
-inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
-{
-       u32 vi_neg_x = vi;
-       vm->m_area.add_x(em, vi_neg_x, -1);
-       if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) {
-               const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]);
-               if (c_nx.floodable && !c_nx.isLiquid())
-                       return true;
-       }
-       u32 vi_pos_x = vi;
-       vm->m_area.add_x(em, vi_pos_x, +1);
-       if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) {
-               const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]);
-               if (c_px.floodable && !c_px.isLiquid())
-                       return true;
-       }
-       u32 vi_neg_z = vi;
-       vm->m_area.add_z(em, vi_neg_z, -1);
-       if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) {
-               const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]);
-               if (c_nz.floodable && !c_nz.isLiquid())
-                       return true;
-       }
-       u32 vi_pos_z = vi;
-       vm->m_area.add_z(em, vi_pos_z, +1);
-       if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) {
-               const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]);
-               if (c_pz.floodable && !c_pz.isLiquid())
-                       return true;
-       }
-       return false;
-}
-
-void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax)
-{
-       bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;
-       const v3s16 &em  = vm->m_area.getExtent();
-
-       for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++)
-       for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) {
-               wasignored = true;
-               wasliquid = false;
-               waschecked = false;
-               waspushed = false;
-
-               u32 vi = vm->m_area.index(x, nmax.Y, z);
-               for (s16 y = nmax.Y; y >= nmin.Y; y--) {
-                       isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE;
-                       isliquid = ndef->get(vm->m_data[vi]).isLiquid();
-
-                       if (isignored || wasignored || isliquid == wasliquid) {
-                               // Neither topmost node of liquid column nor topmost node below column
-                               waschecked = false;
-                               waspushed = false;
-                       } else if (isliquid) {
-                               // This is the topmost node in the column
-                               bool ispushed = false;
-                               if (isLiquidHorizontallyFlowable(vi, em)) {
-                                       trans_liquid->push_back(v3s16(x, y, z));
-                                       ispushed = true;
-                               }
-                               // Remember waschecked and waspushed to avoid repeated
-                               // checks/pushes in case the column consists of only this node
-                               waschecked = true;
-                               waspushed = ispushed;
-                       } else {
-                               // This is the topmost node below a liquid column
-                               u32 vi_above = vi;
-                               vm->m_area.add_y(em, vi_above, 1);
-                               if (!waspushed && (ndef->get(vm->m_data[vi]).floodable ||
-                                               (!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) {
-                                       // Push back the lowest node in the column which is one
-                                       // node above this one
-                                       trans_liquid->push_back(v3s16(x, y + 1, z));
-                               }
-                       }
-
-                       wasliquid = isliquid;
-                       wasignored = isignored;
-                       vm->m_area.add_y(em, vi, -1);
-               }
-       }
-}
-
-
-void Mapgen::setLighting(u8 light, v3s16 nmin, v3s16 nmax)
-{
-       ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
-       VoxelArea a(nmin, nmax);
-
-       for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
-               for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
-                       u32 i = vm->m_area.index(a.MinEdge.X, y, z);
-                       for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++)
-                               vm->m_data[i].param1 = light;
-               }
-       }
-}
-
-
-void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light)
-{
-       if (light <= 1 || !a.contains(p))
-               return;
-
-       u32 vi = vm->m_area.index(p);
-       MapNode &n = vm->m_data[vi];
-
-       // Decay light in each of the banks separately
-       u8 light_day = light & 0x0F;
-       if (light_day > 0)
-               light_day -= 0x01;
-
-       u8 light_night = light & 0xF0;
-       if (light_night > 0)
-               light_night -= 0x10;
-
-       // Bail out only if we have no more light from either bank to propogate, or
-       // we hit a solid block that light cannot pass through.
-       if ((light_day  <= (n.param1 & 0x0F) &&
-               light_night <= (n.param1 & 0xF0)) ||
-               !ndef->get(n).light_propagates)
-               return;
-
-       // Since this recursive function only terminates when there is no light from
-       // either bank left, we need to take the max of both banks into account for
-       // the case where spreading has stopped for one light bank but not the other.
-       light = MYMAX(light_day, n.param1 & 0x0F) |
-                       MYMAX(light_night, n.param1 & 0xF0);
-
-       n.param1 = light;
-
-       lightSpread(a, p + v3s16(0, 0, 1), light);
-       lightSpread(a, p + v3s16(0, 1, 0), light);
-       lightSpread(a, p + v3s16(1, 0, 0), light);
-       lightSpread(a, p - v3s16(0, 0, 1), light);
-       lightSpread(a, p - v3s16(0, 1, 0), light);
-       lightSpread(a, p - v3s16(1, 0, 0), light);
-}
-
-
-void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
-       bool propagate_shadow)
-{
-       ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
-       //TimeTaker t("updateLighting");
-
-       propagateSunlight(nmin, nmax, propagate_shadow);
-       spreadLight(full_nmin, full_nmax);
-
-       //printf("updateLighting: %dms\n", t.stop());
-}
-
-
-void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow)
-{
-       //TimeTaker t("propagateSunlight");
-       VoxelArea a(nmin, nmax);
-       bool block_is_underground = (water_level >= nmax.Y);
-       const v3s16 &em = vm->m_area.getExtent();
-
-       // NOTE: Direct access to the low 4 bits of param1 is okay here because,
-       // by definition, sunlight will never be in the night lightbank.
-
-       for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
-               for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) {
-                       // see if we can get a light value from the overtop
-                       u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z);
-                       if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
-                               if (block_is_underground)
-                                       continue;
-                       } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN &&
-                                       propagate_shadow) {
-                               continue;
-                       }
-                       vm->m_area.add_y(em, i, -1);
-
-                       for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) {
-                               MapNode &n = vm->m_data[i];
-                               if (!ndef->get(n).sunlight_propagates)
-                                       break;
-                               n.param1 = LIGHT_SUN;
-                               vm->m_area.add_y(em, i, -1);
-                       }
-               }
-       }
-       //printf("propagateSunlight: %dms\n", t.stop());
-}
-
-
-void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
-{
-       //TimeTaker t("spreadLight");
-       VoxelArea a(nmin, nmax);
-
-       for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
-               for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
-                       u32 i = vm->m_area.index(a.MinEdge.X, y, z);
-                       for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) {
-                               MapNode &n = vm->m_data[i];
-                               if (n.getContent() == CONTENT_IGNORE)
-                                       continue;
-
-                               const ContentFeatures &cf = ndef->get(n);
-                               if (!cf.light_propagates)
-                                       continue;
-
-                               // TODO(hmmmmm): Abstract away direct param1 accesses with a
-                               // wrapper, but something lighter than MapNode::get/setLight
-
-                               u8 light_produced = cf.light_source;
-                               if (light_produced)
-                                       n.param1 = light_produced | (light_produced << 4);
-
-                               u8 light = n.param1;
-                               if (light) {
-                                       lightSpread(a, v3s16(x,     y,     z + 1), light);
-                                       lightSpread(a, v3s16(x,     y + 1, z    ), light);
-                                       lightSpread(a, v3s16(x + 1, y,     z    ), light);
-                                       lightSpread(a, v3s16(x,     y,     z - 1), light);
-                                       lightSpread(a, v3s16(x,     y - 1, z    ), light);
-                                       lightSpread(a, v3s16(x - 1, y,     z    ), light);
-                               }
-                       }
-               }
-       }
-
-       //printf("spreadLight: %dms\n", t.stop());
-}
-
-
-////
-//// MapgenBasic
-////
-
-MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge)
-       : Mapgen(mapgenid, params, emerge)
-{
-       this->m_emerge = emerge;
-       this->m_bmgr   = emerge->biomemgr;
-
-       //// Here, 'stride' refers to the number of elements needed to skip to index
-       //// an adjacent element for that coordinate in noise/height/biome maps
-       //// (*not* vmanip content map!)
-
-       // Note there is no X stride explicitly defined.  Items adjacent in the X
-       // coordinate are assumed to be adjacent in memory as well (i.e. stride of 1).
-
-       // Number of elements to skip to get to the next Y coordinate
-       this->ystride = csize.X;
-
-       // Number of elements to skip to get to the next Z coordinate
-       this->zstride = csize.X * csize.Y;
-
-       // Z-stride value for maps oversized for 1-down overgeneration
-       this->zstride_1d = csize.X * (csize.Y + 1);
-
-       // Z-stride value for maps oversized for 1-up 1-down overgeneration
-       this->zstride_1u1d = csize.X * (csize.Y + 2);
-
-       //// Allocate heightmap
-       this->heightmap = new s16[csize.X * csize.Z];
-
-       //// Initialize biome generator
-       // TODO(hmmmm): should we have a way to disable biomemanager biomes?
-       biomegen = m_bmgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize);
-       biomemap = biomegen->biomemap;
-
-       //// Look up some commonly used content
-       c_stone              = ndef->getId("mapgen_stone");
-       c_desert_stone       = ndef->getId("mapgen_desert_stone");
-       c_sandstone          = ndef->getId("mapgen_sandstone");
-       c_water_source       = ndef->getId("mapgen_water_source");
-       c_river_water_source = ndef->getId("mapgen_river_water_source");
-       c_lava_source        = ndef->getId("mapgen_lava_source");
-
-       // Fall back to more basic content if not defined
-       // river_water_source cannot fallback to water_source because river water
-       // needs to be non-renewable and have a short flow range.
-       if (c_desert_stone == CONTENT_IGNORE)
-               c_desert_stone = c_stone;
-       if (c_sandstone == CONTENT_IGNORE)
-               c_sandstone = c_stone;
-
-       //// Content used for dungeon generation
-       c_cobble                = ndef->getId("mapgen_cobble");
-       c_mossycobble           = ndef->getId("mapgen_mossycobble");
-       c_stair_cobble          = ndef->getId("mapgen_stair_cobble");
-       c_stair_desert_stone    = ndef->getId("mapgen_stair_desert_stone");
-       c_sandstonebrick        = ndef->getId("mapgen_sandstonebrick");
-       c_stair_sandstone_block = ndef->getId("mapgen_stair_sandstone_block");
-
-       // Fall back to more basic content if not defined
-       if (c_mossycobble == CONTENT_IGNORE)
-               c_mossycobble = c_cobble;
-       if (c_stair_cobble == CONTENT_IGNORE)
-               c_stair_cobble = c_cobble;
-       if (c_stair_desert_stone == CONTENT_IGNORE)
-               c_stair_desert_stone = c_desert_stone;
-       if (c_sandstonebrick == CONTENT_IGNORE)
-               c_sandstonebrick = c_sandstone;
-       if (c_stair_sandstone_block == CONTENT_IGNORE)
-               c_stair_sandstone_block = c_sandstonebrick;
-}
-
-
-MapgenBasic::~MapgenBasic()
-{
-       delete biomegen;
-       delete []heightmap;
-}
-
-
-void MapgenBasic::generateBiomes(MgStoneType *mgstone_type,
-       content_t *biome_stone)
-{
-       // can't generate biomes without a biome generator!
-       assert(biomegen);
-       assert(biomemap);
-
-       const v3s16 &em = vm->m_area.getExtent();
-       u32 index = 0;
-       MgStoneType stone_type = MGSTONE_OTHER;
-       content_t c_biome_stone = c_stone;
-
-       noise_filler_depth->perlinMap2D(node_min.X, node_min.Z);
-
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
-               Biome *biome = NULL;
-               u16 depth_top = 0;
-               u16 base_filler = 0;
-               u16 depth_water_top = 0;
-               u16 depth_riverbed = 0;
-               s16 biome_y_min = -MAX_MAP_GENERATION_LIMIT;
-               u32 vi = vm->m_area.index(x, node_max.Y, z);
-
-               // Check node at base of mapchunk above, either a node of a previously
-               // generated mapchunk or if not, a node of overgenerated base terrain.
-               content_t c_above = vm->m_data[vi + em.X].getContent();
-               bool air_above = c_above == CONTENT_AIR;
-               bool river_water_above = c_above == c_river_water_source;
-               bool water_above = c_above == c_water_source || river_water_above;
-
-               biomemap[index] = BIOME_NONE;
-
-               // If there is air or water above enable top/filler placement, otherwise force
-               // nplaced to stone level by setting a number exceeding any possible filler depth.
-               u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
-
-               for (s16 y = node_max.Y; y >= node_min.Y; y--) {
-                       content_t c = vm->m_data[vi].getContent();
-                       // Biome is (re)calculated:
-                       // 1. At the surface of stone below air or water.
-                       // 2. At the surface of water below air.
-                       // 3. When stone or water is detected but biome has not yet been calculated.
-                       // 4. When stone or water is detected just below a biome's lower limit.
-                       bool is_stone_surface = (c == c_stone) &&
-                               (air_above || water_above || !biome || y < biome_y_min); // 1, 3, 4
-
-                       bool is_water_surface =
-                               (c == c_water_source || c == c_river_water_source) &&
-                               (air_above || !biome || y < biome_y_min); // 2, 3, 4
-
-                       if (is_stone_surface || is_water_surface) {
-                               // (Re)calculate biome
-                               biome = biomegen->getBiomeAtIndex(index, y);
-
-                               if (biomemap[index] == BIOME_NONE && is_stone_surface)
-                                       biomemap[index] = biome->index;
-
-                               depth_top = biome->depth_top;
-                               base_filler = MYMAX(depth_top +
-                                       biome->depth_filler +
-                                       noise_filler_depth->result[index], 0.0f);
-                               depth_water_top = biome->depth_water_top;
-                               depth_riverbed = biome->depth_riverbed;
-                               biome_y_min = biome->y_min;
-
-                               // Detect stone type for dungeons during every biome calculation.
-                               // If none detected the last selected biome stone is chosen.
-                               if (biome->c_stone == c_stone)
-                                       stone_type = MGSTONE_STONE;
-                               else if (biome->c_stone == c_desert_stone)
-                                       stone_type = MGSTONE_DESERT_STONE;
-                               else if (biome->c_stone == c_sandstone)
-                                       stone_type = MGSTONE_SANDSTONE;
-
-                               c_biome_stone = biome->c_stone;
-                       }
-
-                       if (c == c_stone) {
-                               content_t c_below = vm->m_data[vi - em.X].getContent();
-
-                               // If the node below isn't solid, make this node stone, so that
-                               // any top/filler nodes above are structurally supported.
-                               // This is done by aborting the cycle of top/filler placement
-                               // immediately by forcing nplaced to stone level.
-                               if (c_below == CONTENT_AIR
-                                               || c_below == c_water_source
-                                               || c_below == c_river_water_source)
-                                       nplaced = U16_MAX;
-
-                               if (river_water_above) {
-                                       if (nplaced < depth_riverbed) {
-                                               vm->m_data[vi] = MapNode(biome->c_riverbed);
-                                               nplaced++;
-                                       } else {
-                                               nplaced = U16_MAX;  // Disable top/filler placement
-                                               river_water_above = false;
-                                       }
-                               } else if (nplaced < depth_top) {
-                                       vm->m_data[vi] = MapNode(biome->c_top);
-                                       nplaced++;
-                               } else if (nplaced < base_filler) {
-                                       vm->m_data[vi] = MapNode(biome->c_filler);
-                                       nplaced++;
-                               } else {
-                                       vm->m_data[vi] = MapNode(biome->c_stone);
-                                       nplaced = U16_MAX;  // Disable top/filler placement
-                               }
-
-                               air_above = false;
-                               water_above = false;
-                       } else if (c == c_water_source) {
-                               vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
-                                               ? biome->c_water_top : biome->c_water);
-                               nplaced = 0;  // Enable top/filler placement for next surface
-                               air_above = false;
-                               water_above = true;
-                       } else if (c == c_river_water_source) {
-                               vm->m_data[vi] = MapNode(biome->c_river_water);
-                               nplaced = 0;  // Enable riverbed placement for next surface
-                               air_above = false;
-                               water_above = true;
-                               river_water_above = true;
-                       } else if (c == CONTENT_AIR) {
-                               nplaced = 0;  // Enable top/filler placement for next surface
-                               air_above = true;
-                               water_above = false;
-                       } else {  // Possible various nodes overgenerated from neighbouring mapchunks
-                               nplaced = U16_MAX;  // Disable top/filler placement
-                               air_above = false;
-                               water_above = false;
-                       }
-
-                       vm->m_area.add_y(em, vi, -1);
-               }
-       }
-
-       *mgstone_type = stone_type;
-       *biome_stone = c_biome_stone;
-}
-
-
-void MapgenBasic::dustTopNodes()
-{
-       if (node_max.Y < water_level)
-               return;
-
-       const v3s16 &em = vm->m_area.getExtent();
-       u32 index = 0;
-
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
-               Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]);
-
-               if (biome->c_dust == CONTENT_IGNORE)
-                       continue;
-
-               u32 vi = vm->m_area.index(x, full_node_max.Y, z);
-               content_t c_full_max = vm->m_data[vi].getContent();
-               s16 y_start;
-
-               if (c_full_max == CONTENT_AIR) {
-                       y_start = full_node_max.Y - 1;
-               } else if (c_full_max == CONTENT_IGNORE) {
-                       vi = vm->m_area.index(x, node_max.Y + 1, z);
-                       content_t c_max = vm->m_data[vi].getContent();
-
-                       if (c_max == CONTENT_AIR)
-                               y_start = node_max.Y;
-                       else
-                               continue;
-               } else {
-                       continue;
-               }
-
-               vi = vm->m_area.index(x, y_start, z);
-               for (s16 y = y_start; y >= node_min.Y - 1; y--) {
-                       if (vm->m_data[vi].getContent() != CONTENT_AIR)
-                               break;
-
-                       vm->m_area.add_y(em, vi, -1);
-               }
-
-               content_t c = vm->m_data[vi].getContent();
-               if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
-                       vm->m_area.add_y(em, vi, 1);
-                       vm->m_data[vi] = MapNode(biome->c_dust);
-               }
-       }
-}
-
-
-void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth)
-{
-       if (max_stone_y < node_min.Y)
-               return;
-
-       CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize,
-               &np_cave1, &np_cave2, seed, cave_width);
-
-       caves_noise.generateCaves(vm, node_min, node_max, biomemap);
-
-       if (node_max.Y > large_cave_depth)
-               return;
-
-       PseudoRandom ps(blockseed + 21343);
-       u32 bruises_count = ps.range(0, 2);
-       for (u32 i = 0; i < bruises_count; i++) {
-               CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
-                       c_water_source, CONTENT_IGNORE, lava_depth);
-
-               cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
-       }
-}
-
-
-bool MapgenBasic::generateCaverns(s16 max_stone_y)
-{
-       if (node_min.Y > max_stone_y || node_min.Y > cavern_limit)
-               return false;
-
-       CavernsNoise caverns_noise(ndef, csize, &np_cavern,
-               seed, cavern_limit, cavern_taper, cavern_threshold);
-
-       return caverns_noise.generateCaverns(vm, node_min, node_max);
-}
-
-
-void MapgenBasic::generateDungeons(s16 max_stone_y,
-       MgStoneType stone_type, content_t biome_stone)
-{
-       if (max_stone_y < node_min.Y)
-               return;
-
-       DungeonParams dp;
-
-       dp.seed             = seed;
-       dp.c_water          = c_water_source;
-       dp.c_river_water    = c_river_water_source;
-
-       dp.only_in_ground   = true;
-       dp.corridor_len_min = 1;
-       dp.corridor_len_max = 13;
-       dp.rooms_min        = 2;
-       dp.rooms_max        = 16;
-       dp.y_min            = -MAX_MAP_GENERATION_LIMIT;
-       dp.y_max            = MAX_MAP_GENERATION_LIMIT;
-
-       dp.np_density       = nparams_dungeon_density;
-       dp.np_alt_wall      = nparams_dungeon_alt_wall;
-
-       switch (stone_type) {
-       default:
-       case MGSTONE_STONE:
-               dp.c_wall              = c_cobble;
-               dp.c_alt_wall          = c_mossycobble;
-               dp.c_stair             = c_stair_cobble;
-
-               dp.diagonal_dirs       = false;
-               dp.holesize            = v3s16(1, 2, 1);
-               dp.room_size_min       = v3s16(4, 4, 4);
-               dp.room_size_max       = v3s16(8, 6, 8);
-               dp.room_size_large_min = v3s16(8, 8, 8);
-               dp.room_size_large_max = v3s16(16, 16, 16);
-               dp.notifytype          = GENNOTIFY_DUNGEON;
-               break;
-       case MGSTONE_DESERT_STONE:
-               dp.c_wall              = c_desert_stone;
-               dp.c_alt_wall          = CONTENT_IGNORE;
-               dp.c_stair             = c_stair_desert_stone;
-
-               dp.diagonal_dirs       = true;
-               dp.holesize            = v3s16(2, 3, 2);
-               dp.room_size_min       = v3s16(6, 9, 6);
-               dp.room_size_max       = v3s16(10, 11, 10);
-               dp.room_size_large_min = v3s16(10, 13, 10);
-               dp.room_size_large_max = v3s16(18, 21, 18);
-               dp.notifytype          = GENNOTIFY_TEMPLE;
-               break;
-       case MGSTONE_SANDSTONE:
-               dp.c_wall              = c_sandstonebrick;
-               dp.c_alt_wall          = CONTENT_IGNORE;
-               dp.c_stair             = c_stair_sandstone_block;
-
-               dp.diagonal_dirs       = false;
-               dp.holesize            = v3s16(2, 2, 2);
-               dp.room_size_min       = v3s16(6, 4, 6);
-               dp.room_size_max       = v3s16(10, 6, 10);
-               dp.room_size_large_min = v3s16(10, 8, 10);
-               dp.room_size_large_max = v3s16(18, 16, 18);
-               dp.notifytype          = GENNOTIFY_DUNGEON;
-               break;
-       case MGSTONE_OTHER:
-               dp.c_wall              = biome_stone;
-               dp.c_alt_wall          = biome_stone;
-               dp.c_stair             = biome_stone;
-
-               dp.diagonal_dirs       = false;
-               dp.holesize            = v3s16(1, 2, 1);
-               dp.room_size_min       = v3s16(4, 4, 4);
-               dp.room_size_max       = v3s16(8, 6, 8);
-               dp.room_size_large_min = v3s16(8, 8, 8);
-               dp.room_size_large_max = v3s16(16, 16, 16);
-               dp.notifytype          = GENNOTIFY_DUNGEON;
-               break;
-       }
-
-       DungeonGen dgen(ndef, &gennotify, &dp);
-       dgen.generate(vm, blockseed, full_node_min, full_node_max);
-}
-
-
-////
-//// GenerateNotifier
-////
-
-GenerateNotifier::GenerateNotifier(u32 notify_on,
-       std::set<u32> *notify_on_deco_ids)
-{
-       m_notify_on = notify_on;
-       m_notify_on_deco_ids = notify_on_deco_ids;
-}
-
-
-void GenerateNotifier::setNotifyOn(u32 notify_on)
-{
-       m_notify_on = notify_on;
-}
-
-
-void GenerateNotifier::setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids)
-{
-       m_notify_on_deco_ids = notify_on_deco_ids;
-}
-
-
-bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
-{
-       if (!(m_notify_on & (1 << type)))
-               return false;
-
-       if (type == GENNOTIFY_DECORATION &&
-               m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->end())
-               return false;
-
-       GenNotifyEvent gne;
-       gne.type = type;
-       gne.pos  = pos;
-       gne.id   = id;
-       m_notify_events.push_back(gne);
-
-       return true;
-}
-
-
-void GenerateNotifier::getEvents(
-       std::map<std::string, std::vector<v3s16> > &event_map,
-       bool peek_events)
-{
-       std::list<GenNotifyEvent>::iterator it;
-
-       for (it = m_notify_events.begin(); it != m_notify_events.end(); ++it) {
-               GenNotifyEvent &gn = *it;
-               std::string name = (gn.type == GENNOTIFY_DECORATION) ?
-                       "decoration#"+ itos(gn.id) :
-                       flagdesc_gennotify[gn.type].name;
-
-               event_map[name].push_back(gn.pos);
-       }
-
-       if (!peek_events)
-               m_notify_events.clear();
-}
-
-
-////
-//// MapgenParams
-////
-
-
-MapgenParams::~MapgenParams()
-{
-       delete bparams;
-}
-
-
-void MapgenParams::readParams(const Settings *settings)
-{
-       std::string seed_str;
-       const char *seed_name = (settings == g_settings) ? "fixed_map_seed" : "seed";
-
-       if (settings->getNoEx(seed_name, seed_str)) {
-               if (!seed_str.empty())
-                       seed = read_seed(seed_str.c_str());
-               else
-                       myrand_bytes(&seed, sizeof(seed));
-       }
-
-       std::string mg_name;
-       if (settings->getNoEx("mg_name", mg_name)) {
-               mgtype = Mapgen::getMapgenType(mg_name);
-               if (mgtype == MAPGEN_INVALID)
-                       mgtype = MAPGEN_DEFAULT;
-       }
-
-       settings->getS16NoEx("water_level", water_level);
-       settings->getS16NoEx("mapgen_limit", mapgen_limit);
-       settings->getS16NoEx("chunksize", chunksize);
-       settings->getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
-
-       delete bparams;
-       bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL);
-       if (bparams) {
-               bparams->readParams(settings);
-               bparams->seed = seed;
-       }
-}
-
-
-void MapgenParams::writeParams(Settings *settings) const
-{
-       settings->set("mg_name", Mapgen::getMapgenName(mgtype));
-       settings->setU64("seed", seed);
-       settings->setS16("water_level", water_level);
-       settings->setS16("mapgen_limit", mapgen_limit);
-       settings->setS16("chunksize", chunksize);
-       settings->setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX);
-
-       if (bparams)
-               bparams->writeParams(settings);
-}
-
-// Calculate edges of outermost generated mapchunks (less than
-// 'mapgen_limit'), and corresponding exact limits for SAO entities.
-void MapgenParams::calcMapgenEdges()
-{
-       if (m_mapgen_edges_calculated)
-               return;
-
-       // Central chunk offset, in blocks
-       s16 ccoff_b = -chunksize / 2;
-       // Chunksize, in nodes
-       s32 csize_n = chunksize * MAP_BLOCKSIZE;
-       // Minp/maxp of central chunk, in nodes
-       s16 ccmin = ccoff_b * MAP_BLOCKSIZE;
-       s16 ccmax = ccmin + csize_n - 1;
-       // Fullminp/fullmaxp of central chunk, in nodes
-       s16 ccfmin = ccmin - MAP_BLOCKSIZE;
-       s16 ccfmax = ccmax + MAP_BLOCKSIZE;
-       // Effective mapgen limit, in blocks
-       // Uses same calculation as ServerMap::blockpos_over_mapgen_limit(v3s16 p)
-       s16 mapgen_limit_b = rangelim(mapgen_limit,
-               0, MAX_MAP_GENERATION_LIMIT) / MAP_BLOCKSIZE;
-       // Effective mapgen limits, in nodes
-       s16 mapgen_limit_min = -mapgen_limit_b * MAP_BLOCKSIZE;
-       s16 mapgen_limit_max = (mapgen_limit_b + 1) * MAP_BLOCKSIZE - 1;
-       // Number of complete chunks from central chunk fullminp/fullmaxp
-       // to effective mapgen limits.
-       s16 numcmin = MYMAX((ccfmin - mapgen_limit_min) / csize_n, 0);
-       s16 numcmax = MYMAX((mapgen_limit_max - ccfmax) / csize_n, 0);
-       // Mapgen edges, in nodes
-       mapgen_edge_min = ccmin - numcmin * csize_n;
-       mapgen_edge_max = ccmax + numcmax * csize_n;
-       // SAO position limits, in Irrlicht units
-       m_sao_limit_min = mapgen_edge_min * BS - 3.0f;
-       m_sao_limit_max = mapgen_edge_max * BS + 3.0f;
-
-       m_mapgen_edges_calculated = true;
-}
-
-
-bool MapgenParams::saoPosOverLimit(const v3f &p)
-{
-       if (!m_mapgen_edges_calculated)
-               calcMapgenEdges();
-
-       return p.X < m_sao_limit_min ||
-               p.X > m_sao_limit_max ||
-               p.Y < m_sao_limit_min ||
-               p.Y > m_sao_limit_max ||
-               p.Z < m_sao_limit_min ||
-               p.Z > m_sao_limit_max;
-}
-
-
-s32 MapgenParams::getSpawnRangeMax()
-{
-       calcMapgenEdges();
-
-       return MYMIN(-mapgen_edge_min, mapgen_edge_max);
-}
diff --git a/src/mapgen.h b/src/mapgen.h
deleted file mode 100644 (file)
index 8994fdc..0000000
+++ /dev/null
@@ -1,302 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2015-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "noise.h"
-#include "nodedef.h"
-#include "util/string.h"
-#include "util/container.h"
-
-#define MAPGEN_DEFAULT MAPGEN_V7
-#define MAPGEN_DEFAULT_NAME "v7"
-
-/////////////////// Mapgen flags
-#define MG_TREES       0x01  // Deprecated. Moved into mgv6 flags
-#define MG_CAVES       0x02
-#define MG_DUNGEONS    0x04
-#define MG_FLAT        0x08  // Deprecated. Moved into mgv6 flags
-#define MG_LIGHT       0x10
-#define MG_DECORATIONS 0x20
-
-typedef u8 biome_t;  // copy from mg_biome.h to avoid an unnecessary include
-
-class Settings;
-class MMVManip;
-class INodeDefManager;
-
-extern FlagDesc flagdesc_mapgen[];
-extern FlagDesc flagdesc_gennotify[];
-
-class Biome;
-class BiomeGen;
-struct BiomeParams;
-class BiomeManager;
-class EmergeManager;
-class MapBlock;
-class VoxelManipulator;
-struct BlockMakeData;
-class VoxelArea;
-class Map;
-
-enum MapgenObject {
-       MGOBJ_VMANIP,
-       MGOBJ_HEIGHTMAP,
-       MGOBJ_BIOMEMAP,
-       MGOBJ_HEATMAP,
-       MGOBJ_HUMIDMAP,
-       MGOBJ_GENNOTIFY
-};
-
-enum GenNotifyType {
-       GENNOTIFY_DUNGEON,
-       GENNOTIFY_TEMPLE,
-       GENNOTIFY_CAVE_BEGIN,
-       GENNOTIFY_CAVE_END,
-       GENNOTIFY_LARGECAVE_BEGIN,
-       GENNOTIFY_LARGECAVE_END,
-       GENNOTIFY_DECORATION,
-       NUM_GENNOTIFY_TYPES
-};
-
-enum MgStoneType {
-       MGSTONE_STONE,
-       MGSTONE_DESERT_STONE,
-       MGSTONE_SANDSTONE,
-       MGSTONE_OTHER,
-};
-
-struct GenNotifyEvent {
-       GenNotifyType type;
-       v3s16 pos;
-       u32 id;
-};
-
-class GenerateNotifier {
-public:
-       GenerateNotifier() = default;
-       GenerateNotifier(u32 notify_on, std::set<u32> *notify_on_deco_ids);
-
-       void setNotifyOn(u32 notify_on);
-       void setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids);
-
-       bool addEvent(GenNotifyType type, v3s16 pos, u32 id=0);
-       void getEvents(std::map<std::string, std::vector<v3s16> > &event_map,
-               bool peek_events=false);
-
-private:
-       u32 m_notify_on = 0;
-       std::set<u32> *m_notify_on_deco_ids;
-       std::list<GenNotifyEvent> m_notify_events;
-};
-
-enum MapgenType {
-       MAPGEN_V5,
-       MAPGEN_V6,
-       MAPGEN_V7,
-       MAPGEN_FLAT,
-       MAPGEN_FRACTAL,
-       MAPGEN_VALLEYS,
-       MAPGEN_SINGLENODE,
-       MAPGEN_CARPATHIAN,
-       MAPGEN_INVALID,
-};
-
-struct MapgenParams {
-       MapgenParams() = default;
-       virtual ~MapgenParams();
-
-       MapgenType mgtype = MAPGEN_DEFAULT;
-       s16 chunksize = 5;
-       u64 seed = 0;
-       s16 water_level = 1;
-       s16 mapgen_limit = MAX_MAP_GENERATION_LIMIT;
-       u32 flags = MG_CAVES | MG_LIGHT | MG_DECORATIONS;
-
-       BiomeParams *bparams = nullptr;
-
-       s16 mapgen_edge_min = -MAX_MAP_GENERATION_LIMIT;
-       s16 mapgen_edge_max = MAX_MAP_GENERATION_LIMIT;
-
-       virtual void readParams(const Settings *settings);
-       virtual void writeParams(Settings *settings) const;
-
-       bool saoPosOverLimit(const v3f &p);
-       s32 getSpawnRangeMax();
-
-private:
-       void calcMapgenEdges();
-
-       float m_sao_limit_min = -MAX_MAP_GENERATION_LIMIT * BS;
-       float m_sao_limit_max = MAX_MAP_GENERATION_LIMIT * BS;
-       bool m_mapgen_edges_calculated = false;
-};
-
-
-/*
-       Generic interface for map generators.  All mapgens must inherit this class.
-       If a feature exposed by a public member pointer is not supported by a
-       certain mapgen, it must be set to NULL.
-
-       Apart from makeChunk, getGroundLevelAtPoint, and getSpawnLevelAtPoint, all
-       methods can be used by constructing a Mapgen base class and setting the
-       appropriate public members (e.g. vm, ndef, and so on).
-*/
-class Mapgen {
-public:
-       s32 seed = 0;
-       int water_level = 0;
-       int mapgen_limit = 0;
-       u32 flags = 0;
-       bool generating = false;
-       int id = -1;
-
-       MMVManip *vm = nullptr;
-       INodeDefManager *ndef = nullptr;
-
-       u32 blockseed;
-       s16 *heightmap = nullptr;
-       biome_t *biomemap = nullptr;
-       v3s16 csize;
-
-       BiomeGen *biomegen = nullptr;
-       GenerateNotifier gennotify;
-
-       Mapgen() = default;
-       Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge);
-       virtual ~Mapgen() = default;
-       DISABLE_CLASS_COPY(Mapgen);
-
-       virtual MapgenType getType() const { return MAPGEN_INVALID; }
-
-       static u32 getBlockSeed(v3s16 p, s32 seed);
-       static u32 getBlockSeed2(v3s16 p, s32 seed);
-       s16 findGroundLevelFull(v2s16 p2d);
-       s16 findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax);
-       s16 findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax);
-       void updateHeightmap(v3s16 nmin, v3s16 nmax);
-       void getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
-               s16 *floors, s16 *ceilings, u16 *num_floors, u16 *num_ceilings);
-
-       void updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax);
-
-       void setLighting(u8 light, v3s16 nmin, v3s16 nmax);
-       void lightSpread(VoxelArea &a, v3s16 p, u8 light);
-       void calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
-               bool propagate_shadow = true);
-       void propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow);
-       void spreadLight(v3s16 nmin, v3s16 nmax);
-
-       virtual void makeChunk(BlockMakeData *data) {}
-       virtual int getGroundLevelAtPoint(v2s16 p) { return 0; }
-
-       // getSpawnLevelAtPoint() is a function within each mapgen that returns a
-       // suitable y co-ordinate for player spawn ('suitable' usually meaning
-       // within 16 nodes of water_level). If a suitable spawn level cannot be
-       // found at the specified (X, Z) 'MAX_MAP_GENERATION_LIMIT' is returned to
-       // signify this and to cause Server::findSpawnPos() to try another (X, Z).
-       virtual int getSpawnLevelAtPoint(v2s16 p) { return 0; }
-
-       // Mapgen management functions
-       static MapgenType getMapgenType(const std::string &mgname);
-       static const char *getMapgenName(MapgenType mgtype);
-       static Mapgen *createMapgen(MapgenType mgtype, int mgid,
-               MapgenParams *params, EmergeManager *emerge);
-       static MapgenParams *createMapgenParams(MapgenType mgtype);
-       static void getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden);
-
-private:
-       // isLiquidHorizontallyFlowable() is a helper function for updateLiquid()
-       // that checks whether there are floodable nodes without liquid beneath
-       // the node at index vi.
-       inline bool isLiquidHorizontallyFlowable(u32 vi, v3s16 em);
-};
-
-/*
-       MapgenBasic is a Mapgen implementation that handles basic functionality
-       the majority of conventional mapgens will probably want to use, but isn't
-       generic enough to be included as part of the base Mapgen class (such as
-       generating biome terrain over terrain node skeletons, generating caves,
-       dungeons, etc.)
-
-       Inherit MapgenBasic instead of Mapgen to add this basic functionality to
-       your mapgen without having to reimplement it.  Feel free to override any of
-       these methods if you desire different or more advanced behavior.
-
-       Note that you must still create your own generateTerrain implementation when
-       inheriting MapgenBasic.
-*/
-class MapgenBasic : public Mapgen {
-public:
-       MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge);
-       virtual ~MapgenBasic();
-
-       virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth);
-       virtual bool generateCaverns(s16 max_stone_y);
-       virtual void generateDungeons(s16 max_stone_y,
-               MgStoneType stone_type, content_t biome_stone);
-       virtual void generateBiomes(MgStoneType *mgstone_type,
-               content_t *biome_stone);
-       virtual void dustTopNodes();
-
-protected:
-       EmergeManager *m_emerge;
-       BiomeManager *m_bmgr;
-
-       Noise *noise_filler_depth;
-
-       v3s16 node_min;
-       v3s16 node_max;
-       v3s16 full_node_min;
-       v3s16 full_node_max;
-
-       // Content required for generateBiomes
-       content_t c_stone;
-       content_t c_desert_stone;
-       content_t c_sandstone;
-       content_t c_water_source;
-       content_t c_river_water_source;
-       content_t c_lava_source;
-
-       // Content required for generateDungeons
-       content_t c_cobble;
-       content_t c_stair_cobble;
-       content_t c_mossycobble;
-       content_t c_stair_desert_stone;
-       content_t c_sandstonebrick;
-       content_t c_stair_sandstone_block;
-
-       int ystride;
-       int zstride;
-       int zstride_1d;
-       int zstride_1u1d;
-
-       u32 spflags;
-
-       NoiseParams np_cave1;
-       NoiseParams np_cave2;
-       NoiseParams np_cavern;
-       float cave_width;
-       float cavern_limit;
-       float cavern_taper;
-       float cavern_threshold;
-       int lava_depth;
-};
diff --git a/src/mapgen/CMakeLists.txt b/src/mapgen/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e74bd85
--- /dev/null
@@ -0,0 +1,19 @@
+set(mapgen_SRCS
+       ${CMAKE_CURRENT_SOURCE_DIR}/cavegen.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/dungeongen.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mapgen_carpathian.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mapgen.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mapgen_flat.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mapgen_fractal.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mapgen_singlenode.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mapgen_v5.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mapgen_v6.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mapgen_v7.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mapgen_valleys.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mg_biome.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mg_decoration.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mg_ore.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/mg_schematic.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/treegen.cpp
+       PARENT_SCOPE
+)
diff --git a/src/mapgen/cavegen.cpp b/src/mapgen/cavegen.cpp
new file mode 100644 (file)
index 0000000..e666345
--- /dev/null
@@ -0,0 +1,882 @@
+/*
+Minetest
+Copyright (C) 2010-2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "util/numeric.h"
+#include "map.h"
+#include "mapgen.h"
+#include "mapgen_v5.h"
+#include "mapgen_v6.h"
+#include "mapgen_v7.h"
+#include "mg_biome.h"
+#include "cavegen.h"
+
+static NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0);
+
+
+////
+//// CavesNoiseIntersection
+////
+
+CavesNoiseIntersection::CavesNoiseIntersection(
+       INodeDefManager *nodedef, BiomeManager *biomemgr, v3s16 chunksize,
+       NoiseParams *np_cave1, NoiseParams *np_cave2, s32 seed, float cave_width)
+{
+       assert(nodedef);
+       assert(biomemgr);
+
+       m_ndef = nodedef;
+       m_bmgr = biomemgr;
+
+       m_csize = chunksize;
+       m_cave_width = cave_width;
+
+       m_ystride    = m_csize.X;
+       m_zstride_1d = m_csize.X * (m_csize.Y + 1);
+
+       // Noises are created using 1-down overgeneration
+       // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for
+       // re-carving the solid overtop placed for blocking sunlight
+       noise_cave1 = new Noise(np_cave1, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
+       noise_cave2 = new Noise(np_cave2, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
+}
+
+
+CavesNoiseIntersection::~CavesNoiseIntersection()
+{
+       delete noise_cave1;
+       delete noise_cave2;
+}
+
+
+void CavesNoiseIntersection::generateCaves(MMVManip *vm,
+       v3s16 nmin, v3s16 nmax, u8 *biomemap)
+{
+       assert(vm);
+       assert(biomemap);
+
+       noise_cave1->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
+       noise_cave2->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
+
+       const v3s16 &em = vm->m_area.getExtent();
+       u32 index2d = 0;  // Biomemap index
+
+       for (s16 z = nmin.Z; z <= nmax.Z; z++)
+       for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) {
+               bool column_is_open = false;  // Is column open to overground
+               bool is_under_river = false;  // Is column under river water
+               bool is_under_tunnel = false;  // Is tunnel or is under tunnel
+               bool is_top_filler_above = false;  // Is top or filler above node
+               // Indexes at column top
+               u32 vi = vm->m_area.index(x, nmax.Y, z);
+               u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
+                       (x - nmin.X);  // 3D noise index
+               // Biome of column
+               Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index2d]);
+               u16 depth_top = biome->depth_top;
+               u16 base_filler = depth_top + biome->depth_filler;
+               u16 depth_riverbed = biome->depth_riverbed;
+               u16 nplaced = 0;
+               // Don't excavate the overgenerated stone at nmax.Y + 1,
+               // this creates a 'roof' over the tunnel, preventing light in
+               // tunnels at mapchunk borders when generating mapchunks upwards.
+               // This 'roof' is removed when the mapchunk above is generated.
+               for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
+                               index3d -= m_ystride,
+                               vm->m_area.add_y(em, vi, -1)) {
+                       content_t c = vm->m_data[vi].getContent();
+
+                       if (c == CONTENT_AIR || c == biome->c_water_top ||
+                                       c == biome->c_water) {
+                               column_is_open = true;
+                               is_top_filler_above = false;
+                               continue;
+                       }
+
+                       if (c == biome->c_river_water) {
+                               column_is_open = true;
+                               is_under_river = true;
+                               is_top_filler_above = false;
+                               continue;
+                       }
+
+                       // Ground
+                       float d1 = contour(noise_cave1->result[index3d]);
+                       float d2 = contour(noise_cave2->result[index3d]);
+
+                       if (d1 * d2 > m_cave_width && m_ndef->get(c).is_ground_content) {
+                               // In tunnel and ground content, excavate
+                               vm->m_data[vi] = MapNode(CONTENT_AIR);
+                               is_under_tunnel = true;
+                               // If tunnel roof is top or filler, replace with stone
+                               if (is_top_filler_above)
+                                       vm->m_data[vi + em.X] = MapNode(biome->c_stone);
+                               is_top_filler_above = false;
+                       } else if (column_is_open && is_under_tunnel &&
+                                       (c == biome->c_stone || c == biome->c_filler)) {
+                               // Tunnel entrance floor, place biome surface nodes
+                               if (is_under_river) {
+                                       if (nplaced < depth_riverbed) {
+                                               vm->m_data[vi] = MapNode(biome->c_riverbed);
+                                               is_top_filler_above = true;
+                                               nplaced++;
+                                       } else {
+                                               // Disable top/filler placement
+                                               column_is_open = false;
+                                               is_under_river = false;
+                                               is_under_tunnel = false;
+                                       }
+                               } else if (nplaced < depth_top) {
+                                       vm->m_data[vi] = MapNode(biome->c_top);
+                                       is_top_filler_above = true;
+                                       nplaced++;
+                               } else if (nplaced < base_filler) {
+                                       vm->m_data[vi] = MapNode(biome->c_filler);
+                                       is_top_filler_above = true;
+                                       nplaced++;
+                               } else {
+                                       // Disable top/filler placement
+                                       column_is_open = false;
+                                       is_under_tunnel = false;
+                               }
+                       } else {
+                               // Not tunnel or tunnel entrance floor
+                               // Check node for possible replacing with stone for tunnel roof
+                               if (c == biome->c_top || c == biome->c_filler)
+                                       is_top_filler_above = true;
+
+                               column_is_open = false;
+                       }
+               }
+       }
+}
+
+
+////
+//// CavernsNoise
+////
+
+CavernsNoise::CavernsNoise(
+       INodeDefManager *nodedef, v3s16 chunksize, NoiseParams *np_cavern,
+       s32 seed, float cavern_limit, float cavern_taper, float cavern_threshold)
+{
+       assert(nodedef);
+
+       m_ndef  = nodedef;
+
+       m_csize            = chunksize;
+       m_cavern_limit     = cavern_limit;
+       m_cavern_taper     = cavern_taper;
+       m_cavern_threshold = cavern_threshold;
+
+       m_ystride = m_csize.X;
+       m_zstride_1d = m_csize.X * (m_csize.Y + 1);
+
+       // Noise is created using 1-down overgeneration
+       // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for
+       // re-carving the solid overtop placed for blocking sunlight
+       noise_cavern = new Noise(np_cavern, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
+
+       c_water_source = m_ndef->getId("mapgen_water_source");
+       if (c_water_source == CONTENT_IGNORE)
+               c_water_source = CONTENT_AIR;
+
+       c_lava_source = m_ndef->getId("mapgen_lava_source");
+       if (c_lava_source == CONTENT_IGNORE)
+               c_lava_source = CONTENT_AIR;
+}
+
+
+CavernsNoise::~CavernsNoise()
+{
+       delete noise_cavern;
+}
+
+
+bool CavernsNoise::generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax)
+{
+       assert(vm);
+
+       // Calculate noise
+       noise_cavern->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
+
+       // Cache cavern_amp values
+       float *cavern_amp = new float[m_csize.Y + 1];
+       u8 cavern_amp_index = 0;  // Index zero at column top
+       for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, cavern_amp_index++) {
+               cavern_amp[cavern_amp_index] =
+                       MYMIN((m_cavern_limit - y) / (float)m_cavern_taper, 1.0f);
+       }
+
+       //// Place nodes
+       bool near_cavern = false;
+       const v3s16 &em = vm->m_area.getExtent();
+       u32 index2d = 0;
+
+       for (s16 z = nmin.Z; z <= nmax.Z; z++)
+       for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) {
+               // Reset cave_amp index to column top
+               cavern_amp_index = 0;
+               // Initial voxelmanip index at column top
+               u32 vi = vm->m_area.index(x, nmax.Y, z);
+               // Initial 3D noise index at column top
+               u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
+                       (x - nmin.X);
+               // Don't excavate the overgenerated stone at node_max.Y + 1,
+               // this creates a 'roof' over the cavern, preventing light in
+               // caverns at mapchunk borders when generating mapchunks upwards.
+               // This 'roof' is excavated when the mapchunk above is generated.
+               for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
+                               index3d -= m_ystride,
+                               vm->m_area.add_y(em, vi, -1),
+                               cavern_amp_index++) {
+                       content_t c = vm->m_data[vi].getContent();
+                       float n_absamp_cavern = fabs(noise_cavern->result[index3d]) *
+                               cavern_amp[cavern_amp_index];
+                       // Disable CavesRandomWalk at a safe distance from caverns
+                       // to avoid excessively spreading liquids in caverns.
+                       if (n_absamp_cavern > m_cavern_threshold - 0.1f) {
+                               near_cavern = true;
+                               if (n_absamp_cavern > m_cavern_threshold &&
+                                               m_ndef->get(c).is_ground_content)
+                                       vm->m_data[vi] = MapNode(CONTENT_AIR);
+                       }
+               }
+       }
+
+       delete[] cavern_amp;
+
+       return near_cavern;
+}
+
+
+////
+//// CavesRandomWalk
+////
+
+CavesRandomWalk::CavesRandomWalk(
+       INodeDefManager *ndef,
+       GenerateNotifier *gennotify,
+       s32 seed,
+       int water_level,
+       content_t water_source,
+       content_t lava_source,
+       int lava_depth)
+{
+       assert(ndef);
+
+       this->ndef           = ndef;
+       this->gennotify      = gennotify;
+       this->seed           = seed;
+       this->water_level    = water_level;
+       this->np_caveliquids = &nparams_caveliquids;
+       this->lava_depth     = lava_depth;
+
+       c_water_source = water_source;
+       if (c_water_source == CONTENT_IGNORE)
+               c_water_source = ndef->getId("mapgen_water_source");
+       if (c_water_source == CONTENT_IGNORE)
+               c_water_source = CONTENT_AIR;
+
+       c_lava_source = lava_source;
+       if (c_lava_source == CONTENT_IGNORE)
+               c_lava_source = ndef->getId("mapgen_lava_source");
+       if (c_lava_source == CONTENT_IGNORE)
+               c_lava_source = CONTENT_AIR;
+}
+
+
+void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
+       PseudoRandom *ps, bool is_large_cave, int max_stone_height, s16 *heightmap)
+{
+       assert(vm);
+       assert(ps);
+
+       this->vm         = vm;
+       this->ps         = ps;
+       this->node_min   = nmin;
+       this->node_max   = nmax;
+       this->heightmap  = heightmap;
+       this->large_cave = is_large_cave;
+
+       this->ystride = nmax.X - nmin.X + 1;
+
+       // Set initial parameters from randomness
+       int dswitchint = ps->range(1, 14);
+       flooded = ps->range(1, 2) == 2;
+
+       if (large_cave) {
+               part_max_length_rs = ps->range(2, 4);
+               tunnel_routepoints = ps->range(5, ps->range(15, 30));
+               min_tunnel_diameter = 5;
+               max_tunnel_diameter = ps->range(7, ps->range(8, 24));
+       } else {
+               part_max_length_rs = ps->range(2, 9);
+               tunnel_routepoints = ps->range(10, ps->range(15, 30));
+               min_tunnel_diameter = 2;
+               max_tunnel_diameter = ps->range(2, 6);
+       }
+
+       large_cave_is_flat = (ps->range(0, 1) == 0);
+
+       main_direction = v3f(0, 0, 0);
+
+       // Allowed route area size in nodes
+       ar = node_max - node_min + v3s16(1, 1, 1);
+       // Area starting point in nodes
+       of = node_min;
+
+       // Allow a bit more
+       //(this should be more than the maximum radius of the tunnel)
+       const s16 insure = 10;
+       s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
+       ar += v3s16(1, 0, 1) * more * 2;
+       of -= v3s16(1, 0, 1) * more;
+
+       route_y_min = 0;
+       // Allow half a diameter + 7 over stone surface
+       route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
+
+       // Limit maximum to area
+       route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
+
+       if (large_cave) {
+               s16 minpos = 0;
+               if (node_min.Y < water_level && node_max.Y > water_level) {
+                       minpos = water_level - max_tunnel_diameter / 3 - of.Y;
+                       route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
+               }
+               route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
+               route_y_min = rangelim(route_y_min, 0, route_y_max);
+       }
+
+       s16 route_start_y_min = route_y_min;
+       s16 route_start_y_max = route_y_max;
+
+       route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
+       route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
+
+       // Randomize starting position
+       orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
+       orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
+       orp.X = (float)(ps->next() % ar.X) + 0.5f;
+
+       // Add generation notify begin event
+       if (gennotify) {
+               v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
+               GenNotifyType notifytype = large_cave ?
+                       GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
+               gennotify->addEvent(notifytype, abs_pos);
+       }
+
+       // Generate some tunnel starting from orp
+       for (u16 j = 0; j < tunnel_routepoints; j++)
+               makeTunnel(j % dswitchint == 0);
+
+       // Add generation notify end event
+       if (gennotify) {
+               v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
+               GenNotifyType notifytype = large_cave ?
+                       GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
+               gennotify->addEvent(notifytype, abs_pos);
+       }
+}
+
+
+void CavesRandomWalk::makeTunnel(bool dirswitch)
+{
+       if (dirswitch && !large_cave) {
+               main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
+               main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
+               main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
+
+               main_direction *= (float)ps->range(0, 10) / 10;
+       }
+
+       // Randomize size
+       s16 min_d = min_tunnel_diameter;
+       s16 max_d = max_tunnel_diameter;
+       rs = ps->range(min_d, max_d);
+       s16 rs_part_max_length_rs = rs * part_max_length_rs;
+
+       v3s16 maxlen;
+       if (large_cave) {
+               maxlen = v3s16(
+                       rs_part_max_length_rs,
+                       rs_part_max_length_rs / 2,
+                       rs_part_max_length_rs
+               );
+       } else {
+               maxlen = v3s16(
+                       rs_part_max_length_rs,
+                       ps->range(1, rs_part_max_length_rs),
+                       rs_part_max_length_rs
+               );
+       }
+
+       v3f vec;
+       // Jump downward sometimes
+       if (!large_cave && ps->range(0, 12) == 0) {
+               vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2;
+               vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
+               vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2;
+       } else {
+               vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2;
+               vec.Y = (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2;
+               vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2;
+       }
+
+       // Do not make caves that are above ground.
+       // It is only necessary to check the startpoint and endpoint.
+       v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
+       v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
+       if (isPosAboveSurface(p1) || isPosAboveSurface(p2))
+               return;
+
+       vec += main_direction;
+
+       v3f rp = orp + vec;
+       if (rp.X < 0)
+               rp.X = 0;
+       else if (rp.X >= ar.X)
+               rp.X = ar.X - 1;
+
+       if (rp.Y < route_y_min)
+               rp.Y = route_y_min;
+       else if (rp.Y >= route_y_max)
+               rp.Y = route_y_max - 1;
+
+       if (rp.Z < 0)
+               rp.Z = 0;
+       else if (rp.Z >= ar.Z)
+               rp.Z = ar.Z - 1;
+
+       vec = rp - orp;
+
+       float veclen = vec.getLength();
+       if (veclen < 0.05f)
+               veclen = 1.0f;
+
+       // Every second section is rough
+       bool randomize_xz = (ps->range(1, 2) == 1);
+
+       // Carve routes
+       for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
+               carveRoute(vec, f, randomize_xz);
+
+       orp = rp;
+}
+
+
+void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz)
+{
+       MapNode airnode(CONTENT_AIR);
+       MapNode waternode(c_water_source);
+       MapNode lavanode(c_lava_source);
+
+       v3s16 startp(orp.X, orp.Y, orp.Z);
+       startp += of;
+
+       float nval = NoisePerlin3D(np_caveliquids, startp.X,
+               startp.Y, startp.Z, seed);
+       MapNode liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ?
+               lavanode : waternode;
+
+       v3f fp = orp + vec * f;
+       fp.X += 0.1f * ps->range(-10, 10);
+       fp.Z += 0.1f * ps->range(-10, 10);
+       v3s16 cp(fp.X, fp.Y, fp.Z);
+
+       s16 d0 = -rs / 2;
+       s16 d1 = d0 + rs;
+       if (randomize_xz) {
+               d0 += ps->range(-1, 1);
+               d1 += ps->range(-1, 1);
+       }
+
+       bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2;
+
+       for (s16 z0 = d0; z0 <= d1; z0++) {
+               s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
+               for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
+                       s16 maxabsxz = MYMAX(abs(x0), abs(z0));
+
+                       s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
+
+                       for (s16 y0 = -si2; y0 <= si2; y0++) {
+                               // Make better floors in small caves
+                               if (flat_cave_floor && y0 <= -rs / 2 && rs <= 7)
+                                       continue;
+
+                               if (large_cave_is_flat) {
+                                       // Make large caves not so tall
+                                       if (rs > 7 && abs(y0) >= rs / 3)
+                                               continue;
+                               }
+
+                               v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
+                               p += of;
+
+                               if (!vm->m_area.contains(p))
+                                       continue;
+
+                               u32 i = vm->m_area.index(p);
+                               content_t c = vm->m_data[i].getContent();
+                               if (!ndef->get(c).is_ground_content)
+                                       continue;
+
+                               if (large_cave) {
+                                       int full_ymin = node_min.Y - MAP_BLOCKSIZE;
+                                       int full_ymax = node_max.Y + MAP_BLOCKSIZE;
+
+                                       if (flooded && full_ymin < water_level && full_ymax > water_level)
+                                               vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
+                                       else if (flooded && full_ymax < water_level)
+                                               vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode;
+                                       else
+                                               vm->m_data[i] = airnode;
+                               } else {
+                                       if (c == CONTENT_IGNORE)
+                                               continue;
+
+                                       vm->m_data[i] = airnode;
+                                       vm->m_flags[i] |= VMANIP_FLAG_CAVE;
+                               }
+                       }
+               }
+       }
+}
+
+
+inline bool CavesRandomWalk::isPosAboveSurface(v3s16 p)
+{
+       if (heightmap != NULL &&
+                       p.Z >= node_min.Z && p.Z <= node_max.Z &&
+                       p.X >= node_min.X && p.X <= node_max.X) {
+               u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
+               if (heightmap[index] < p.Y)
+                       return true;
+       } else if (p.Y > water_level) {
+               return true;
+       }
+
+       return false;
+}
+
+
+////
+//// CavesV6
+////
+
+CavesV6::CavesV6(INodeDefManager *ndef, GenerateNotifier *gennotify,
+       int water_level, content_t water_source, content_t lava_source)
+{
+       assert(ndef);
+
+       this->ndef        = ndef;
+       this->gennotify   = gennotify;
+       this->water_level = water_level;
+
+       c_water_source = water_source;
+       if (c_water_source == CONTENT_IGNORE)
+               c_water_source = ndef->getId("mapgen_water_source");
+       if (c_water_source == CONTENT_IGNORE)
+               c_water_source = CONTENT_AIR;
+
+       c_lava_source = lava_source;
+       if (c_lava_source == CONTENT_IGNORE)
+               c_lava_source = ndef->getId("mapgen_lava_source");
+       if (c_lava_source == CONTENT_IGNORE)
+               c_lava_source = CONTENT_AIR;
+}
+
+
+void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
+       PseudoRandom *ps, PseudoRandom *ps2,
+       bool is_large_cave, int max_stone_height, s16 *heightmap)
+{
+       assert(vm);
+       assert(ps);
+       assert(ps2);
+
+       this->vm         = vm;
+       this->ps         = ps;
+       this->ps2        = ps2;
+       this->node_min   = nmin;
+       this->node_max   = nmax;
+       this->heightmap  = heightmap;
+       this->large_cave = is_large_cave;
+
+       this->ystride = nmax.X - nmin.X + 1;
+
+       // Set initial parameters from randomness
+       min_tunnel_diameter = 2;
+       max_tunnel_diameter = ps->range(2, 6);
+       int dswitchint      = ps->range(1, 14);
+       if (large_cave) {
+               part_max_length_rs  = ps->range(2, 4);
+               tunnel_routepoints  = ps->range(5, ps->range(15, 30));
+               min_tunnel_diameter = 5;
+               max_tunnel_diameter = ps->range(7, ps->range(8, 24));
+       } else {
+               part_max_length_rs = ps->range(2, 9);
+               tunnel_routepoints = ps->range(10, ps->range(15, 30));
+       }
+       large_cave_is_flat = (ps->range(0, 1) == 0);
+
+       main_direction = v3f(0, 0, 0);
+
+       // Allowed route area size in nodes
+       ar = node_max - node_min + v3s16(1, 1, 1);
+       // Area starting point in nodes
+       of = node_min;
+
+       // Allow a bit more
+       //(this should be more than the maximum radius of the tunnel)
+       const s16 max_spread_amount = MAP_BLOCKSIZE;
+       const s16 insure = 10;
+       s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1);
+       ar += v3s16(1, 0, 1) * more * 2;
+       of -= v3s16(1, 0, 1) * more;
+
+       route_y_min = 0;
+       // Allow half a diameter + 7 over stone surface
+       route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7;
+
+       // Limit maximum to area
+       route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
+
+       if (large_cave) {
+               s16 minpos = 0;
+               if (node_min.Y < water_level && node_max.Y > water_level) {
+                       minpos = water_level - max_tunnel_diameter / 3 - of.Y;
+                       route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
+               }
+               route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
+               route_y_min = rangelim(route_y_min, 0, route_y_max);
+       }
+
+       s16 route_start_y_min = route_y_min;
+       s16 route_start_y_max = route_y_max;
+
+       route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
+       route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
+
+       // Randomize starting position
+       orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
+       orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
+       orp.X = (float)(ps->next() % ar.X) + 0.5f;
+
+       // Add generation notify begin event
+       if (gennotify != NULL) {
+               v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
+               GenNotifyType notifytype = large_cave ?
+                       GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
+               gennotify->addEvent(notifytype, abs_pos);
+       }
+
+       // Generate some tunnel starting from orp
+       for (u16 j = 0; j < tunnel_routepoints; j++)
+               makeTunnel(j % dswitchint == 0);
+
+       // Add generation notify end event
+       if (gennotify != NULL) {
+               v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
+               GenNotifyType notifytype = large_cave ?
+                       GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
+               gennotify->addEvent(notifytype, abs_pos);
+       }
+}
+
+
+void CavesV6::makeTunnel(bool dirswitch)
+{
+       if (dirswitch && !large_cave) {
+               main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
+               main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
+               main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
+
+               main_direction *= (float)ps->range(0, 10) / 10;
+       }
+
+       // Randomize size
+       s16 min_d = min_tunnel_diameter;
+       s16 max_d = max_tunnel_diameter;
+       rs = ps->range(min_d, max_d);
+       s16 rs_part_max_length_rs = rs * part_max_length_rs;
+
+       v3s16 maxlen;
+       if (large_cave) {
+               maxlen = v3s16(
+                       rs_part_max_length_rs,
+                       rs_part_max_length_rs / 2,
+                       rs_part_max_length_rs
+               );
+       } else {
+               maxlen = v3s16(
+                       rs_part_max_length_rs,
+                       ps->range(1, rs_part_max_length_rs),
+                       rs_part_max_length_rs
+               );
+       }
+
+       v3f vec;
+       vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
+       vec.Y = (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2;
+       vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
+
+       // Jump downward sometimes
+       if (!large_cave && ps->range(0, 12) == 0) {
+               vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
+               vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
+               vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
+       }
+
+       // Do not make caves that are entirely above ground, to fix shadow bugs
+       // caused by overgenerated large caves.
+       // It is only necessary to check the startpoint and endpoint.
+       v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
+       v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
+
+       // If startpoint and endpoint are above ground, disable placement of nodes
+       // in carveRoute while still running all PseudoRandom calls to ensure caves
+       // are consistent with existing worlds.
+       bool tunnel_above_ground =
+               p1.Y > getSurfaceFromHeightmap(p1) &&
+               p2.Y > getSurfaceFromHeightmap(p2);
+
+       vec += main_direction;
+
+       v3f rp = orp + vec;
+       if (rp.X < 0)
+               rp.X = 0;
+       else if (rp.X >= ar.X)
+               rp.X = ar.X - 1;
+
+       if (rp.Y < route_y_min)
+               rp.Y = route_y_min;
+       else if (rp.Y >= route_y_max)
+               rp.Y = route_y_max - 1;
+
+       if (rp.Z < 0)
+               rp.Z = 0;
+       else if (rp.Z >= ar.Z)
+               rp.Z = ar.Z - 1;
+
+       vec = rp - orp;
+
+       float veclen = vec.getLength();
+       // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE
+       if (veclen < 0.05f)
+               veclen = 1.0f;
+
+       // Every second section is rough
+       bool randomize_xz = (ps2->range(1, 2) == 1);
+
+       // Carve routes
+       for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
+               carveRoute(vec, f, randomize_xz, tunnel_above_ground);
+
+       orp = rp;
+}
+
+
+void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz,
+       bool tunnel_above_ground)
+{
+       MapNode airnode(CONTENT_AIR);
+       MapNode waternode(c_water_source);
+       MapNode lavanode(c_lava_source);
+
+       v3s16 startp(orp.X, orp.Y, orp.Z);
+       startp += of;
+
+       v3f fp = orp + vec * f;
+       fp.X += 0.1f * ps->range(-10, 10);
+       fp.Z += 0.1f * ps->range(-10, 10);
+       v3s16 cp(fp.X, fp.Y, fp.Z);
+
+       s16 d0 = -rs / 2;
+       s16 d1 = d0 + rs;
+       if (randomize_xz) {
+               d0 += ps->range(-1, 1);
+               d1 += ps->range(-1, 1);
+       }
+
+       for (s16 z0 = d0; z0 <= d1; z0++) {
+               s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
+               for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
+                       if (tunnel_above_ground)
+                               continue;
+
+                       s16 maxabsxz = MYMAX(abs(x0), abs(z0));
+                       s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
+                       for (s16 y0 = -si2; y0 <= si2; y0++) {
+                               if (large_cave_is_flat) {
+                                       // Make large caves not so tall
+                                       if (rs > 7 && abs(y0) >= rs / 3)
+                                               continue;
+                               }
+
+                               v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
+                               p += of;
+
+                               if (!vm->m_area.contains(p))
+                                       continue;
+
+                               u32 i = vm->m_area.index(p);
+                               content_t c = vm->m_data[i].getContent();
+                               if (!ndef->get(c).is_ground_content)
+                                       continue;
+
+                               if (large_cave) {
+                                       int full_ymin = node_min.Y - MAP_BLOCKSIZE;
+                                       int full_ymax = node_max.Y + MAP_BLOCKSIZE;
+
+                                       if (full_ymin < water_level && full_ymax > water_level) {
+                                               vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
+                                       } else if (full_ymax < water_level) {
+                                               vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode;
+                                       } else {
+                                               vm->m_data[i] = airnode;
+                                       }
+                               } else {
+                                       if (c == CONTENT_IGNORE || c == CONTENT_AIR)
+                                               continue;
+
+                                       vm->m_data[i] = airnode;
+                                       vm->m_flags[i] |= VMANIP_FLAG_CAVE;
+                               }
+                       }
+               }
+       }
+}
+
+
+inline s16 CavesV6::getSurfaceFromHeightmap(v3s16 p)
+{
+       if (heightmap != NULL &&
+                       p.Z >= node_min.Z && p.Z <= node_max.Z &&
+                       p.X >= node_min.X && p.X <= node_max.X) {
+               u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
+               return heightmap[index];
+       }
+
+       return water_level;
+
+}
diff --git a/src/mapgen/cavegen.h b/src/mapgen/cavegen.h
new file mode 100644 (file)
index 0000000..ce146e0
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+Minetest
+Copyright (C) 2010-2013 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#define VMANIP_FLAG_CAVE VOXELFLAG_CHECKED1
+
+class GenerateNotifier;
+
+/*
+       CavesNoiseIntersection is a cave digging algorithm that carves smooth,
+       web-like, continuous tunnels at points where the density of the intersection
+       between two separate 3d noises is above a certain value.  This value,
+       cave_width, can be modified to set the effective width of these tunnels.
+
+       This algorithm is relatively heavyweight, taking ~80ms to generate an
+       80x80x80 chunk of map on a modern processor.  Use sparingly!
+
+       TODO(hmmmm): Remove dependency on biomes
+       TODO(hmmmm): Find alternative to overgeneration as solution for sunlight issue
+*/
+class CavesNoiseIntersection
+{
+public:
+       CavesNoiseIntersection(INodeDefManager *nodedef, BiomeManager *biomemgr,
+                       v3s16 chunksize, NoiseParams *np_cave1, NoiseParams *np_cave2,
+                       s32 seed, float cave_width);
+       ~CavesNoiseIntersection();
+
+       void generateCaves(MMVManip *vm, v3s16 nmin, v3s16 nmax, u8 *biomemap);
+
+private:
+       INodeDefManager *m_ndef;
+       BiomeManager *m_bmgr;
+
+       // configurable parameters
+       v3s16 m_csize;
+       float m_cave_width;
+
+       // intermediate state variables
+       u16 m_ystride;
+       u16 m_zstride_1d;
+
+       Noise *noise_cave1;
+       Noise *noise_cave2;
+};
+
+/*
+       CavernsNoise is a cave digging algorithm
+*/
+class CavernsNoise
+{
+public:
+       CavernsNoise(INodeDefManager *nodedef, v3s16 chunksize, NoiseParams *np_cavern,
+                       s32 seed, float cavern_limit, float cavern_taper,
+                       float cavern_threshold);
+       ~CavernsNoise();
+
+       bool generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax);
+
+private:
+       INodeDefManager *m_ndef;
+
+       // configurable parameters
+       v3s16 m_csize;
+       float m_cavern_limit;
+       float m_cavern_taper;
+       float m_cavern_threshold;
+
+       // intermediate state variables
+       u16 m_ystride;
+       u16 m_zstride_1d;
+
+       Noise *noise_cavern;
+
+       content_t c_water_source;
+       content_t c_lava_source;
+};
+
+/*
+       CavesRandomWalk is an implementation of a cave-digging algorithm that
+       operates on the principle of a "random walk" to approximate the stochiastic
+       activity of cavern development.
+
+       In summary, this algorithm works by carving a randomly sized tunnel in a
+       random direction a random amount of times, randomly varying in width.
+       All randomness here is uniformly distributed; alternative distributions have
+       not yet been implemented.
+
+       This algorithm is very fast, executing in less than 1ms on average for an
+       80x80x80 chunk of map on a modern processor.
+*/
+class CavesRandomWalk
+{
+public:
+       MMVManip *vm;
+       INodeDefManager *ndef;
+       GenerateNotifier *gennotify;
+       s16 *heightmap;
+
+       // configurable parameters
+       s32 seed;
+       int water_level;
+       int lava_depth;
+       NoiseParams *np_caveliquids;
+
+       // intermediate state variables
+       u16 ystride;
+
+       s16 min_tunnel_diameter;
+       s16 max_tunnel_diameter;
+       u16 tunnel_routepoints;
+       int part_max_length_rs;
+
+       bool large_cave;
+       bool large_cave_is_flat;
+       bool flooded;
+
+       s16 max_stone_y;
+       v3s16 node_min;
+       v3s16 node_max;
+
+       v3f orp;  // starting point, relative to caved space
+       v3s16 of; // absolute coordinates of caved space
+       v3s16 ar; // allowed route area
+       s16 rs;   // tunnel radius size
+       v3f main_direction;
+
+       s16 route_y_min;
+       s16 route_y_max;
+
+       PseudoRandom *ps;
+
+       content_t c_water_source;
+       content_t c_lava_source;
+
+       // ndef is a mandatory parameter.
+       // If gennotify is NULL, generation events are not logged.
+       CavesRandomWalk(INodeDefManager *ndef, GenerateNotifier *gennotify = NULL,
+                       s32 seed = 0, int water_level = 1,
+                       content_t water_source = CONTENT_IGNORE,
+                       content_t lava_source = CONTENT_IGNORE, int lava_depth = -256);
+
+       // vm and ps are mandatory parameters.
+       // If heightmap is NULL, the surface level at all points is assumed to
+       // be water_level.
+       void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps,
+                       bool is_large_cave, int max_stone_height, s16 *heightmap);
+
+private:
+       void makeTunnel(bool dirswitch);
+       void carveRoute(v3f vec, float f, bool randomize_xz);
+
+       inline bool isPosAboveSurface(v3s16 p);
+};
+
+/*
+       CavesV6 is the original version of caves used with Mapgen V6.
+
+       Though it uses the same fundamental algorithm as CavesRandomWalk, it is made
+       separate to preserve the exact sequence of PseudoRandom calls - any change
+       to this ordering results in the output being radically different.
+       Because caves in Mapgen V6 are responsible for a large portion of the basic
+       terrain shape, modifying this will break our contract of reverse
+       compatibility for a 'stable' mapgen such as V6.
+
+       tl;dr,
+       *** DO NOT TOUCH THIS CLASS UNLESS YOU KNOW WHAT YOU ARE DOING ***
+*/
+class CavesV6
+{
+public:
+       MMVManip *vm;
+       INodeDefManager *ndef;
+       GenerateNotifier *gennotify;
+       PseudoRandom *ps;
+       PseudoRandom *ps2;
+
+       // configurable parameters
+       s16 *heightmap;
+       content_t c_water_source;
+       content_t c_lava_source;
+       int water_level;
+
+       // intermediate state variables
+       u16 ystride;
+
+       s16 min_tunnel_diameter;
+       s16 max_tunnel_diameter;
+       u16 tunnel_routepoints;
+       int part_max_length_rs;
+
+       bool large_cave;
+       bool large_cave_is_flat;
+
+       v3s16 node_min;
+       v3s16 node_max;
+
+       v3f orp;  // starting point, relative to caved space
+       v3s16 of; // absolute coordinates of caved space
+       v3s16 ar; // allowed route area
+       s16 rs;   // tunnel radius size
+       v3f main_direction;
+
+       s16 route_y_min;
+       s16 route_y_max;
+
+       // ndef is a mandatory parameter.
+       // If gennotify is NULL, generation events are not logged.
+       CavesV6(INodeDefManager *ndef, GenerateNotifier *gennotify = NULL,
+                       int water_level = 1, content_t water_source = CONTENT_IGNORE,
+                       content_t lava_source = CONTENT_IGNORE);
+
+       // vm, ps, and ps2 are mandatory parameters.
+       // If heightmap is NULL, the surface level at all points is assumed to
+       // be water_level.
+       void makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax, PseudoRandom *ps,
+                       PseudoRandom *ps2, bool is_large_cave, int max_stone_height,
+                       s16 *heightmap = NULL);
+
+private:
+       void makeTunnel(bool dirswitch);
+       void carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground);
+
+       inline s16 getSurfaceFromHeightmap(v3s16 p);
+};
diff --git a/src/mapgen/dungeongen.cpp b/src/mapgen/dungeongen.cpp
new file mode 100644 (file)
index 0000000..fa867b3
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+Minetest
+Copyright (C) 2010-2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "dungeongen.h"
+#include "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "nodedef.h"
+#include "settings.h"
+
+//#define DGEN_USE_TORCHES
+
+NoiseParams nparams_dungeon_density(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0);
+NoiseParams nparams_dungeon_alt_wall(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+DungeonGen::DungeonGen(INodeDefManager *ndef,
+       GenerateNotifier *gennotify, DungeonParams *dparams)
+{
+       assert(ndef);
+
+       this->ndef      = ndef;
+       this->gennotify = gennotify;
+
+#ifdef DGEN_USE_TORCHES
+       c_torch  = ndef->getId("default:torch");
+#endif
+
+       if (dparams) {
+               memcpy(&dp, dparams, sizeof(dp));
+       } else {
+               // Default dungeon parameters
+               dp.seed = 0;
+
+               dp.c_water       = ndef->getId("mapgen_water_source");
+               dp.c_river_water = ndef->getId("mapgen_river_water_source");
+               dp.c_wall        = ndef->getId("mapgen_cobble");
+               dp.c_alt_wall    = ndef->getId("mapgen_mossycobble");
+               dp.c_stair       = ndef->getId("mapgen_stair_cobble");
+
+               if (dp.c_river_water == CONTENT_IGNORE)
+                       dp.c_river_water = ndef->getId("mapgen_water_source");
+
+               dp.diagonal_dirs       = false;
+               dp.only_in_ground      = true;
+               dp.holesize            = v3s16(1, 2, 1);
+               dp.corridor_len_min    = 1;
+               dp.corridor_len_max    = 13;
+               dp.room_size_min       = v3s16(4, 4, 4);
+               dp.room_size_max       = v3s16(8, 6, 8);
+               dp.room_size_large_min = v3s16(8, 8, 8);
+               dp.room_size_large_max = v3s16(16, 16, 16);
+               dp.rooms_min           = 2;
+               dp.rooms_max           = 16;
+               dp.y_min               = -MAX_MAP_GENERATION_LIMIT;
+               dp.y_max               = MAX_MAP_GENERATION_LIMIT;
+               dp.notifytype          = GENNOTIFY_DUNGEON;
+
+               dp.np_density  = nparams_dungeon_density;
+               dp.np_alt_wall = nparams_dungeon_alt_wall;
+       }
+}
+
+
+void DungeonGen::generate(MMVManip *vm, u32 bseed, v3s16 nmin, v3s16 nmax)
+{
+       assert(vm);
+
+       //TimeTaker t("gen dungeons");
+       if (nmin.Y < dp.y_min || nmax.Y > dp.y_max)
+               return;
+
+       float nval_density = NoisePerlin3D(&dp.np_density, nmin.X, nmin.Y, nmin.Z, dp.seed);
+       if (nval_density < 1.0f)
+               return;
+
+       static const bool preserve_ignore = !g_settings->getBool("projecting_dungeons");
+
+       this->vm = vm;
+       this->blockseed = bseed;
+       random.seed(bseed + 2);
+
+       // Dungeon generator doesn't modify places which have this set
+       vm->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE);
+
+       if (dp.only_in_ground) {
+               // Set all air and water to be untouchable to make dungeons open to
+               // caves and open air. Optionally set ignore to be untouchable to
+               // prevent protruding dungeons.
+               for (s16 z = nmin.Z; z <= nmax.Z; z++) {
+                       for (s16 y = nmin.Y; y <= nmax.Y; y++) {
+                               u32 i = vm->m_area.index(nmin.X, y, z);
+                               for (s16 x = nmin.X; x <= nmax.X; x++) {
+                                       content_t c = vm->m_data[i].getContent();
+                                       if (c == CONTENT_AIR || c == dp.c_water ||
+                                                       (preserve_ignore && c == CONTENT_IGNORE) ||
+                                                       c == dp.c_river_water)
+                                               vm->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE;
+                                       i++;
+                               }
+                       }
+               }
+       }
+
+       // Add them
+       for (u32 i = 0; i < floor(nval_density); i++)
+               makeDungeon(v3s16(1, 1, 1) * MAP_BLOCKSIZE);
+
+       // Optionally convert some structure to alternative structure
+       if (dp.c_alt_wall == CONTENT_IGNORE)
+               return;
+
+       for (s16 z = nmin.Z; z <= nmax.Z; z++)
+       for (s16 y = nmin.Y; y <= nmax.Y; y++) {
+               u32 i = vm->m_area.index(nmin.X, y, z);
+               for (s16 x = nmin.X; x <= nmax.X; x++) {
+                       if (vm->m_data[i].getContent() == dp.c_wall) {
+                               if (NoisePerlin3D(&dp.np_alt_wall, x, y, z, blockseed) > 0.0f)
+                                       vm->m_data[i].setContent(dp.c_alt_wall);
+                       }
+                       i++;
+               }
+       }
+
+       //printf("== gen dungeons: %dms\n", t.stop());
+}
+
+
+void DungeonGen::makeDungeon(v3s16 start_padding)
+{
+       const v3s16 &areasize = vm->m_area.getExtent();
+       v3s16 roomsize;
+       v3s16 roomplace;
+
+       /*
+               Find place for first room.
+               There is a 1 in 4 chance of the first room being 'large',
+               all other rooms are not 'large'.
+       */
+       bool fits = false;
+       for (u32 i = 0; i < 100 && !fits; i++) {
+               bool is_large_room = ((random.next() & 3) == 1);
+               if (is_large_room) {
+                       roomsize.Z = random.range(
+                               dp.room_size_large_min.Z, dp.room_size_large_max.Z);
+                       roomsize.Y = random.range(
+                               dp.room_size_large_min.Y, dp.room_size_large_max.Y);
+                       roomsize.X = random.range(
+                               dp.room_size_large_min.X, dp.room_size_large_max.X);
+               } else {
+                       roomsize.Z = random.range(dp.room_size_min.Z, dp.room_size_max.Z);
+                       roomsize.Y = random.range(dp.room_size_min.Y, dp.room_size_max.Y);
+                       roomsize.X = random.range(dp.room_size_min.X, dp.room_size_max.X);
+               }
+
+               // start_padding is used to disallow starting the generation of
+               // a dungeon in a neighboring generation chunk
+               roomplace = vm->m_area.MinEdge + start_padding;
+               roomplace.Z += random.range(0, areasize.Z - roomsize.Z - start_padding.Z);
+               roomplace.Y += random.range(0, areasize.Y - roomsize.Y - start_padding.Y);
+               roomplace.X += random.range(0, areasize.X - roomsize.X - start_padding.X);
+
+               /*
+                       Check that we're not putting the room to an unknown place,
+                       otherwise it might end up floating in the air
+               */
+               fits = true;
+               for (s16 z = 0; z < roomsize.Z; z++)
+               for (s16 y = 0; y < roomsize.Y; y++)
+               for (s16 x = 0; x < roomsize.X; x++) {
+                       v3s16 p = roomplace + v3s16(x, y, z);
+                       u32 vi = vm->m_area.index(p);
+                       if ((vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) ||
+                                       vm->m_data[vi].getContent() == CONTENT_IGNORE) {
+                               fits = false;
+                               break;
+                       }
+               }
+       }
+       // No place found
+       if (!fits)
+               return;
+
+       /*
+               Stores the center position of the last room made, so that
+               a new corridor can be started from the last room instead of
+               the new room, if chosen so.
+       */
+       v3s16 last_room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
+
+       u32 room_count = random.range(dp.rooms_min, dp.rooms_max);
+       for (u32 i = 0; i < room_count; i++) {
+               // Make a room to the determined place
+               makeRoom(roomsize, roomplace);
+
+               v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2);
+               if (gennotify)
+                       gennotify->addEvent(dp.notifytype, room_center);
+
+#ifdef DGEN_USE_TORCHES
+               // Place torch at room center (for testing)
+               vm->m_data[vm->m_area.index(room_center)] = MapNode(c_torch);
+#endif
+
+               // Quit if last room
+               if (i == room_count - 1)
+                       break;
+
+               // Determine walker start position
+
+               bool start_in_last_room = (random.range(0, 2) != 0);
+
+               v3s16 walker_start_place;
+
+               if (start_in_last_room) {
+                       walker_start_place = last_room_center;
+               } else {
+                       walker_start_place = room_center;
+                       // Store center of current room as the last one
+                       last_room_center = room_center;
+               }
+
+               // Create walker and find a place for a door
+               v3s16 doorplace;
+               v3s16 doordir;
+
+               m_pos = walker_start_place;
+               if (!findPlaceForDoor(doorplace, doordir))
+                       return;
+
+               if (random.range(0, 1) == 0)
+                       // Make the door
+                       makeDoor(doorplace, doordir);
+               else
+                       // Don't actually make a door
+                       doorplace -= doordir;
+
+               // Make a random corridor starting from the door
+               v3s16 corridor_end;
+               v3s16 corridor_end_dir;
+               makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir);
+
+               // Find a place for a random sized room
+               roomsize.Z = random.range(dp.room_size_min.Z, dp.room_size_max.Z);
+               roomsize.Y = random.range(dp.room_size_min.Y, dp.room_size_max.Y);
+               roomsize.X = random.range(dp.room_size_min.X, dp.room_size_max.X);
+
+               m_pos = corridor_end;
+               m_dir = corridor_end_dir;
+               if (!findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace))
+                       return;
+
+               if (random.range(0, 1) == 0)
+                       // Make the door
+                       makeDoor(doorplace, doordir);
+               else
+                       // Don't actually make a door
+                       roomplace -= doordir;
+
+       }
+}
+
+
+void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace)
+{
+       MapNode n_wall(dp.c_wall);
+       MapNode n_air(CONTENT_AIR);
+
+       // Make +-X walls
+       for (s16 z = 0; z < roomsize.Z; z++)
+       for (s16 y = 0; y < roomsize.Y; y++) {
+               {
+                       v3s16 p = roomplace + v3s16(0, y, z);
+                       if (!vm->m_area.contains(p))
+                               continue;
+                       u32 vi = vm->m_area.index(p);
+                       if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
+                               continue;
+                       vm->m_data[vi] = n_wall;
+               }
+               {
+                       v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z);
+                       if (!vm->m_area.contains(p))
+                               continue;
+                       u32 vi = vm->m_area.index(p);
+                       if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
+                               continue;
+                       vm->m_data[vi] = n_wall;
+               }
+       }
+
+       // Make +-Z walls
+       for (s16 x = 0; x < roomsize.X; x++)
+       for (s16 y = 0; y < roomsize.Y; y++) {
+               {
+                       v3s16 p = roomplace + v3s16(x, y, 0);
+                       if (!vm->m_area.contains(p))
+                               continue;
+                       u32 vi = vm->m_area.index(p);
+                       if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
+                               continue;
+                       vm->m_data[vi] = n_wall;
+               }
+               {
+                       v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1);
+                       if (!vm->m_area.contains(p))
+                               continue;
+                       u32 vi = vm->m_area.index(p);
+                       if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
+                               continue;
+                       vm->m_data[vi] = n_wall;
+               }
+       }
+
+       // Make +-Y walls (floor and ceiling)
+       for (s16 z = 0; z < roomsize.Z; z++)
+       for (s16 x = 0; x < roomsize.X; x++) {
+               {
+                       v3s16 p = roomplace + v3s16(x, 0, z);
+                       if (!vm->m_area.contains(p))
+                               continue;
+                       u32 vi = vm->m_area.index(p);
+                       if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
+                               continue;
+                       vm->m_data[vi] = n_wall;
+               }
+               {
+                       v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z);
+                       if (!vm->m_area.contains(p))
+                               continue;
+                       u32 vi = vm->m_area.index(p);
+                       if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE)
+                               continue;
+                       vm->m_data[vi] = n_wall;
+               }
+       }
+
+       // Fill with air
+       for (s16 z = 1; z < roomsize.Z - 1; z++)
+       for (s16 y = 1; y < roomsize.Y - 1; y++)
+       for (s16 x = 1; x < roomsize.X - 1; x++) {
+               v3s16 p = roomplace + v3s16(x, y, z);
+               if (!vm->m_area.contains(p))
+                       continue;
+               u32 vi = vm->m_area.index(p);
+               vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE;
+               vm->m_data[vi] = n_air;
+       }
+}
+
+
+void DungeonGen::makeFill(v3s16 place, v3s16 size,
+       u8 avoid_flags, MapNode n, u8 or_flags)
+{
+       for (s16 z = 0; z < size.Z; z++)
+       for (s16 y = 0; y < size.Y; y++)
+       for (s16 x = 0; x < size.X; x++) {
+               v3s16 p = place + v3s16(x, y, z);
+               if (!vm->m_area.contains(p))
+                       continue;
+               u32 vi = vm->m_area.index(p);
+               if (vm->m_flags[vi] & avoid_flags)
+                       continue;
+               vm->m_flags[vi] |= or_flags;
+               vm->m_data[vi] = n;
+       }
+}
+
+
+void DungeonGen::makeHole(v3s16 place)
+{
+       makeFill(place, dp.holesize, 0, MapNode(CONTENT_AIR),
+               VMANIP_FLAG_DUNGEON_INSIDE);
+}
+
+
+void DungeonGen::makeDoor(v3s16 doorplace, v3s16 doordir)
+{
+       makeHole(doorplace);
+
+#ifdef DGEN_USE_TORCHES
+       // Place torch (for testing)
+       vm->m_data[vm->m_area.index(doorplace)] = MapNode(c_torch);
+#endif
+}
+
+
+void DungeonGen::makeCorridor(v3s16 doorplace, v3s16 doordir,
+       v3s16 &result_place, v3s16 &result_dir)
+{
+       makeHole(doorplace);
+       v3s16 p0 = doorplace;
+       v3s16 dir = doordir;
+       u32 length = random.range(dp.corridor_len_min, dp.corridor_len_max);
+       u32 partlength = random.range(dp.corridor_len_min, dp.corridor_len_max);
+       u32 partcount = 0;
+       s16 make_stairs = 0;
+
+       if (random.next() % 2 == 0 && partlength >= 3)
+               make_stairs = random.next() % 2 ? 1 : -1;
+
+       for (u32 i = 0; i < length; i++) {
+               v3s16 p = p0 + dir;
+               if (partcount != 0)
+                       p.Y += make_stairs;
+
+               // Check segment of minimum size corridor is in voxelmanip
+               if (vm->m_area.contains(p) && vm->m_area.contains(p + v3s16(0, 1, 0))) {
+                       if (make_stairs) {
+                               makeFill(p + v3s16(-1, -1, -1),
+                                       dp.holesize + v3s16(2, 3, 2),
+                                       VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
+                                       MapNode(dp.c_wall),
+                                       0);
+                               makeHole(p);
+                               makeHole(p - dir);
+
+                               // TODO: fix stairs code so it works 100%
+                               // (quite difficult)
+
+                               // exclude stairs from the bottom step
+                               // exclude stairs from diagonal steps
+                               if (((dir.X ^ dir.Z) & 1) &&
+                                               (((make_stairs ==  1) && i != 0) ||
+                                               ((make_stairs == -1) && i != length - 1))) {
+                                       // rotate face 180 deg if
+                                       // making stairs backwards
+                                       int facedir = dir_to_facedir(dir * make_stairs);
+                                       v3s16 ps = p;
+                                       u16 stair_width = (dir.Z != 0) ? dp.holesize.X : dp.holesize.Z;
+                                       // Stair width direction vector
+                                       v3s16 swv = (dir.Z != 0) ? v3s16(1, 0, 0) : v3s16(0, 0, 1);
+
+                                       for (u16 st = 0; st < stair_width; st++) {
+                                               u32 vi = vm->m_area.index(ps.X - dir.X, ps.Y - 1, ps.Z - dir.Z);
+                                               if (vm->m_area.contains(ps + v3s16(-dir.X, -1, -dir.Z)) &&
+                                                               vm->m_data[vi].getContent() == dp.c_wall)
+                                                       vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
+
+                                               vi = vm->m_area.index(ps.X, ps.Y, ps.Z);
+                                               if (vm->m_area.contains(ps) &&
+                                                               vm->m_data[vi].getContent() == dp.c_wall)
+                                                       vm->m_data[vi] = MapNode(dp.c_stair, 0, facedir);
+
+                                               ps += swv;
+                                       }
+                               }
+                       } else {
+                               makeFill(p + v3s16(-1, -1, -1),
+                                       dp.holesize + v3s16(2, 2, 2),
+                                       VMANIP_FLAG_DUNGEON_UNTOUCHABLE,
+                                       MapNode(dp.c_wall),
+                                       0);
+                               makeHole(p);
+                       }
+
+                       p0 = p;
+               } else {
+                       // Can't go here, turn away
+                       dir = turn_xz(dir, random.range(0, 1));
+                       make_stairs = -make_stairs;
+                       partcount = 0;
+                       partlength = random.range(1, length);
+                       continue;
+               }
+
+               partcount++;
+               if (partcount >= partlength) {
+                       partcount = 0;
+
+                       dir = random_turn(random, dir);
+
+                       partlength = random.range(1, length);
+
+                       make_stairs = 0;
+                       if (random.next() % 2 == 0 && partlength >= 3)
+                               make_stairs = random.next() % 2 ? 1 : -1;
+               }
+       }
+       result_place = p0;
+       result_dir = dir;
+}
+
+
+bool DungeonGen::findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir)
+{
+       for (u32 i = 0; i < 100; i++) {
+               v3s16 p = m_pos + m_dir;
+               v3s16 p1 = p + v3s16(0, 1, 0);
+               if (!vm->m_area.contains(p) || !vm->m_area.contains(p1) || i % 4 == 0) {
+                       randomizeDir();
+                       continue;
+               }
+               if (vm->getNodeNoExNoEmerge(p).getContent() == dp.c_wall &&
+                               vm->getNodeNoExNoEmerge(p1).getContent() == dp.c_wall) {
+                       // Found wall, this is a good place!
+                       result_place = p;
+                       result_dir = m_dir;
+                       // Randomize next direction
+                       randomizeDir();
+                       return true;
+               }
+               /*
+                       Determine where to move next
+               */
+               // Jump one up if the actual space is there
+               if (vm->getNodeNoExNoEmerge(p +
+                               v3s16(0, 0, 0)).getContent() == dp.c_wall &&
+                               vm->getNodeNoExNoEmerge(p +
+                               v3s16(0, 1, 0)).getContent() == CONTENT_AIR &&
+                               vm->getNodeNoExNoEmerge(p +
+                               v3s16(0, 2, 0)).getContent() == CONTENT_AIR)
+                       p += v3s16(0,1,0);
+               // Jump one down if the actual space is there
+               if (vm->getNodeNoExNoEmerge(p +
+                               v3s16(0, 1, 0)).getContent() == dp.c_wall &&
+                               vm->getNodeNoExNoEmerge(p +
+                               v3s16(0, 0, 0)).getContent() == CONTENT_AIR &&
+                               vm->getNodeNoExNoEmerge(p +
+                               v3s16(0, -1, 0)).getContent() == CONTENT_AIR)
+                       p += v3s16(0, -1, 0);
+               // Check if walking is now possible
+               if (vm->getNodeNoExNoEmerge(p).getContent() != CONTENT_AIR ||
+                               vm->getNodeNoExNoEmerge(p +
+                               v3s16(0, 1, 0)).getContent() != CONTENT_AIR) {
+                       // Cannot continue walking here
+                       randomizeDir();
+                       continue;
+               }
+               // Move there
+               m_pos = p;
+       }
+       return false;
+}
+
+
+bool DungeonGen::findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
+       v3s16 &result_doordir, v3s16 &result_roomplace)
+{
+       for (s16 trycount = 0; trycount < 30; trycount++) {
+               v3s16 doorplace;
+               v3s16 doordir;
+               bool r = findPlaceForDoor(doorplace, doordir);
+               if (!r)
+                       continue;
+               v3s16 roomplace;
+               // X east, Z north, Y up
+               if (doordir == v3s16(1, 0, 0)) // X+
+                       roomplace = doorplace +
+                               v3s16(0, -1, random.range(-roomsize.Z + 2, -2));
+               if (doordir == v3s16(-1, 0, 0)) // X-
+                       roomplace = doorplace +
+                               v3s16(-roomsize.X + 1, -1, random.range(-roomsize.Z + 2, -2));
+               if (doordir == v3s16(0, 0, 1)) // Z+
+                       roomplace = doorplace +
+                               v3s16(random.range(-roomsize.X + 2, -2), -1, 0);
+               if (doordir == v3s16(0, 0, -1)) // Z-
+                       roomplace = doorplace +
+                               v3s16(random.range(-roomsize.X + 2, -2), -1, -roomsize.Z + 1);
+
+               // Check fit
+               bool fits = true;
+               for (s16 z = 1; z < roomsize.Z - 1; z++)
+               for (s16 y = 1; y < roomsize.Y - 1; y++)
+               for (s16 x = 1; x < roomsize.X - 1; x++) {
+                       v3s16 p = roomplace + v3s16(x, y, z);
+                       if (!vm->m_area.contains(p)) {
+                               fits = false;
+                               break;
+                       }
+                       if (vm->m_flags[vm->m_area.index(p)] & VMANIP_FLAG_DUNGEON_INSIDE) {
+                               fits = false;
+                               break;
+                       }
+               }
+               if (!fits) {
+                       // Find new place
+                       continue;
+               }
+               result_doorplace = doorplace;
+               result_doordir   = doordir;
+               result_roomplace = roomplace;
+               return true;
+       }
+       return false;
+}
+
+
+v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs)
+{
+       // Make diagonal directions somewhat rare
+       if (diagonal_dirs && (random.next() % 4 == 0)) {
+               v3s16 dir;
+               int trycount = 0;
+
+               do {
+                       trycount++;
+
+                       dir.Z = random.next() % 3 - 1;
+                       dir.Y = 0;
+                       dir.X = random.next() % 3 - 1;
+               } while ((dir.X == 0 || dir.Z == 0) && trycount < 10);
+
+               return dir;
+       }
+
+       if (random.next() % 2 == 0)
+               return random.next() % 2 ? v3s16(-1, 0, 0) : v3s16(1, 0, 0);
+
+       return random.next() % 2 ? v3s16(0, 0, -1) : v3s16(0, 0, 1);
+}
+
+
+v3s16 turn_xz(v3s16 olddir, int t)
+{
+       v3s16 dir;
+       if (t == 0) {
+               // Turn right
+               dir.X = olddir.Z;
+               dir.Z = -olddir.X;
+               dir.Y = olddir.Y;
+       } else {
+               // Turn left
+               dir.X = -olddir.Z;
+               dir.Z = olddir.X;
+               dir.Y = olddir.Y;
+       }
+       return dir;
+}
+
+
+v3s16 random_turn(PseudoRandom &random, v3s16 olddir)
+{
+       int turn = random.range(0, 2);
+       v3s16 dir;
+       if (turn == 0)
+               // Go straight
+               dir = olddir;
+       else if (turn == 1)
+               // Turn right
+               dir = turn_xz(olddir, 0);
+       else
+               // Turn left
+               dir = turn_xz(olddir, 1);
+       return dir;
+}
+
+
+int dir_to_facedir(v3s16 d)
+{
+       if (abs(d.X) > abs(d.Z))
+               return d.X < 0 ? 3 : 1;
+
+       return d.Z < 0 ? 2 : 0;
+}
diff --git a/src/mapgen/dungeongen.h b/src/mapgen/dungeongen.h
new file mode 100644 (file)
index 0000000..6799db7
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+Minetest
+Copyright (C) 2010-2013 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 Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "voxel.h"
+#include "noise.h"
+#include "mapgen.h"
+
+#define VMANIP_FLAG_DUNGEON_INSIDE VOXELFLAG_CHECKED1
+#define VMANIP_FLAG_DUNGEON_PRESERVE VOXELFLAG_CHECKED2
+#define VMANIP_FLAG_DUNGEON_UNTOUCHABLE (\
+               VMANIP_FLAG_DUNGEON_INSIDE|VMANIP_FLAG_DUNGEON_PRESERVE)
+
+class MMVManip;
+class INodeDefManager;
+
+v3s16 rand_ortho_dir(PseudoRandom &random, bool diagonal_dirs);
+v3s16 turn_xz(v3s16 olddir, int t);
+v3s16 random_turn(PseudoRandom &random, v3s16 olddir);
+int dir_to_facedir(v3s16 d);
+
+
+struct DungeonParams {
+       s32 seed;
+
+       content_t c_water;
+       content_t c_river_water;
+       content_t c_wall;
+       content_t c_alt_wall;
+       content_t c_stair;
+
+       bool diagonal_dirs;
+       bool only_in_ground;
+       v3s16 holesize;
+       u16 corridor_len_min;
+       u16 corridor_len_max;
+       v3s16 room_size_min;
+       v3s16 room_size_max;
+       v3s16 room_size_large_min;
+       v3s16 room_size_large_max;
+       u16 rooms_min;
+       u16 rooms_max;
+       s16 y_min;
+       s16 y_max;
+       GenNotifyType notifytype;
+
+       NoiseParams np_density;
+       NoiseParams np_alt_wall;
+};
+
+class DungeonGen {
+public:
+       MMVManip *vm;
+       INodeDefManager *ndef;
+       GenerateNotifier *gennotify;
+
+       u32 blockseed;
+       PseudoRandom random;
+       v3s16 csize;
+
+       content_t c_torch;
+       DungeonParams dp;
+
+       // RoomWalker
+       v3s16 m_pos;
+       v3s16 m_dir;
+
+       DungeonGen(INodeDefManager *ndef,
+               GenerateNotifier *gennotify, DungeonParams *dparams);
+
+       void generate(MMVManip *vm, u32 bseed,
+               v3s16 full_node_min, v3s16 full_node_max);
+
+       void makeDungeon(v3s16 start_padding);
+       void makeRoom(v3s16 roomsize, v3s16 roomplace);
+       void makeCorridor(v3s16 doorplace, v3s16 doordir,
+               v3s16 &result_place, v3s16 &result_dir);
+       void makeDoor(v3s16 doorplace, v3s16 doordir);
+       void makeFill(v3s16 place, v3s16 size, u8 avoid_flags, MapNode n, u8 or_flags);
+       void makeHole(v3s16 place);
+
+       bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir);
+       bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace,
+                       v3s16 &result_doordir, v3s16 &result_roomplace);
+
+       inline void randomizeDir()
+       {
+               m_dir = rand_ortho_dir(random, dp.diagonal_dirs);
+       }
+};
+
+extern NoiseParams nparams_dungeon_density;
+extern NoiseParams nparams_dungeon_alt_wall;
diff --git a/src/mapgen/mapgen.cpp b/src/mapgen/mapgen.cpp
new file mode 100644 (file)
index 0000000..f73e16d
--- /dev/null
@@ -0,0 +1,1140 @@
+/*
+Minetest
+Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "gamedef.h"
+#include "mg_biome.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "content_sao.h"
+#include "nodedef.h"
+#include "emerge.h"
+#include "voxelalgorithms.h"
+#include "porting.h"
+#include "profiler.h"
+#include "settings.h"
+#include "treegen.h"
+#include "serialization.h"
+#include "util/serialize.h"
+#include "util/numeric.h"
+#include "filesys.h"
+#include "log.h"
+#include "mapgen_carpathian.h"
+#include "mapgen_flat.h"
+#include "mapgen_fractal.h"
+#include "mapgen_v5.h"
+#include "mapgen_v6.h"
+#include "mapgen_v7.h"
+#include "mapgen_valleys.h"
+#include "mapgen_singlenode.h"
+#include "cavegen.h"
+#include "dungeongen.h"
+
+FlagDesc flagdesc_mapgen[] = {
+       {"caves",       MG_CAVES},
+       {"dungeons",    MG_DUNGEONS},
+       {"light",       MG_LIGHT},
+       {"decorations", MG_DECORATIONS},
+       {NULL,       0}
+};
+
+FlagDesc flagdesc_gennotify[] = {
+       {"dungeon",          1 << GENNOTIFY_DUNGEON},
+       {"temple",           1 << GENNOTIFY_TEMPLE},
+       {"cave_begin",       1 << GENNOTIFY_CAVE_BEGIN},
+       {"cave_end",         1 << GENNOTIFY_CAVE_END},
+       {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN},
+       {"large_cave_end",   1 << GENNOTIFY_LARGECAVE_END},
+       {"decoration",       1 << GENNOTIFY_DECORATION},
+       {NULL,               0}
+};
+
+struct MapgenDesc {
+       const char *name;
+       bool is_user_visible;
+};
+
+////
+//// Built-in mapgens
+////
+
+static MapgenDesc g_reg_mapgens[] = {
+       {"v5",         true},
+       {"v6",         true},
+       {"v7",         true},
+       {"flat",       true},
+       {"fractal",    true},
+       {"valleys",    true},
+       {"singlenode", true},
+       {"carpathian", true},
+};
+
+STATIC_ASSERT(
+       ARRLEN(g_reg_mapgens) == MAPGEN_INVALID,
+       registered_mapgens_is_wrong_size);
+
+////
+//// Mapgen
+////
+
+Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) :
+       gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids)
+{
+       id           = mapgenid;
+       water_level  = params->water_level;
+       mapgen_limit = params->mapgen_limit;
+       flags        = params->flags;
+       csize        = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
+
+       /*
+               We are losing half our entropy by doing this, but it is necessary to
+               preserve reverse compatibility.  If the top half of our current 64 bit
+               seeds ever starts getting used, existing worlds will break due to a
+               different hash outcome and no way to differentiate between versions.
+
+               A solution could be to add a new bit to designate that the top half of
+               the seed value should be used, essentially a 1-bit version code, but
+               this would require increasing the total size of a seed to 9 bytes (yuck)
+
+               It's probably okay if this never gets fixed.  4.2 billion possibilities
+               ought to be enough for anyone.
+       */
+       seed = (s32)params->seed;
+
+       ndef      = emerge->ndef;
+}
+
+
+MapgenType Mapgen::getMapgenType(const std::string &mgname)
+{
+       for (size_t i = 0; i != ARRLEN(g_reg_mapgens); i++) {
+               if (mgname == g_reg_mapgens[i].name)
+                       return (MapgenType)i;
+       }
+
+       return MAPGEN_INVALID;
+}
+
+
+const char *Mapgen::getMapgenName(MapgenType mgtype)
+{
+       size_t index = (size_t)mgtype;
+       if (index == MAPGEN_INVALID || index >= ARRLEN(g_reg_mapgens))
+               return "invalid";
+
+       return g_reg_mapgens[index].name;
+}
+
+
+Mapgen *Mapgen::createMapgen(MapgenType mgtype, int mgid,
+       MapgenParams *params, EmergeManager *emerge)
+{
+       switch (mgtype) {
+       case MAPGEN_CARPATHIAN:
+               return new MapgenCarpathian(mgid, (MapgenCarpathianParams *)params, emerge);
+       case MAPGEN_FLAT:
+               return new MapgenFlat(mgid, (MapgenFlatParams *)params, emerge);
+       case MAPGEN_FRACTAL:
+               return new MapgenFractal(mgid, (MapgenFractalParams *)params, emerge);
+       case MAPGEN_SINGLENODE:
+               return new MapgenSinglenode(mgid, (MapgenSinglenodeParams *)params, emerge);
+       case MAPGEN_V5:
+               return new MapgenV5(mgid, (MapgenV5Params *)params, emerge);
+       case MAPGEN_V6:
+               return new MapgenV6(mgid, (MapgenV6Params *)params, emerge);
+       case MAPGEN_V7:
+               return new MapgenV7(mgid, (MapgenV7Params *)params, emerge);
+       case MAPGEN_VALLEYS:
+               return new MapgenValleys(mgid, (MapgenValleysParams *)params, emerge);
+       default:
+               return NULL;
+       }
+}
+
+
+MapgenParams *Mapgen::createMapgenParams(MapgenType mgtype)
+{
+       switch (mgtype) {
+       case MAPGEN_CARPATHIAN:
+               return new MapgenCarpathianParams;
+       case MAPGEN_FLAT:
+               return new MapgenFlatParams;
+       case MAPGEN_FRACTAL:
+               return new MapgenFractalParams;
+       case MAPGEN_SINGLENODE:
+               return new MapgenSinglenodeParams;
+       case MAPGEN_V5:
+               return new MapgenV5Params;
+       case MAPGEN_V6:
+               return new MapgenV6Params;
+       case MAPGEN_V7:
+               return new MapgenV7Params;
+       case MAPGEN_VALLEYS:
+               return new MapgenValleysParams;
+       default:
+               return NULL;
+       }
+}
+
+
+void Mapgen::getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden)
+{
+       for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) {
+               if (include_hidden || g_reg_mapgens[i].is_user_visible)
+                       mgnames->push_back(g_reg_mapgens[i].name);
+       }
+}
+
+
+u32 Mapgen::getBlockSeed(v3s16 p, s32 seed)
+{
+       return (u32)seed   +
+               p.Z * 38134234 +
+               p.Y * 42123    +
+               p.X * 23;
+}
+
+
+u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed)
+{
+       u32 n = 1619 * p.X + 31337 * p.Y + 52591 * p.Z + 1013 * seed;
+       n = (n >> 13) ^ n;
+       return (n * (n * n * 60493 + 19990303) + 1376312589);
+}
+
+
+// Returns Y one under area minimum if not found
+s16 Mapgen::findGroundLevelFull(v2s16 p2d)
+{
+       const v3s16 &em = vm->m_area.getExtent();
+       s16 y_nodes_max = vm->m_area.MaxEdge.Y;
+       s16 y_nodes_min = vm->m_area.MinEdge.Y;
+       u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
+       s16 y;
+
+       for (y = y_nodes_max; y >= y_nodes_min; y--) {
+               MapNode &n = vm->m_data[i];
+               if (ndef->get(n).walkable)
+                       break;
+
+               vm->m_area.add_y(em, i, -1);
+       }
+       return (y >= y_nodes_min) ? y : y_nodes_min - 1;
+}
+
+
+// Returns -MAX_MAP_GENERATION_LIMIT if not found
+s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
+{
+       const v3s16 &em = vm->m_area.getExtent();
+       u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
+       s16 y;
+
+       for (y = ymax; y >= ymin; y--) {
+               MapNode &n = vm->m_data[i];
+               if (ndef->get(n).walkable)
+                       break;
+
+               vm->m_area.add_y(em, i, -1);
+       }
+       return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
+}
+
+
+// Returns -MAX_MAP_GENERATION_LIMIT if not found or if ground is found first
+s16 Mapgen::findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax)
+{
+       const v3s16 &em = vm->m_area.getExtent();
+       u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
+       s16 y;
+
+       for (y = ymax; y >= ymin; y--) {
+               MapNode &n = vm->m_data[i];
+               if (ndef->get(n).walkable)
+                       return -MAX_MAP_GENERATION_LIMIT;
+
+               if (ndef->get(n).isLiquid())
+                       break;
+
+               vm->m_area.add_y(em, i, -1);
+       }
+       return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
+}
+
+
+void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
+{
+       if (!heightmap)
+               return;
+
+       //TimeTaker t("Mapgen::updateHeightmap", NULL, PRECISION_MICRO);
+       int index = 0;
+       for (s16 z = nmin.Z; z <= nmax.Z; z++) {
+               for (s16 x = nmin.X; x <= nmax.X; x++, index++) {
+                       s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
+
+                       heightmap[index] = y;
+               }
+       }
+}
+
+
+void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
+       s16 *floors, s16 *ceilings, u16 *num_floors, u16 *num_ceilings)
+{
+       u16 floor_i = 0;
+       u16 ceiling_i = 0;
+       const v3s16 &em = vm->m_area.getExtent();
+
+       bool is_walkable = false;
+       u32 vi = vm->m_area.index(p2d.X, ymax, p2d.Y);
+       MapNode mn_max = vm->m_data[vi];
+       bool walkable_above = ndef->get(mn_max).walkable;
+       vm->m_area.add_y(em, vi, -1);
+
+       for (s16 y = ymax - 1; y >= ymin; y--) {
+               MapNode mn = vm->m_data[vi];
+               is_walkable = ndef->get(mn).walkable;
+
+               if (is_walkable && !walkable_above) {
+                       floors[floor_i] = y;
+                       floor_i++;
+               } else if (!is_walkable && walkable_above) {
+                       ceilings[ceiling_i] = y + 1;
+                       ceiling_i++;
+               }
+
+               vm->m_area.add_y(em, vi, -1);
+               walkable_above = is_walkable;
+       }
+
+       *num_floors = floor_i;
+       *num_ceilings = ceiling_i;
+}
+
+
+inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
+{
+       u32 vi_neg_x = vi;
+       vm->m_area.add_x(em, vi_neg_x, -1);
+       if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) {
+               const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]);
+               if (c_nx.floodable && !c_nx.isLiquid())
+                       return true;
+       }
+       u32 vi_pos_x = vi;
+       vm->m_area.add_x(em, vi_pos_x, +1);
+       if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) {
+               const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]);
+               if (c_px.floodable && !c_px.isLiquid())
+                       return true;
+       }
+       u32 vi_neg_z = vi;
+       vm->m_area.add_z(em, vi_neg_z, -1);
+       if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) {
+               const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]);
+               if (c_nz.floodable && !c_nz.isLiquid())
+                       return true;
+       }
+       u32 vi_pos_z = vi;
+       vm->m_area.add_z(em, vi_pos_z, +1);
+       if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) {
+               const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]);
+               if (c_pz.floodable && !c_pz.isLiquid())
+                       return true;
+       }
+       return false;
+}
+
+void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax)
+{
+       bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;
+       const v3s16 &em  = vm->m_area.getExtent();
+
+       for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++)
+       for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) {
+               wasignored = true;
+               wasliquid = false;
+               waschecked = false;
+               waspushed = false;
+
+               u32 vi = vm->m_area.index(x, nmax.Y, z);
+               for (s16 y = nmax.Y; y >= nmin.Y; y--) {
+                       isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE;
+                       isliquid = ndef->get(vm->m_data[vi]).isLiquid();
+
+                       if (isignored || wasignored || isliquid == wasliquid) {
+                               // Neither topmost node of liquid column nor topmost node below column
+                               waschecked = false;
+                               waspushed = false;
+                       } else if (isliquid) {
+                               // This is the topmost node in the column
+                               bool ispushed = false;
+                               if (isLiquidHorizontallyFlowable(vi, em)) {
+                                       trans_liquid->push_back(v3s16(x, y, z));
+                                       ispushed = true;
+                               }
+                               // Remember waschecked and waspushed to avoid repeated
+                               // checks/pushes in case the column consists of only this node
+                               waschecked = true;
+                               waspushed = ispushed;
+                       } else {
+                               // This is the topmost node below a liquid column
+                               u32 vi_above = vi;
+                               vm->m_area.add_y(em, vi_above, 1);
+                               if (!waspushed && (ndef->get(vm->m_data[vi]).floodable ||
+                                               (!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) {
+                                       // Push back the lowest node in the column which is one
+                                       // node above this one
+                                       trans_liquid->push_back(v3s16(x, y + 1, z));
+                               }
+                       }
+
+                       wasliquid = isliquid;
+                       wasignored = isignored;
+                       vm->m_area.add_y(em, vi, -1);
+               }
+       }
+}
+
+
+void Mapgen::setLighting(u8 light, v3s16 nmin, v3s16 nmax)
+{
+       ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
+       VoxelArea a(nmin, nmax);
+
+       for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
+               for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
+                       u32 i = vm->m_area.index(a.MinEdge.X, y, z);
+                       for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++)
+                               vm->m_data[i].param1 = light;
+               }
+       }
+}
+
+
+void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light)
+{
+       if (light <= 1 || !a.contains(p))
+               return;
+
+       u32 vi = vm->m_area.index(p);
+       MapNode &n = vm->m_data[vi];
+
+       // Decay light in each of the banks separately
+       u8 light_day = light & 0x0F;
+       if (light_day > 0)
+               light_day -= 0x01;
+
+       u8 light_night = light & 0xF0;
+       if (light_night > 0)
+               light_night -= 0x10;
+
+       // Bail out only if we have no more light from either bank to propogate, or
+       // we hit a solid block that light cannot pass through.
+       if ((light_day  <= (n.param1 & 0x0F) &&
+               light_night <= (n.param1 & 0xF0)) ||
+               !ndef->get(n).light_propagates)
+               return;
+
+       // Since this recursive function only terminates when there is no light from
+       // either bank left, we need to take the max of both banks into account for
+       // the case where spreading has stopped for one light bank but not the other.
+       light = MYMAX(light_day, n.param1 & 0x0F) |
+                       MYMAX(light_night, n.param1 & 0xF0);
+
+       n.param1 = light;
+
+       lightSpread(a, p + v3s16(0, 0, 1), light);
+       lightSpread(a, p + v3s16(0, 1, 0), light);
+       lightSpread(a, p + v3s16(1, 0, 0), light);
+       lightSpread(a, p - v3s16(0, 0, 1), light);
+       lightSpread(a, p - v3s16(0, 1, 0), light);
+       lightSpread(a, p - v3s16(1, 0, 0), light);
+}
+
+
+void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
+       bool propagate_shadow)
+{
+       ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
+       //TimeTaker t("updateLighting");
+
+       propagateSunlight(nmin, nmax, propagate_shadow);
+       spreadLight(full_nmin, full_nmax);
+
+       //printf("updateLighting: %dms\n", t.stop());
+}
+
+
+void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow)
+{
+       //TimeTaker t("propagateSunlight");
+       VoxelArea a(nmin, nmax);
+       bool block_is_underground = (water_level >= nmax.Y);
+       const v3s16 &em = vm->m_area.getExtent();
+
+       // NOTE: Direct access to the low 4 bits of param1 is okay here because,
+       // by definition, sunlight will never be in the night lightbank.
+
+       for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
+               for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) {
+                       // see if we can get a light value from the overtop
+                       u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z);
+                       if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
+                               if (block_is_underground)
+                                       continue;
+                       } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN &&
+                                       propagate_shadow) {
+                               continue;
+                       }
+                       vm->m_area.add_y(em, i, -1);
+
+                       for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) {
+                               MapNode &n = vm->m_data[i];
+                               if (!ndef->get(n).sunlight_propagates)
+                                       break;
+                               n.param1 = LIGHT_SUN;
+                               vm->m_area.add_y(em, i, -1);
+                       }
+               }
+       }
+       //printf("propagateSunlight: %dms\n", t.stop());
+}
+
+
+void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
+{
+       //TimeTaker t("spreadLight");
+       VoxelArea a(nmin, nmax);
+
+       for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
+               for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
+                       u32 i = vm->m_area.index(a.MinEdge.X, y, z);
+                       for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) {
+                               MapNode &n = vm->m_data[i];
+                               if (n.getContent() == CONTENT_IGNORE)
+                                       continue;
+
+                               const ContentFeatures &cf = ndef->get(n);
+                               if (!cf.light_propagates)
+                                       continue;
+
+                               // TODO(hmmmmm): Abstract away direct param1 accesses with a
+                               // wrapper, but something lighter than MapNode::get/setLight
+
+                               u8 light_produced = cf.light_source;
+                               if (light_produced)
+                                       n.param1 = light_produced | (light_produced << 4);
+
+                               u8 light = n.param1;
+                               if (light) {
+                                       lightSpread(a, v3s16(x,     y,     z + 1), light);
+                                       lightSpread(a, v3s16(x,     y + 1, z    ), light);
+                                       lightSpread(a, v3s16(x + 1, y,     z    ), light);
+                                       lightSpread(a, v3s16(x,     y,     z - 1), light);
+                                       lightSpread(a, v3s16(x,     y - 1, z    ), light);
+                                       lightSpread(a, v3s16(x - 1, y,     z    ), light);
+                               }
+                       }
+               }
+       }
+
+       //printf("spreadLight: %dms\n", t.stop());
+}
+
+
+////
+//// MapgenBasic
+////
+
+MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge)
+       : Mapgen(mapgenid, params, emerge)
+{
+       this->m_emerge = emerge;
+       this->m_bmgr   = emerge->biomemgr;
+
+       //// Here, 'stride' refers to the number of elements needed to skip to index
+       //// an adjacent element for that coordinate in noise/height/biome maps
+       //// (*not* vmanip content map!)
+
+       // Note there is no X stride explicitly defined.  Items adjacent in the X
+       // coordinate are assumed to be adjacent in memory as well (i.e. stride of 1).
+
+       // Number of elements to skip to get to the next Y coordinate
+       this->ystride = csize.X;
+
+       // Number of elements to skip to get to the next Z coordinate
+       this->zstride = csize.X * csize.Y;
+
+       // Z-stride value for maps oversized for 1-down overgeneration
+       this->zstride_1d = csize.X * (csize.Y + 1);
+
+       // Z-stride value for maps oversized for 1-up 1-down overgeneration
+       this->zstride_1u1d = csize.X * (csize.Y + 2);
+
+       //// Allocate heightmap
+       this->heightmap = new s16[csize.X * csize.Z];
+
+       //// Initialize biome generator
+       // TODO(hmmmm): should we have a way to disable biomemanager biomes?
+       biomegen = m_bmgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize);
+       biomemap = biomegen->biomemap;
+
+       //// Look up some commonly used content
+       c_stone              = ndef->getId("mapgen_stone");
+       c_desert_stone       = ndef->getId("mapgen_desert_stone");
+       c_sandstone          = ndef->getId("mapgen_sandstone");
+       c_water_source       = ndef->getId("mapgen_water_source");
+       c_river_water_source = ndef->getId("mapgen_river_water_source");
+       c_lava_source        = ndef->getId("mapgen_lava_source");
+
+       // Fall back to more basic content if not defined
+       // river_water_source cannot fallback to water_source because river water
+       // needs to be non-renewable and have a short flow range.
+       if (c_desert_stone == CONTENT_IGNORE)
+               c_desert_stone = c_stone;
+       if (c_sandstone == CONTENT_IGNORE)
+               c_sandstone = c_stone;
+
+       //// Content used for dungeon generation
+       c_cobble                = ndef->getId("mapgen_cobble");
+       c_mossycobble           = ndef->getId("mapgen_mossycobble");
+       c_stair_cobble          = ndef->getId("mapgen_stair_cobble");
+       c_stair_desert_stone    = ndef->getId("mapgen_stair_desert_stone");
+       c_sandstonebrick        = ndef->getId("mapgen_sandstonebrick");
+       c_stair_sandstone_block = ndef->getId("mapgen_stair_sandstone_block");
+
+       // Fall back to more basic content if not defined
+       if (c_mossycobble == CONTENT_IGNORE)
+               c_mossycobble = c_cobble;
+       if (c_stair_cobble == CONTENT_IGNORE)
+               c_stair_cobble = c_cobble;
+       if (c_stair_desert_stone == CONTENT_IGNORE)
+               c_stair_desert_stone = c_desert_stone;
+       if (c_sandstonebrick == CONTENT_IGNORE)
+               c_sandstonebrick = c_sandstone;
+       if (c_stair_sandstone_block == CONTENT_IGNORE)
+               c_stair_sandstone_block = c_sandstonebrick;
+}
+
+
+MapgenBasic::~MapgenBasic()
+{
+       delete biomegen;
+       delete []heightmap;
+}
+
+
+void MapgenBasic::generateBiomes(MgStoneType *mgstone_type,
+       content_t *biome_stone)
+{
+       // can't generate biomes without a biome generator!
+       assert(biomegen);
+       assert(biomemap);
+
+       const v3s16 &em = vm->m_area.getExtent();
+       u32 index = 0;
+       MgStoneType stone_type = MGSTONE_OTHER;
+       content_t c_biome_stone = c_stone;
+
+       noise_filler_depth->perlinMap2D(node_min.X, node_min.Z);
+
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+               Biome *biome = NULL;
+               u16 depth_top = 0;
+               u16 base_filler = 0;
+               u16 depth_water_top = 0;
+               u16 depth_riverbed = 0;
+               s16 biome_y_min = -MAX_MAP_GENERATION_LIMIT;
+               u32 vi = vm->m_area.index(x, node_max.Y, z);
+
+               // Check node at base of mapchunk above, either a node of a previously
+               // generated mapchunk or if not, a node of overgenerated base terrain.
+               content_t c_above = vm->m_data[vi + em.X].getContent();
+               bool air_above = c_above == CONTENT_AIR;
+               bool river_water_above = c_above == c_river_water_source;
+               bool water_above = c_above == c_water_source || river_water_above;
+
+               biomemap[index] = BIOME_NONE;
+
+               // If there is air or water above enable top/filler placement, otherwise force
+               // nplaced to stone level by setting a number exceeding any possible filler depth.
+               u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
+
+               for (s16 y = node_max.Y; y >= node_min.Y; y--) {
+                       content_t c = vm->m_data[vi].getContent();
+                       // Biome is (re)calculated:
+                       // 1. At the surface of stone below air or water.
+                       // 2. At the surface of water below air.
+                       // 3. When stone or water is detected but biome has not yet been calculated.
+                       // 4. When stone or water is detected just below a biome's lower limit.
+                       bool is_stone_surface = (c == c_stone) &&
+                               (air_above || water_above || !biome || y < biome_y_min); // 1, 3, 4
+
+                       bool is_water_surface =
+                               (c == c_water_source || c == c_river_water_source) &&
+                               (air_above || !biome || y < biome_y_min); // 2, 3, 4
+
+                       if (is_stone_surface || is_water_surface) {
+                               // (Re)calculate biome
+                               biome = biomegen->getBiomeAtIndex(index, y);
+
+                               if (biomemap[index] == BIOME_NONE && is_stone_surface)
+                                       biomemap[index] = biome->index;
+
+                               depth_top = biome->depth_top;
+                               base_filler = MYMAX(depth_top +
+                                       biome->depth_filler +
+                                       noise_filler_depth->result[index], 0.0f);
+                               depth_water_top = biome->depth_water_top;
+                               depth_riverbed = biome->depth_riverbed;
+                               biome_y_min = biome->y_min;
+
+                               // Detect stone type for dungeons during every biome calculation.
+                               // If none detected the last selected biome stone is chosen.
+                               if (biome->c_stone == c_stone)
+                                       stone_type = MGSTONE_STONE;
+                               else if (biome->c_stone == c_desert_stone)
+                                       stone_type = MGSTONE_DESERT_STONE;
+                               else if (biome->c_stone == c_sandstone)
+                                       stone_type = MGSTONE_SANDSTONE;
+
+                               c_biome_stone = biome->c_stone;
+                       }
+
+                       if (c == c_stone) {
+                               content_t c_below = vm->m_data[vi - em.X].getContent();
+
+                               // If the node below isn't solid, make this node stone, so that
+                               // any top/filler nodes above are structurally supported.
+                               // This is done by aborting the cycle of top/filler placement
+                               // immediately by forcing nplaced to stone level.
+                               if (c_below == CONTENT_AIR
+                                               || c_below == c_water_source
+                                               || c_below == c_river_water_source)
+                                       nplaced = U16_MAX;
+
+                               if (river_water_above) {
+                                       if (nplaced < depth_riverbed) {
+                                               vm->m_data[vi] = MapNode(biome->c_riverbed);
+                                               nplaced++;
+                                       } else {
+                                               nplaced = U16_MAX;  // Disable top/filler placement
+                                               river_water_above = false;
+                                       }
+                               } else if (nplaced < depth_top) {
+                                       vm->m_data[vi] = MapNode(biome->c_top);
+                                       nplaced++;
+                               } else if (nplaced < base_filler) {
+                                       vm->m_data[vi] = MapNode(biome->c_filler);
+                                       nplaced++;
+                               } else {
+                                       vm->m_data[vi] = MapNode(biome->c_stone);
+                                       nplaced = U16_MAX;  // Disable top/filler placement
+                               }
+
+                               air_above = false;
+                               water_above = false;
+                       } else if (c == c_water_source) {
+                               vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
+                                               ? biome->c_water_top : biome->c_water);
+                               nplaced = 0;  // Enable top/filler placement for next surface
+                               air_above = false;
+                               water_above = true;
+                       } else if (c == c_river_water_source) {
+                               vm->m_data[vi] = MapNode(biome->c_river_water);
+                               nplaced = 0;  // Enable riverbed placement for next surface
+                               air_above = false;
+                               water_above = true;
+                               river_water_above = true;
+                       } else if (c == CONTENT_AIR) {
+                               nplaced = 0;  // Enable top/filler placement for next surface
+                               air_above = true;
+                               water_above = false;
+                       } else {  // Possible various nodes overgenerated from neighbouring mapchunks
+                               nplaced = U16_MAX;  // Disable top/filler placement
+                               air_above = false;
+                               water_above = false;
+                       }
+
+                       vm->m_area.add_y(em, vi, -1);
+               }
+       }
+
+       *mgstone_type = stone_type;
+       *biome_stone = c_biome_stone;
+}
+
+
+void MapgenBasic::dustTopNodes()
+{
+       if (node_max.Y < water_level)
+               return;
+
+       const v3s16 &em = vm->m_area.getExtent();
+       u32 index = 0;
+
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+               Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]);
+
+               if (biome->c_dust == CONTENT_IGNORE)
+                       continue;
+
+               u32 vi = vm->m_area.index(x, full_node_max.Y, z);
+               content_t c_full_max = vm->m_data[vi].getContent();
+               s16 y_start;
+
+               if (c_full_max == CONTENT_AIR) {
+                       y_start = full_node_max.Y - 1;
+               } else if (c_full_max == CONTENT_IGNORE) {
+                       vi = vm->m_area.index(x, node_max.Y + 1, z);
+                       content_t c_max = vm->m_data[vi].getContent();
+
+                       if (c_max == CONTENT_AIR)
+                               y_start = node_max.Y;
+                       else
+                               continue;
+               } else {
+                       continue;
+               }
+
+               vi = vm->m_area.index(x, y_start, z);
+               for (s16 y = y_start; y >= node_min.Y - 1; y--) {
+                       if (vm->m_data[vi].getContent() != CONTENT_AIR)
+                               break;
+
+                       vm->m_area.add_y(em, vi, -1);
+               }
+
+               content_t c = vm->m_data[vi].getContent();
+               if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
+                       vm->m_area.add_y(em, vi, 1);
+                       vm->m_data[vi] = MapNode(biome->c_dust);
+               }
+       }
+}
+
+
+void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth)
+{
+       if (max_stone_y < node_min.Y)
+               return;
+
+       CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize,
+               &np_cave1, &np_cave2, seed, cave_width);
+
+       caves_noise.generateCaves(vm, node_min, node_max, biomemap);
+
+       if (node_max.Y > large_cave_depth)
+               return;
+
+       PseudoRandom ps(blockseed + 21343);
+       u32 bruises_count = ps.range(0, 2);
+       for (u32 i = 0; i < bruises_count; i++) {
+               CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
+                       c_water_source, CONTENT_IGNORE, lava_depth);
+
+               cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
+       }
+}
+
+
+bool MapgenBasic::generateCaverns(s16 max_stone_y)
+{
+       if (node_min.Y > max_stone_y || node_min.Y > cavern_limit)
+               return false;
+
+       CavernsNoise caverns_noise(ndef, csize, &np_cavern,
+               seed, cavern_limit, cavern_taper, cavern_threshold);
+
+       return caverns_noise.generateCaverns(vm, node_min, node_max);
+}
+
+
+void MapgenBasic::generateDungeons(s16 max_stone_y,
+       MgStoneType stone_type, content_t biome_stone)
+{
+       if (max_stone_y < node_min.Y)
+               return;
+
+       DungeonParams dp;
+
+       dp.seed             = seed;
+       dp.c_water          = c_water_source;
+       dp.c_river_water    = c_river_water_source;
+
+       dp.only_in_ground   = true;
+       dp.corridor_len_min = 1;
+       dp.corridor_len_max = 13;
+       dp.rooms_min        = 2;
+       dp.rooms_max        = 16;
+       dp.y_min            = -MAX_MAP_GENERATION_LIMIT;
+       dp.y_max            = MAX_MAP_GENERATION_LIMIT;
+
+       dp.np_density       = nparams_dungeon_density;
+       dp.np_alt_wall      = nparams_dungeon_alt_wall;
+
+       switch (stone_type) {
+       default:
+       case MGSTONE_STONE:
+               dp.c_wall              = c_cobble;
+               dp.c_alt_wall          = c_mossycobble;
+               dp.c_stair             = c_stair_cobble;
+
+               dp.diagonal_dirs       = false;
+               dp.holesize            = v3s16(1, 2, 1);
+               dp.room_size_min       = v3s16(4, 4, 4);
+               dp.room_size_max       = v3s16(8, 6, 8);
+               dp.room_size_large_min = v3s16(8, 8, 8);
+               dp.room_size_large_max = v3s16(16, 16, 16);
+               dp.notifytype          = GENNOTIFY_DUNGEON;
+               break;
+       case MGSTONE_DESERT_STONE:
+               dp.c_wall              = c_desert_stone;
+               dp.c_alt_wall          = CONTENT_IGNORE;
+               dp.c_stair             = c_stair_desert_stone;
+
+               dp.diagonal_dirs       = true;
+               dp.holesize            = v3s16(2, 3, 2);
+               dp.room_size_min       = v3s16(6, 9, 6);
+               dp.room_size_max       = v3s16(10, 11, 10);
+               dp.room_size_large_min = v3s16(10, 13, 10);
+               dp.room_size_large_max = v3s16(18, 21, 18);
+               dp.notifytype          = GENNOTIFY_TEMPLE;
+               break;
+       case MGSTONE_SANDSTONE:
+               dp.c_wall              = c_sandstonebrick;
+               dp.c_alt_wall          = CONTENT_IGNORE;
+               dp.c_stair             = c_stair_sandstone_block;
+
+               dp.diagonal_dirs       = false;
+               dp.holesize            = v3s16(2, 2, 2);
+               dp.room_size_min       = v3s16(6, 4, 6);
+               dp.room_size_max       = v3s16(10, 6, 10);
+               dp.room_size_large_min = v3s16(10, 8, 10);
+               dp.room_size_large_max = v3s16(18, 16, 18);
+               dp.notifytype          = GENNOTIFY_DUNGEON;
+               break;
+       case MGSTONE_OTHER:
+               dp.c_wall              = biome_stone;
+               dp.c_alt_wall          = biome_stone;
+               dp.c_stair             = biome_stone;
+
+               dp.diagonal_dirs       = false;
+               dp.holesize            = v3s16(1, 2, 1);
+               dp.room_size_min       = v3s16(4, 4, 4);
+               dp.room_size_max       = v3s16(8, 6, 8);
+               dp.room_size_large_min = v3s16(8, 8, 8);
+               dp.room_size_large_max = v3s16(16, 16, 16);
+               dp.notifytype          = GENNOTIFY_DUNGEON;
+               break;
+       }
+
+       DungeonGen dgen(ndef, &gennotify, &dp);
+       dgen.generate(vm, blockseed, full_node_min, full_node_max);
+}
+
+
+////
+//// GenerateNotifier
+////
+
+GenerateNotifier::GenerateNotifier(u32 notify_on,
+       std::set<u32> *notify_on_deco_ids)
+{
+       m_notify_on = notify_on;
+       m_notify_on_deco_ids = notify_on_deco_ids;
+}
+
+
+void GenerateNotifier::setNotifyOn(u32 notify_on)
+{
+       m_notify_on = notify_on;
+}
+
+
+void GenerateNotifier::setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids)
+{
+       m_notify_on_deco_ids = notify_on_deco_ids;
+}
+
+
+bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
+{
+       if (!(m_notify_on & (1 << type)))
+               return false;
+
+       if (type == GENNOTIFY_DECORATION &&
+               m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->end())
+               return false;
+
+       GenNotifyEvent gne;
+       gne.type = type;
+       gne.pos  = pos;
+       gne.id   = id;
+       m_notify_events.push_back(gne);
+
+       return true;
+}
+
+
+void GenerateNotifier::getEvents(
+       std::map<std::string, std::vector<v3s16> > &event_map,
+       bool peek_events)
+{
+       std::list<GenNotifyEvent>::iterator it;
+
+       for (it = m_notify_events.begin(); it != m_notify_events.end(); ++it) {
+               GenNotifyEvent &gn = *it;
+               std::string name = (gn.type == GENNOTIFY_DECORATION) ?
+                       "decoration#"+ itos(gn.id) :
+                       flagdesc_gennotify[gn.type].name;
+
+               event_map[name].push_back(gn.pos);
+       }
+
+       if (!peek_events)
+               m_notify_events.clear();
+}
+
+
+////
+//// MapgenParams
+////
+
+
+MapgenParams::~MapgenParams()
+{
+       delete bparams;
+}
+
+
+void MapgenParams::readParams(const Settings *settings)
+{
+       std::string seed_str;
+       const char *seed_name = (settings == g_settings) ? "fixed_map_seed" : "seed";
+
+       if (settings->getNoEx(seed_name, seed_str)) {
+               if (!seed_str.empty())
+                       seed = read_seed(seed_str.c_str());
+               else
+                       myrand_bytes(&seed, sizeof(seed));
+       }
+
+       std::string mg_name;
+       if (settings->getNoEx("mg_name", mg_name)) {
+               mgtype = Mapgen::getMapgenType(mg_name);
+               if (mgtype == MAPGEN_INVALID)
+                       mgtype = MAPGEN_DEFAULT;
+       }
+
+       settings->getS16NoEx("water_level", water_level);
+       settings->getS16NoEx("mapgen_limit", mapgen_limit);
+       settings->getS16NoEx("chunksize", chunksize);
+       settings->getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
+
+       delete bparams;
+       bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL);
+       if (bparams) {
+               bparams->readParams(settings);
+               bparams->seed = seed;
+       }
+}
+
+
+void MapgenParams::writeParams(Settings *settings) const
+{
+       settings->set("mg_name", Mapgen::getMapgenName(mgtype));
+       settings->setU64("seed", seed);
+       settings->setS16("water_level", water_level);
+       settings->setS16("mapgen_limit", mapgen_limit);
+       settings->setS16("chunksize", chunksize);
+       settings->setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX);
+
+       if (bparams)
+               bparams->writeParams(settings);
+}
+
+// Calculate edges of outermost generated mapchunks (less than
+// 'mapgen_limit'), and corresponding exact limits for SAO entities.
+void MapgenParams::calcMapgenEdges()
+{
+       if (m_mapgen_edges_calculated)
+               return;
+
+       // Central chunk offset, in blocks
+       s16 ccoff_b = -chunksize / 2;
+       // Chunksize, in nodes
+       s32 csize_n = chunksize * MAP_BLOCKSIZE;
+       // Minp/maxp of central chunk, in nodes
+       s16 ccmin = ccoff_b * MAP_BLOCKSIZE;
+       s16 ccmax = ccmin + csize_n - 1;
+       // Fullminp/fullmaxp of central chunk, in nodes
+       s16 ccfmin = ccmin - MAP_BLOCKSIZE;
+       s16 ccfmax = ccmax + MAP_BLOCKSIZE;
+       // Effective mapgen limit, in blocks
+       // Uses same calculation as ServerMap::blockpos_over_mapgen_limit(v3s16 p)
+       s16 mapgen_limit_b = rangelim(mapgen_limit,
+               0, MAX_MAP_GENERATION_LIMIT) / MAP_BLOCKSIZE;
+       // Effective mapgen limits, in nodes
+       s16 mapgen_limit_min = -mapgen_limit_b * MAP_BLOCKSIZE;
+       s16 mapgen_limit_max = (mapgen_limit_b + 1) * MAP_BLOCKSIZE - 1;
+       // Number of complete chunks from central chunk fullminp/fullmaxp
+       // to effective mapgen limits.
+       s16 numcmin = MYMAX((ccfmin - mapgen_limit_min) / csize_n, 0);
+       s16 numcmax = MYMAX((mapgen_limit_max - ccfmax) / csize_n, 0);
+       // Mapgen edges, in nodes
+       mapgen_edge_min = ccmin - numcmin * csize_n;
+       mapgen_edge_max = ccmax + numcmax * csize_n;
+       // SAO position limits, in Irrlicht units
+       m_sao_limit_min = mapgen_edge_min * BS - 3.0f;
+       m_sao_limit_max = mapgen_edge_max * BS + 3.0f;
+
+       m_mapgen_edges_calculated = true;
+}
+
+
+bool MapgenParams::saoPosOverLimit(const v3f &p)
+{
+       if (!m_mapgen_edges_calculated)
+               calcMapgenEdges();
+
+       return p.X < m_sao_limit_min ||
+               p.X > m_sao_limit_max ||
+               p.Y < m_sao_limit_min ||
+               p.Y > m_sao_limit_max ||
+               p.Z < m_sao_limit_min ||
+               p.Z > m_sao_limit_max;
+}
+
+
+s32 MapgenParams::getSpawnRangeMax()
+{
+       calcMapgenEdges();
+
+       return MYMIN(-mapgen_edge_min, mapgen_edge_max);
+}
diff --git a/src/mapgen/mapgen.h b/src/mapgen/mapgen.h
new file mode 100644 (file)
index 0000000..8994fdc
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+Minetest
+Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "noise.h"
+#include "nodedef.h"
+#include "util/string.h"
+#include "util/container.h"
+
+#define MAPGEN_DEFAULT MAPGEN_V7
+#define MAPGEN_DEFAULT_NAME "v7"
+
+/////////////////// Mapgen flags
+#define MG_TREES       0x01  // Deprecated. Moved into mgv6 flags
+#define MG_CAVES       0x02
+#define MG_DUNGEONS    0x04
+#define MG_FLAT        0x08  // Deprecated. Moved into mgv6 flags
+#define MG_LIGHT       0x10
+#define MG_DECORATIONS 0x20
+
+typedef u8 biome_t;  // copy from mg_biome.h to avoid an unnecessary include
+
+class Settings;
+class MMVManip;
+class INodeDefManager;
+
+extern FlagDesc flagdesc_mapgen[];
+extern FlagDesc flagdesc_gennotify[];
+
+class Biome;
+class BiomeGen;
+struct BiomeParams;
+class BiomeManager;
+class EmergeManager;
+class MapBlock;
+class VoxelManipulator;
+struct BlockMakeData;
+class VoxelArea;
+class Map;
+
+enum MapgenObject {
+       MGOBJ_VMANIP,
+       MGOBJ_HEIGHTMAP,
+       MGOBJ_BIOMEMAP,
+       MGOBJ_HEATMAP,
+       MGOBJ_HUMIDMAP,
+       MGOBJ_GENNOTIFY
+};
+
+enum GenNotifyType {
+       GENNOTIFY_DUNGEON,
+       GENNOTIFY_TEMPLE,
+       GENNOTIFY_CAVE_BEGIN,
+       GENNOTIFY_CAVE_END,
+       GENNOTIFY_LARGECAVE_BEGIN,
+       GENNOTIFY_LARGECAVE_END,
+       GENNOTIFY_DECORATION,
+       NUM_GENNOTIFY_TYPES
+};
+
+enum MgStoneType {
+       MGSTONE_STONE,
+       MGSTONE_DESERT_STONE,
+       MGSTONE_SANDSTONE,
+       MGSTONE_OTHER,
+};
+
+struct GenNotifyEvent {
+       GenNotifyType type;
+       v3s16 pos;
+       u32 id;
+};
+
+class GenerateNotifier {
+public:
+       GenerateNotifier() = default;
+       GenerateNotifier(u32 notify_on, std::set<u32> *notify_on_deco_ids);
+
+       void setNotifyOn(u32 notify_on);
+       void setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids);
+
+       bool addEvent(GenNotifyType type, v3s16 pos, u32 id=0);
+       void getEvents(std::map<std::string, std::vector<v3s16> > &event_map,
+               bool peek_events=false);
+
+private:
+       u32 m_notify_on = 0;
+       std::set<u32> *m_notify_on_deco_ids;
+       std::list<GenNotifyEvent> m_notify_events;
+};
+
+enum MapgenType {
+       MAPGEN_V5,
+       MAPGEN_V6,
+       MAPGEN_V7,
+       MAPGEN_FLAT,
+       MAPGEN_FRACTAL,
+       MAPGEN_VALLEYS,
+       MAPGEN_SINGLENODE,
+       MAPGEN_CARPATHIAN,
+       MAPGEN_INVALID,
+};
+
+struct MapgenParams {
+       MapgenParams() = default;
+       virtual ~MapgenParams();
+
+       MapgenType mgtype = MAPGEN_DEFAULT;
+       s16 chunksize = 5;
+       u64 seed = 0;
+       s16 water_level = 1;
+       s16 mapgen_limit = MAX_MAP_GENERATION_LIMIT;
+       u32 flags = MG_CAVES | MG_LIGHT | MG_DECORATIONS;
+
+       BiomeParams *bparams = nullptr;
+
+       s16 mapgen_edge_min = -MAX_MAP_GENERATION_LIMIT;
+       s16 mapgen_edge_max = MAX_MAP_GENERATION_LIMIT;
+
+       virtual void readParams(const Settings *settings);
+       virtual void writeParams(Settings *settings) const;
+
+       bool saoPosOverLimit(const v3f &p);
+       s32 getSpawnRangeMax();
+
+private:
+       void calcMapgenEdges();
+
+       float m_sao_limit_min = -MAX_MAP_GENERATION_LIMIT * BS;
+       float m_sao_limit_max = MAX_MAP_GENERATION_LIMIT * BS;
+       bool m_mapgen_edges_calculated = false;
+};
+
+
+/*
+       Generic interface for map generators.  All mapgens must inherit this class.
+       If a feature exposed by a public member pointer is not supported by a
+       certain mapgen, it must be set to NULL.
+
+       Apart from makeChunk, getGroundLevelAtPoint, and getSpawnLevelAtPoint, all
+       methods can be used by constructing a Mapgen base class and setting the
+       appropriate public members (e.g. vm, ndef, and so on).
+*/
+class Mapgen {
+public:
+       s32 seed = 0;
+       int water_level = 0;
+       int mapgen_limit = 0;
+       u32 flags = 0;
+       bool generating = false;
+       int id = -1;
+
+       MMVManip *vm = nullptr;
+       INodeDefManager *ndef = nullptr;
+
+       u32 blockseed;
+       s16 *heightmap = nullptr;
+       biome_t *biomemap = nullptr;
+       v3s16 csize;
+
+       BiomeGen *biomegen = nullptr;
+       GenerateNotifier gennotify;
+
+       Mapgen() = default;
+       Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge);
+       virtual ~Mapgen() = default;
+       DISABLE_CLASS_COPY(Mapgen);
+
+       virtual MapgenType getType() const { return MAPGEN_INVALID; }
+
+       static u32 getBlockSeed(v3s16 p, s32 seed);
+       static u32 getBlockSeed2(v3s16 p, s32 seed);
+       s16 findGroundLevelFull(v2s16 p2d);
+       s16 findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax);
+       s16 findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax);
+       void updateHeightmap(v3s16 nmin, v3s16 nmax);
+       void getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
+               s16 *floors, s16 *ceilings, u16 *num_floors, u16 *num_ceilings);
+
+       void updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax);
+
+       void setLighting(u8 light, v3s16 nmin, v3s16 nmax);
+       void lightSpread(VoxelArea &a, v3s16 p, u8 light);
+       void calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
+               bool propagate_shadow = true);
+       void propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow);
+       void spreadLight(v3s16 nmin, v3s16 nmax);
+
+       virtual void makeChunk(BlockMakeData *data) {}
+       virtual int getGroundLevelAtPoint(v2s16 p) { return 0; }
+
+       // getSpawnLevelAtPoint() is a function within each mapgen that returns a
+       // suitable y co-ordinate for player spawn ('suitable' usually meaning
+       // within 16 nodes of water_level). If a suitable spawn level cannot be
+       // found at the specified (X, Z) 'MAX_MAP_GENERATION_LIMIT' is returned to
+       // signify this and to cause Server::findSpawnPos() to try another (X, Z).
+       virtual int getSpawnLevelAtPoint(v2s16 p) { return 0; }
+
+       // Mapgen management functions
+       static MapgenType getMapgenType(const std::string &mgname);
+       static const char *getMapgenName(MapgenType mgtype);
+       static Mapgen *createMapgen(MapgenType mgtype, int mgid,
+               MapgenParams *params, EmergeManager *emerge);
+       static MapgenParams *createMapgenParams(MapgenType mgtype);
+       static void getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden);
+
+private:
+       // isLiquidHorizontallyFlowable() is a helper function for updateLiquid()
+       // that checks whether there are floodable nodes without liquid beneath
+       // the node at index vi.
+       inline bool isLiquidHorizontallyFlowable(u32 vi, v3s16 em);
+};
+
+/*
+       MapgenBasic is a Mapgen implementation that handles basic functionality
+       the majority of conventional mapgens will probably want to use, but isn't
+       generic enough to be included as part of the base Mapgen class (such as
+       generating biome terrain over terrain node skeletons, generating caves,
+       dungeons, etc.)
+
+       Inherit MapgenBasic instead of Mapgen to add this basic functionality to
+       your mapgen without having to reimplement it.  Feel free to override any of
+       these methods if you desire different or more advanced behavior.
+
+       Note that you must still create your own generateTerrain implementation when
+       inheriting MapgenBasic.
+*/
+class MapgenBasic : public Mapgen {
+public:
+       MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge);
+       virtual ~MapgenBasic();
+
+       virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth);
+       virtual bool generateCaverns(s16 max_stone_y);
+       virtual void generateDungeons(s16 max_stone_y,
+               MgStoneType stone_type, content_t biome_stone);
+       virtual void generateBiomes(MgStoneType *mgstone_type,
+               content_t *biome_stone);
+       virtual void dustTopNodes();
+
+protected:
+       EmergeManager *m_emerge;
+       BiomeManager *m_bmgr;
+
+       Noise *noise_filler_depth;
+
+       v3s16 node_min;
+       v3s16 node_max;
+       v3s16 full_node_min;
+       v3s16 full_node_max;
+
+       // Content required for generateBiomes
+       content_t c_stone;
+       content_t c_desert_stone;
+       content_t c_sandstone;
+       content_t c_water_source;
+       content_t c_river_water_source;
+       content_t c_lava_source;
+
+       // Content required for generateDungeons
+       content_t c_cobble;
+       content_t c_stair_cobble;
+       content_t c_mossycobble;
+       content_t c_stair_desert_stone;
+       content_t c_sandstonebrick;
+       content_t c_stair_sandstone_block;
+
+       int ystride;
+       int zstride;
+       int zstride_1d;
+       int zstride_1u1d;
+
+       u32 spflags;
+
+       NoiseParams np_cave1;
+       NoiseParams np_cave2;
+       NoiseParams np_cavern;
+       float cave_width;
+       float cavern_limit;
+       float cavern_taper;
+       float cavern_threshold;
+       int lava_depth;
+};
diff --git a/src/mapgen/mapgen_carpathian.cpp b/src/mapgen/mapgen_carpathian.cpp
new file mode 100644 (file)
index 0000000..9884ce4
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+Minetest
+Copyright (C) 2010-2016 paramat, Matt Gregory
+Copyright (C) 2010-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2017 vlapsley, Vaughan Lapsley <vlapsley@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 <cmath>
+#include "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "content_sao.h"
+#include "nodedef.h"
+#include "voxelalgorithms.h"
+//#include "profiler.h" // For TimeTaker
+#include "settings.h" // For g_settings
+#include "emerge.h"
+#include "dungeongen.h"
+#include "cavegen.h"
+#include "mg_biome.h"
+#include "mg_ore.h"
+#include "mg_decoration.h"
+#include "mapgen_carpathian.h"
+
+
+FlagDesc flagdesc_mapgen_carpathian[] = {
+       {"caverns", MGCARPATHIAN_CAVERNS},
+       {NULL,      0}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+MapgenCarpathian::MapgenCarpathian(
+               int mapgenid, MapgenCarpathianParams *params, EmergeManager *emerge)
+       : MapgenBasic(mapgenid, params, emerge)
+{
+       spflags          = params->spflags;
+       cave_width       = params->cave_width;
+       large_cave_depth = params->large_cave_depth;
+       lava_depth       = params->lava_depth;
+       cavern_limit     = params->cavern_limit;
+       cavern_taper     = params->cavern_taper;
+       cavern_threshold = params->cavern_threshold;
+       grad_wl          = 1 - water_level;
+
+       //// 2D Terrain noise
+       noise_base          = new Noise(&params->np_base,          seed, csize.X, csize.Z);
+       noise_filler_depth  = new Noise(&params->np_filler_depth,  seed, csize.X, csize.Z);
+       noise_height1       = new Noise(&params->np_height1,       seed, csize.X, csize.Z);
+       noise_height2       = new Noise(&params->np_height2,       seed, csize.X, csize.Z);
+       noise_height3       = new Noise(&params->np_height3,       seed, csize.X, csize.Z);
+       noise_height4       = new Noise(&params->np_height4,       seed, csize.X, csize.Z);
+       noise_hills_terrain = new Noise(&params->np_hills_terrain, seed, csize.X, csize.Z);
+       noise_ridge_terrain = new Noise(&params->np_ridge_terrain, seed, csize.X, csize.Z);
+       noise_step_terrain  = new Noise(&params->np_step_terrain,  seed, csize.X, csize.Z);
+       noise_hills         = new Noise(&params->np_hills,         seed, csize.X, csize.Z);
+       noise_ridge_mnt     = new Noise(&params->np_ridge_mnt,     seed, csize.X, csize.Z);
+       noise_step_mnt      = new Noise(&params->np_step_mnt,      seed, csize.X, csize.Z);
+
+       //// 3D terrain noise
+       // 1 up 1 down overgeneration
+       noise_mnt_var = new Noise(&params->np_mnt_var, seed, csize.X, csize.Y + 2, csize.Z);
+
+       //// Cave noise
+       MapgenBasic::np_cave1  = params->np_cave1;
+       MapgenBasic::np_cave2  = params->np_cave2;
+       MapgenBasic::np_cavern = params->np_cavern;
+}
+
+
+MapgenCarpathian::~MapgenCarpathian()
+{
+       delete noise_base;
+       delete noise_filler_depth;
+       delete noise_height1;
+       delete noise_height2;
+       delete noise_height3;
+       delete noise_height4;
+       delete noise_hills_terrain;
+       delete noise_ridge_terrain;
+       delete noise_step_terrain;
+       delete noise_hills;
+       delete noise_ridge_mnt;
+       delete noise_step_mnt;
+       delete noise_mnt_var;
+}
+
+
+MapgenCarpathianParams::MapgenCarpathianParams():
+       np_base          (12, 1,  v3f(2557, 2557, 2557), 6538,  4, 0.8,  0.5),
+       np_filler_depth  (0,  1,  v3f(128,  128,  128),  261,   3, 0.7,  2.0),
+       np_height1       (0,  5,  v3f(251,  251,  251),  9613,  5, 0.5,  2.0),
+       np_height2       (0,  5,  v3f(383,  383,  383),  1949,  5, 0.5,  2.0),
+       np_height3       (0,  5,  v3f(509,  509,  509),  3211,  5, 0.5,  2.0),
+       np_height4       (0,  5,  v3f(631,  631,  631),  1583,  5, 0.5,  2.0),
+       np_hills_terrain (1,  1,  v3f(1301, 1301, 1301), 1692,  5, 0.5,  2.0),
+       np_ridge_terrain (1,  1,  v3f(1889, 1889, 1889), 3568,  5, 0.5,  2.0),
+       np_step_terrain  (1,  1,  v3f(1889, 1889, 1889), 4157,  5, 0.5,  2.0),
+       np_hills         (0,  3,  v3f(257,  257,  257),  6604,  6, 0.5,  2.0),
+       np_ridge_mnt     (0,  12, v3f(743,  743,  743),  5520,  6, 0.7,  2.0),
+       np_step_mnt      (0,  8,  v3f(509,  509,  509),  2590,  6, 0.6,  2.0),
+       np_mnt_var       (0,  1,  v3f(499,  499,  499),  2490,  5, 0.55, 2.0),
+       np_cave1         (0,  12, v3f(61,   61,   61),   52534, 3, 0.5,  2.0),
+       np_cave2         (0,  12, v3f(67,   67,   67),   10325, 3, 0.5,  2.0),
+       np_cavern        (0,  1,  v3f(384,  128,  384),  723,   5, 0.63, 2.0)
+{
+}
+
+
+void MapgenCarpathianParams::readParams(const Settings *settings)
+{
+       settings->getFlagStrNoEx("mgcarpathian_spflags", spflags, flagdesc_mapgen_carpathian);
+       settings->getFloatNoEx("mgcarpathian_cave_width",       cave_width);
+       settings->getS16NoEx("mgcarpathian_large_cave_depth",   large_cave_depth);
+       settings->getS16NoEx("mgcarpathian_lava_depth",         lava_depth);
+       settings->getS16NoEx("mgcarpathian_cavern_limit",       cavern_limit);
+       settings->getS16NoEx("mgcarpathian_cavern_taper",       cavern_taper);
+       settings->getFloatNoEx("mgcarpathian_cavern_threshold", cavern_threshold);
+
+       settings->getNoiseParams("mgcarpathian_np_base",          np_base);
+       settings->getNoiseParams("mgcarpathian_np_filler_depth",  np_filler_depth);
+       settings->getNoiseParams("mgcarpathian_np_height1",       np_height1);
+       settings->getNoiseParams("mgcarpathian_np_height2",       np_height2);
+       settings->getNoiseParams("mgcarpathian_np_height3",       np_height3);
+       settings->getNoiseParams("mgcarpathian_np_height4",       np_height4);
+       settings->getNoiseParams("mgcarpathian_np_hills_terrain", np_hills_terrain);
+       settings->getNoiseParams("mgcarpathian_np_ridge_terrain", np_ridge_terrain);
+       settings->getNoiseParams("mgcarpathian_np_step_terrain",  np_step_terrain);
+       settings->getNoiseParams("mgcarpathian_np_hills",         np_hills);
+       settings->getNoiseParams("mgcarpathian_np_ridge_mnt",     np_ridge_mnt);
+       settings->getNoiseParams("mgcarpathian_np_step_mnt",      np_step_mnt);
+       settings->getNoiseParams("mgcarpathian_np_mnt_var",       np_mnt_var);
+       settings->getNoiseParams("mgcarpathian_np_cave1",         np_cave1);
+       settings->getNoiseParams("mgcarpathian_np_cave2",         np_cave2);
+       settings->getNoiseParams("mgcarpathian_np_cavern",        np_cavern);
+}
+
+
+void MapgenCarpathianParams::writeParams(Settings *settings) const
+{
+       settings->setFlagStr("mgcarpathian_spflags", spflags, flagdesc_mapgen_carpathian, U32_MAX);
+       settings->setFloat("mgcarpathian_cave_width",       cave_width);
+       settings->setS16("mgcarpathian_large_cave_depth",   large_cave_depth);
+       settings->setS16("mgcarpathian_lava_depth",         lava_depth);
+       settings->setS16("mgcarpathian_cavern_limit",       cavern_limit);
+       settings->setS16("mgcarpathian_cavern_taper",       cavern_taper);
+       settings->setFloat("mgcarpathian_cavern_threshold", cavern_threshold);
+
+       settings->setNoiseParams("mgcarpathian_np_base",          np_base);
+       settings->setNoiseParams("mgcarpathian_np_filler_depth",  np_filler_depth);
+       settings->setNoiseParams("mgcarpathian_np_height1",       np_height1);
+       settings->setNoiseParams("mgcarpathian_np_height2",       np_height2);
+       settings->setNoiseParams("mgcarpathian_np_height3",       np_height3);
+       settings->setNoiseParams("mgcarpathian_np_height4",       np_height4);
+       settings->setNoiseParams("mgcarpathian_np_hills_terrain", np_hills_terrain);
+       settings->setNoiseParams("mgcarpathian_np_ridge_terrain", np_ridge_terrain);
+       settings->setNoiseParams("mgcarpathian_np_step_terrain",  np_step_terrain);
+       settings->setNoiseParams("mgcarpathian_np_hills",         np_hills);
+       settings->setNoiseParams("mgcarpathian_np_ridge_mnt",     np_ridge_mnt);
+       settings->setNoiseParams("mgcarpathian_np_step_mnt",      np_step_mnt);
+       settings->setNoiseParams("mgcarpathian_np_mnt_var",       np_mnt_var);
+       settings->setNoiseParams("mgcarpathian_np_cave1",         np_cave1);
+       settings->setNoiseParams("mgcarpathian_np_cave2",         np_cave2);
+       settings->setNoiseParams("mgcarpathian_np_cavern",        np_cavern);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+// Lerp function
+inline float MapgenCarpathian::getLerp(float noise1, float noise2, float mod)
+{
+       return noise1 + mod * (noise2 - noise1);
+}
+
+// Steps function
+float MapgenCarpathian::getSteps(float noise)
+{
+       float w = 0.5f;
+       float k = floor(noise / w);
+       float f = (noise - k * w) / w;
+       float s = std::fmin(2.f * f, 1.f);
+       return (k + s) * w;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+void MapgenCarpathian::makeChunk(BlockMakeData *data)
+{
+       // Pre-conditions
+       assert(data->vmanip);
+       assert(data->nodedef);
+       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
+                       data->blockpos_requested.Y >= data->blockpos_min.Y &&
+                       data->blockpos_requested.Z >= data->blockpos_min.Z);
+       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
+                       data->blockpos_requested.Y <= data->blockpos_max.Y &&
+                       data->blockpos_requested.Z <= data->blockpos_max.Z);
+
+       this->generating = true;
+       this->vm = data->vmanip;
+       this->ndef = data->nodedef;
+
+       v3s16 blockpos_min = data->blockpos_min;
+       v3s16 blockpos_max = data->blockpos_max;
+       node_min = blockpos_min * MAP_BLOCKSIZE;
+       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
+       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+
+       // Create a block-specific seed
+       blockseed = getBlockSeed2(full_node_min, seed);
+
+       // Generate terrain
+       s16 stone_surface_max_y = generateTerrain();
+
+       // Create heightmap
+       updateHeightmap(node_min, node_max);
+
+       // Init biome generator, place biome-specific nodes, and build biomemap
+       biomegen->calcBiomeNoise(node_min);
+
+       MgStoneType mgstone_type;
+       content_t biome_stone;
+       generateBiomes(&mgstone_type, &biome_stone);
+
+       // Generate caverns, tunnels and classic caves
+       if (flags & MG_CAVES) {
+               bool has_cavern = false;
+               // Generate caverns
+               if (spflags & MGCARPATHIAN_CAVERNS)
+                       has_cavern = generateCaverns(stone_surface_max_y);
+               // Generate tunnels and classic caves
+               if (has_cavern)
+                       // Disable classic caves in this mapchunk by setting
+                       // 'large cave depth' to world base. Avoids excessive liquid in
+                       // large caverns and floating blobs of overgenerated liquid.
+                       generateCaves(stone_surface_max_y, -MAX_MAP_GENERATION_LIMIT);
+               else
+                       generateCaves(stone_surface_max_y, large_cave_depth);
+       }
+
+       // Generate dungeons
+       if (flags & MG_DUNGEONS)
+               generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
+
+       // Generate the registered decorations
+       if (flags & MG_DECORATIONS)
+               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+
+       // Generate the registered ores
+       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+
+       // Sprinkle some dust on top after everything else was generated
+       dustTopNodes();
+
+       // Update liquids
+       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
+
+       // Calculate lighting
+       if (flags & MG_LIGHT) {
+               calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
+                               full_node_min, full_node_max);
+       }
+
+       this->generating = false;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+int MapgenCarpathian::getSpawnLevelAtPoint(v2s16 p)
+{
+       s16 level_at_point = terrainLevelAtPoint(p.X, p.Y);
+       if (level_at_point <= water_level || level_at_point > water_level + 32)
+               return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
+
+       return level_at_point;
+}
+
+
+float MapgenCarpathian::terrainLevelAtPoint(s16 x, s16 z)
+{
+       float ground = NoisePerlin2D(&noise_base->np, x, z, seed);
+       float height1 = NoisePerlin2D(&noise_height1->np, x, z, seed);
+       float height2 = NoisePerlin2D(&noise_height2->np, x, z, seed);
+       float height3 = NoisePerlin2D(&noise_height3->np, x, z, seed);
+       float height4 = NoisePerlin2D(&noise_height4->np, x, z, seed);
+       float hter = NoisePerlin2D(&noise_hills_terrain->np, x, z, seed);
+       float rter = NoisePerlin2D(&noise_ridge_terrain->np, x, z, seed);
+       float ster = NoisePerlin2D(&noise_step_terrain->np, x, z, seed);
+       float n_hills = NoisePerlin2D(&noise_hills->np, x, z, seed);
+       float n_ridge_mnt = NoisePerlin2D(&noise_ridge_mnt->np, x, z, seed);
+       float n_step_mnt = NoisePerlin2D(&noise_step_mnt->np, x, z, seed);
+
+       int height = -MAX_MAP_GENERATION_LIMIT;
+
+       for (s16 y = 1; y <= 30; y++) {
+               float mnt_var = NoisePerlin3D(&noise_mnt_var->np, x, y, z, seed);
+
+               // Gradient & shallow seabed
+               s32 grad = (y < water_level) ? grad_wl + (water_level - y) * 3 : 1 - y;
+
+               // Hill/Mountain height (hilliness)
+               float hill1 = getLerp(height1, height2, mnt_var);
+               float hill2 = getLerp(height3, height4, mnt_var);
+               float hill3 = getLerp(height3, height2, mnt_var);
+               float hill4 = getLerp(height1, height4, mnt_var);
+               float hilliness = std::fmax(std::fmin(hill1, hill2), std::fmin(hill3, hill4));
+
+               // Rolling hills
+               float hill_mnt = hilliness * pow(n_hills, 2.f);
+               float hills = pow(hter, 3.f) * hill_mnt;
+
+               // Ridged mountains
+               float ridge_mnt = hilliness * (1.f - fabs(n_ridge_mnt));
+               float ridged_mountains = pow(rter, 3.f) * ridge_mnt;
+
+               // Step (terraced) mountains
+               float step_mnt = hilliness * getSteps(n_step_mnt);
+               float step_mountains = pow(ster, 3.f) * step_mnt;
+
+               // Final terrain level
+               float mountains = hills + ridged_mountains + step_mountains;
+               float surface_level = ground + mountains + grad;
+
+               if (y > surface_level && height < 0)
+                       height = y;
+       }
+
+       return height;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+int MapgenCarpathian::generateTerrain()
+{
+       MapNode mn_air(CONTENT_AIR);
+       MapNode mn_stone(c_stone);
+       MapNode mn_water(c_water_source);
+
+       s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+       u32 index2d = 0;
+       u32 index3d = 0;
+
+       // Calculate noise for terrain generation
+       noise_base->perlinMap2D(node_min.X, node_min.Z);
+       noise_height1->perlinMap2D(node_min.X, node_min.Z);
+       noise_height2->perlinMap2D(node_min.X, node_min.Z);
+       noise_height3->perlinMap2D(node_min.X, node_min.Z);
+       noise_height4->perlinMap2D(node_min.X, node_min.Z);
+       noise_hills_terrain->perlinMap2D(node_min.X, node_min.Z);
+       noise_ridge_terrain->perlinMap2D(node_min.X, node_min.Z);
+       noise_step_terrain->perlinMap2D(node_min.X, node_min.Z);
+       noise_hills->perlinMap2D(node_min.X, node_min.Z);
+       noise_ridge_mnt->perlinMap2D(node_min.X, node_min.Z);
+       noise_step_mnt->perlinMap2D(node_min.X, node_min.Z);
+       noise_mnt_var->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+
+       //// Place nodes
+       for (s16 z = node_min.Z; z <= node_max.Z; z++) {
+               for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
+                       u32 vi = vm->m_area.index(node_min.X, y, z);
+                       for (s16 x = node_min.X; x <= node_max.X;
+                                       x++, vi++, index2d++, index3d++) {
+                               if (vm->m_data[vi].getContent() != CONTENT_IGNORE)
+                                       continue;
+
+                               // Base terrain
+                               float ground = noise_base->result[index2d];
+
+                               // Gradient & shallow seabed
+                               s32 grad = (y < water_level) ? grad_wl + (water_level - y) * 3 : 1 - y;
+
+                               // Hill/Mountain height (hilliness)
+                               float height1 = noise_height1->result[index2d];
+                               float height2 = noise_height2->result[index2d];
+                               float height3 = noise_height3->result[index2d];
+                               float height4 = noise_height4->result[index2d];
+                               float mnt_var = noise_mnt_var->result[index3d];
+                               // Combine height noises and apply 3D variation
+                               float hill1 = getLerp(height1, height2, mnt_var);
+                               float hill2 = getLerp(height3, height4, mnt_var);
+                               float hill3 = getLerp(height3, height2, mnt_var);
+                               float hill4 = getLerp(height1, height4, mnt_var);
+                               // 'hilliness' determines whether hills/mountains are
+                               // small or large
+                               float hilliness = std::fmax(std::fmin(hill1, hill2), std::fmin(hill3, hill4));
+
+                               // Rolling hills
+                               float hter = noise_hills_terrain->result[index2d];
+                               float n_hills = noise_hills->result[index2d];
+                               float hill_mnt = hilliness * pow(n_hills, 2.f);
+                               float hills = pow(fabs(hter), 3.f) * hill_mnt;
+
+                               // Ridged mountains
+                               float rter = noise_ridge_terrain->result[index2d];
+                               float n_ridge_mnt = noise_ridge_mnt->result[index2d];
+                               float ridge_mnt = hilliness * (1.f - fabs(n_ridge_mnt));
+                               float ridged_mountains = pow(fabs(rter), 3.f) * ridge_mnt;
+
+                               // Step (terraced) mountains
+                               float ster = noise_step_terrain->result[index2d];
+                               float n_step_mnt = noise_step_mnt->result[index2d];
+                               float step_mnt = hilliness * getSteps(n_step_mnt);
+                               float step_mountains = pow(fabs(ster), 3.f) * step_mnt;
+
+                               // Final terrain level
+                               float mountains = hills + ridged_mountains + step_mountains;
+                               float surface_level = ground + mountains + grad;
+
+                               if (y < surface_level) {
+                                       vm->m_data[vi] = mn_stone; // Stone
+                                       if (y > stone_surface_max_y)
+                                               stone_surface_max_y = y;
+                               } else if (y <= water_level) {
+                                       vm->m_data[vi] = mn_water; // Sea water
+                               } else {
+                                       vm->m_data[vi] = mn_air; // Air
+                               }
+                       }
+                       index2d -= ystride;
+               }
+               index2d += ystride;
+       }
+
+       return stone_surface_max_y;
+}
diff --git a/src/mapgen/mapgen_carpathian.h b/src/mapgen/mapgen_carpathian.h
new file mode 100644 (file)
index 0000000..7dbccde
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+Minetest
+Copyright (C) 2010-2016 paramat, Matt Gregory
+Copyright (C) 2010-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2017 vlapsley, Vaughan Lapsley <vlapsley@gmail.com>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "mapgen.h"
+
+///////// Mapgen Carpathian flags
+#define MGCARPATHIAN_CAVERNS 0x01
+
+class BiomeManager;
+
+extern FlagDesc flagdesc_mapgen_carpathian[];
+
+
+struct MapgenCarpathianParams : public MapgenParams
+{
+       u32 spflags            = MGCARPATHIAN_CAVERNS;
+       float cave_width       = 0.09f;
+       s16 large_cave_depth   = -33;
+       s16 lava_depth         = -256;
+       s16 cavern_limit       = -256;
+       s16 cavern_taper       = 256;
+       float cavern_threshold = 0.7f;
+
+       NoiseParams np_base;
+       NoiseParams np_filler_depth;
+       NoiseParams np_height1;
+       NoiseParams np_height2;
+       NoiseParams np_height3;
+       NoiseParams np_height4;
+       NoiseParams np_hills_terrain;
+       NoiseParams np_ridge_terrain;
+       NoiseParams np_step_terrain;
+       NoiseParams np_hills;
+       NoiseParams np_ridge_mnt;
+       NoiseParams np_step_mnt;
+       NoiseParams np_mnt_var;
+       NoiseParams np_cave1;
+       NoiseParams np_cave2;
+       NoiseParams np_cavern;
+
+       MapgenCarpathianParams();
+       ~MapgenCarpathianParams() = default;
+
+       void readParams(const Settings *settings);
+       void writeParams(Settings *settings) const;
+};
+
+class MapgenCarpathian : public MapgenBasic
+{
+public:
+       MapgenCarpathian(int mapgenid, MapgenCarpathianParams *params,
+                       EmergeManager *emerge);
+       ~MapgenCarpathian();
+
+       virtual MapgenType getType() const { return MAPGEN_CARPATHIAN; }
+
+       float getSteps(float noise);
+       inline float getLerp(float noise1, float noise2, float mod);
+
+       virtual void makeChunk(BlockMakeData *data);
+       int getSpawnLevelAtPoint(v2s16 p);
+
+private:
+       s16 large_cave_depth;
+       s32 grad_wl;
+
+       Noise *noise_base;
+       Noise *noise_height1;
+       Noise *noise_height2;
+       Noise *noise_height3;
+       Noise *noise_height4;
+       Noise *noise_hills_terrain;
+       Noise *noise_ridge_terrain;
+       Noise *noise_step_terrain;
+       Noise *noise_hills;
+       Noise *noise_ridge_mnt;
+       Noise *noise_step_mnt;
+       Noise *noise_mnt_var;
+
+       float terrainLevelAtPoint(s16 x, s16 z);
+       int generateTerrain();
+};
diff --git a/src/mapgen/mapgen_flat.cpp b/src/mapgen/mapgen_flat.cpp
new file mode 100644 (file)
index 0000000..57c20d4
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+Minetest
+Copyright (C) 2015-2017 paramat
+Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "content_sao.h"
+#include "nodedef.h"
+#include "voxelalgorithms.h"
+//#include "profiler.h" // For TimeTaker
+#include "settings.h" // For g_settings
+#include "emerge.h"
+#include "dungeongen.h"
+#include "cavegen.h"
+#include "mg_biome.h"
+#include "mg_ore.h"
+#include "mg_decoration.h"
+#include "mapgen_flat.h"
+
+
+FlagDesc flagdesc_mapgen_flat[] = {
+       {"lakes", MGFLAT_LAKES},
+       {"hills", MGFLAT_HILLS},
+       {NULL,    0}
+};
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+
+MapgenFlat::MapgenFlat(int mapgenid, MapgenFlatParams *params, EmergeManager *emerge)
+       : MapgenBasic(mapgenid, params, emerge)
+{
+       spflags          = params->spflags;
+       ground_level     = params->ground_level;
+       large_cave_depth = params->large_cave_depth;
+       lava_depth       = params->lava_depth;
+       cave_width       = params->cave_width;
+       lake_threshold   = params->lake_threshold;
+       lake_steepness   = params->lake_steepness;
+       hill_threshold   = params->hill_threshold;
+       hill_steepness   = params->hill_steepness;
+
+       // 2D noise
+       noise_filler_depth = new Noise(&params->np_filler_depth, seed, csize.X, csize.Z);
+
+       if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
+               noise_terrain = new Noise(&params->np_terrain, seed, csize.X, csize.Z);
+       // 3D noise
+       MapgenBasic::np_cave1 = params->np_cave1;
+       MapgenBasic::np_cave2 = params->np_cave2;
+}
+
+
+MapgenFlat::~MapgenFlat()
+{
+       delete noise_filler_depth;
+
+       if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
+               delete noise_terrain;
+}
+
+
+MapgenFlatParams::MapgenFlatParams():
+       np_terrain      (0, 1,   v3f(600, 600, 600), 7244,  5, 0.6, 2.0),
+       np_filler_depth (0, 1.2, v3f(150, 150, 150), 261,   3, 0.7, 2.0),
+       np_cave1        (0, 12,  v3f(61,  61,  61),  52534, 3, 0.5, 2.0),
+       np_cave2        (0, 12,  v3f(67,  67,  67),  10325, 3, 0.5, 2.0)
+{
+}
+
+
+void MapgenFlatParams::readParams(const Settings *settings)
+{
+       settings->getFlagStrNoEx("mgflat_spflags",      spflags, flagdesc_mapgen_flat);
+       settings->getS16NoEx("mgflat_ground_level",     ground_level);
+       settings->getS16NoEx("mgflat_large_cave_depth", large_cave_depth);
+       settings->getS16NoEx("mgflat_lava_depth",       lava_depth);
+       settings->getFloatNoEx("mgflat_cave_width",     cave_width);
+       settings->getFloatNoEx("mgflat_lake_threshold", lake_threshold);
+       settings->getFloatNoEx("mgflat_lake_steepness", lake_steepness);
+       settings->getFloatNoEx("mgflat_hill_threshold", hill_threshold);
+       settings->getFloatNoEx("mgflat_hill_steepness", hill_steepness);
+
+       settings->getNoiseParams("mgflat_np_terrain",      np_terrain);
+       settings->getNoiseParams("mgflat_np_filler_depth", np_filler_depth);
+       settings->getNoiseParams("mgflat_np_cave1",        np_cave1);
+       settings->getNoiseParams("mgflat_np_cave2",        np_cave2);
+}
+
+
+void MapgenFlatParams::writeParams(Settings *settings) const
+{
+       settings->setFlagStr("mgflat_spflags",      spflags, flagdesc_mapgen_flat, U32_MAX);
+       settings->setS16("mgflat_ground_level",     ground_level);
+       settings->setS16("mgflat_large_cave_depth", large_cave_depth);
+       settings->setS16("mgflat_lava_depth",       lava_depth);
+       settings->setFloat("mgflat_cave_width",     cave_width);
+       settings->setFloat("mgflat_lake_threshold", lake_threshold);
+       settings->setFloat("mgflat_lake_steepness", lake_steepness);
+       settings->setFloat("mgflat_hill_threshold", hill_threshold);
+       settings->setFloat("mgflat_hill_steepness", hill_steepness);
+
+       settings->setNoiseParams("mgflat_np_terrain",      np_terrain);
+       settings->setNoiseParams("mgflat_np_filler_depth", np_filler_depth);
+       settings->setNoiseParams("mgflat_np_cave1",        np_cave1);
+       settings->setNoiseParams("mgflat_np_cave2",        np_cave2);
+}
+
+
+/////////////////////////////////////////////////////////////////
+
+
+int MapgenFlat::getSpawnLevelAtPoint(v2s16 p)
+{
+       s16 level_at_point = ground_level;
+       float n_terrain = 0.0f;
+       if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
+               n_terrain = NoisePerlin2D(&noise_terrain->np, p.X, p.Y, seed);
+
+       if ((spflags & MGFLAT_LAKES) && n_terrain < lake_threshold) {
+               level_at_point = ground_level -
+                       (lake_threshold - n_terrain) * lake_steepness;
+       } else if ((spflags & MGFLAT_HILLS) && n_terrain > hill_threshold) {
+               level_at_point = ground_level +
+                       (n_terrain - hill_threshold) * hill_steepness;
+       }
+
+       if (ground_level < water_level)  // Ocean world, allow spawn in water
+               return MYMAX(level_at_point, water_level);
+
+       if (level_at_point > water_level)
+               return level_at_point;  // Spawn on land
+
+       return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
+}
+
+
+void MapgenFlat::makeChunk(BlockMakeData *data)
+{
+       // Pre-conditions
+       assert(data->vmanip);
+       assert(data->nodedef);
+       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
+               data->blockpos_requested.Y >= data->blockpos_min.Y &&
+               data->blockpos_requested.Z >= data->blockpos_min.Z);
+       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
+               data->blockpos_requested.Y <= data->blockpos_max.Y &&
+               data->blockpos_requested.Z <= data->blockpos_max.Z);
+
+       this->generating = true;
+       this->vm   = data->vmanip;
+       this->ndef = data->nodedef;
+       //TimeTaker t("makeChunk");
+
+       v3s16 blockpos_min = data->blockpos_min;
+       v3s16 blockpos_max = data->blockpos_max;
+       node_min = blockpos_min * MAP_BLOCKSIZE;
+       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
+       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+
+       blockseed = getBlockSeed2(full_node_min, seed);
+
+       // Generate base terrain, mountains, and ridges with initial heightmaps
+       s16 stone_surface_max_y = generateTerrain();
+
+       // Create heightmap
+       updateHeightmap(node_min, node_max);
+
+       // Init biome generator, place biome-specific nodes, and build biomemap
+       biomegen->calcBiomeNoise(node_min);
+
+       MgStoneType mgstone_type;
+       content_t biome_stone;
+       generateBiomes(&mgstone_type, &biome_stone);
+
+       if (flags & MG_CAVES)
+               generateCaves(stone_surface_max_y, large_cave_depth);
+
+       if (flags & MG_DUNGEONS)
+               generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
+
+       // Generate the registered decorations
+       if (flags & MG_DECORATIONS)
+               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+
+       // Generate the registered ores
+       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+
+       // Sprinkle some dust on top after everything else was generated
+       dustTopNodes();
+
+       //printf("makeChunk: %dms\n", t.stop());
+
+       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
+
+       if (flags & MG_LIGHT)
+               calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
+                       full_node_min, full_node_max);
+
+       //setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE,
+       //                      node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF);
+
+       this->generating = false;
+}
+
+
+s16 MapgenFlat::generateTerrain()
+{
+       MapNode n_air(CONTENT_AIR);
+       MapNode n_stone(c_stone);
+       MapNode n_water(c_water_source);
+
+       const v3s16 &em = vm->m_area.getExtent();
+       s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+       u32 ni2d = 0;
+
+       bool use_noise = (spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS);
+       if (use_noise)
+               noise_terrain->perlinMap2D(node_min.X, node_min.Z);
+
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 x = node_min.X; x <= node_max.X; x++, ni2d++) {
+               s16 stone_level = ground_level;
+               float n_terrain = use_noise ? noise_terrain->result[ni2d] : 0.0f;
+
+               if ((spflags & MGFLAT_LAKES) && n_terrain < lake_threshold) {
+                       s16 depress = (lake_threshold - n_terrain) * lake_steepness;
+                       stone_level = ground_level - depress;
+               } else if ((spflags & MGFLAT_HILLS) && n_terrain > hill_threshold) {
+                       s16 rise = (n_terrain - hill_threshold) * hill_steepness;
+                       stone_level = ground_level + rise;
+               }
+
+               u32 vi = vm->m_area.index(x, node_min.Y - 1, z);
+               for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
+                       if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
+                               if (y <= stone_level) {
+                                       vm->m_data[vi] = n_stone;
+                                       if (y > stone_surface_max_y)
+                                               stone_surface_max_y = y;
+                               } else if (y <= water_level) {
+                                       vm->m_data[vi] = n_water;
+                               } else {
+                                       vm->m_data[vi] = n_air;
+                               }
+                       }
+                       vm->m_area.add_y(em, vi, 1);
+               }
+       }
+
+       return stone_surface_max_y;
+}
diff --git a/src/mapgen/mapgen_flat.h b/src/mapgen/mapgen_flat.h
new file mode 100644 (file)
index 0000000..635d406
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+Minetest
+Copyright (C) 2015-2017 paramat
+Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "mapgen.h"
+
+/////// Mapgen Flat flags
+#define MGFLAT_LAKES 0x01
+#define MGFLAT_HILLS 0x02
+
+class BiomeManager;
+
+extern FlagDesc flagdesc_mapgen_flat[];
+
+struct MapgenFlatParams : public MapgenParams
+{
+       u32 spflags = 0;
+       s16 ground_level = 8;
+       s16 large_cave_depth = -33;
+       s16 lava_depth = -256;
+       float cave_width = 0.09f;
+       float lake_threshold = -0.45f;
+       float lake_steepness = 48.0f;
+       float hill_threshold = 0.45f;
+       float hill_steepness = 64.0f;
+       NoiseParams np_terrain;
+       NoiseParams np_filler_depth;
+       NoiseParams np_cave1;
+       NoiseParams np_cave2;
+
+       MapgenFlatParams();
+       ~MapgenFlatParams() = default;
+
+       void readParams(const Settings *settings);
+       void writeParams(Settings *settings) const;
+};
+
+class MapgenFlat : public MapgenBasic
+{
+public:
+       MapgenFlat(int mapgenid, MapgenFlatParams *params, EmergeManager *emerge);
+       ~MapgenFlat();
+
+       virtual MapgenType getType() const { return MAPGEN_FLAT; }
+
+       virtual void makeChunk(BlockMakeData *data);
+       int getSpawnLevelAtPoint(v2s16 p);
+       s16 generateTerrain();
+
+private:
+       s16 ground_level;
+       s16 large_cave_depth;
+       float lake_threshold;
+       float lake_steepness;
+       float hill_threshold;
+       float hill_steepness;
+       Noise *noise_terrain;
+};
diff --git a/src/mapgen/mapgen_fractal.cpp b/src/mapgen/mapgen_fractal.cpp
new file mode 100644 (file)
index 0000000..b068858
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+Minetest
+Copyright (C) 2015-2017 paramat
+Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "content_sao.h"
+#include "nodedef.h"
+#include "voxelalgorithms.h"
+//#include "profiler.h" // For TimeTaker
+#include "settings.h" // For g_settings
+#include "emerge.h"
+#include "dungeongen.h"
+#include "cavegen.h"
+#include "mg_biome.h"
+#include "mg_ore.h"
+#include "mg_decoration.h"
+#include "mapgen_fractal.h"
+
+
+FlagDesc flagdesc_mapgen_fractal[] = {
+       {NULL,    0}
+};
+
+///////////////////////////////////////////////////////////////////////////////////////
+
+
+MapgenFractal::MapgenFractal(int mapgenid, MapgenFractalParams *params, EmergeManager *emerge)
+       : MapgenBasic(mapgenid, params, emerge)
+{
+       spflags          = params->spflags;
+       cave_width       = params->cave_width;
+       large_cave_depth = params->large_cave_depth;
+       lava_depth       = params->lava_depth;
+       fractal          = params->fractal;
+       iterations       = params->iterations;
+       scale            = params->scale;
+       offset           = params->offset;
+       slice_w          = params->slice_w;
+       julia_x          = params->julia_x;
+       julia_y          = params->julia_y;
+       julia_z          = params->julia_z;
+       julia_w          = params->julia_w;
+
+       //// 2D terrain noise
+       noise_seabed       = new Noise(&params->np_seabed, seed, csize.X, csize.Z);
+       noise_filler_depth = new Noise(&params->np_filler_depth, seed, csize.X, csize.Z);
+
+       MapgenBasic::np_cave1 = params->np_cave1;
+       MapgenBasic::np_cave2 = params->np_cave2;
+
+       formula = fractal / 2 + fractal % 2;
+       julia   = fractal % 2 == 0;
+}
+
+
+MapgenFractal::~MapgenFractal()
+{
+       delete noise_seabed;
+       delete noise_filler_depth;
+}
+
+
+MapgenFractalParams::MapgenFractalParams():
+       np_seabed       (-14, 9,   v3f(600, 600, 600), 41900, 5, 0.6, 2.0),
+       np_filler_depth (0,   1.2, v3f(150, 150, 150), 261,   3, 0.7, 2.0),
+       np_cave1        (0,   12,  v3f(61,  61,  61),  52534, 3, 0.5, 2.0),
+       np_cave2        (0,   12,  v3f(67,  67,  67),  10325, 3, 0.5, 2.0)
+{
+}
+
+
+void MapgenFractalParams::readParams(const Settings *settings)
+{
+       settings->getFlagStrNoEx("mgfractal_spflags",      spflags, flagdesc_mapgen_fractal);
+       settings->getFloatNoEx("mgfractal_cave_width",     cave_width);
+       settings->getS16NoEx("mgfractal_large_cave_depth", large_cave_depth);
+       settings->getS16NoEx("mgfractal_lava_depth",       lava_depth);
+       settings->getU16NoEx("mgfractal_fractal",          fractal);
+       settings->getU16NoEx("mgfractal_iterations",       iterations);
+       settings->getV3FNoEx("mgfractal_scale",            scale);
+       settings->getV3FNoEx("mgfractal_offset",           offset);
+       settings->getFloatNoEx("mgfractal_slice_w",        slice_w);
+       settings->getFloatNoEx("mgfractal_julia_x",        julia_x);
+       settings->getFloatNoEx("mgfractal_julia_y",        julia_y);
+       settings->getFloatNoEx("mgfractal_julia_z",        julia_z);
+       settings->getFloatNoEx("mgfractal_julia_w",        julia_w);
+
+       settings->getNoiseParams("mgfractal_np_seabed",       np_seabed);
+       settings->getNoiseParams("mgfractal_np_filler_depth", np_filler_depth);
+       settings->getNoiseParams("mgfractal_np_cave1",        np_cave1);
+       settings->getNoiseParams("mgfractal_np_cave2",        np_cave2);
+}
+
+
+void MapgenFractalParams::writeParams(Settings *settings) const
+{
+       settings->setFlagStr("mgfractal_spflags",      spflags, flagdesc_mapgen_fractal, U32_MAX);
+       settings->setFloat("mgfractal_cave_width",     cave_width);
+       settings->setS16("mgfractal_large_cave_depth", large_cave_depth);
+       settings->setS16("mgfractal_lava_depth",       lava_depth);
+       settings->setU16("mgfractal_fractal",          fractal);
+       settings->setU16("mgfractal_iterations",       iterations);
+       settings->setV3F("mgfractal_scale",            scale);
+       settings->setV3F("mgfractal_offset",           offset);
+       settings->setFloat("mgfractal_slice_w",        slice_w);
+       settings->setFloat("mgfractal_julia_x",        julia_x);
+       settings->setFloat("mgfractal_julia_y",        julia_y);
+       settings->setFloat("mgfractal_julia_z",        julia_z);
+       settings->setFloat("mgfractal_julia_w",        julia_w);
+
+       settings->setNoiseParams("mgfractal_np_seabed",       np_seabed);
+       settings->setNoiseParams("mgfractal_np_filler_depth", np_filler_depth);
+       settings->setNoiseParams("mgfractal_np_cave1",        np_cave1);
+       settings->setNoiseParams("mgfractal_np_cave2",        np_cave2);
+}
+
+
+/////////////////////////////////////////////////////////////////
+
+
+int MapgenFractal::getSpawnLevelAtPoint(v2s16 p)
+{
+       bool solid_below = false;  // Dry solid node is present below to spawn on
+       u8 air_count = 0;  // Consecutive air nodes above the dry solid node
+       s16 seabed_level = NoisePerlin2D(&noise_seabed->np, p.X, p.Y, seed);
+       // Seabed can rise above water_level or might be raised to create dry land
+       s16 search_start = MYMAX(seabed_level, water_level + 1);
+       if (seabed_level > water_level)
+               solid_below = true;
+
+       for (s16 y = search_start; y <= search_start + 128; y++) {
+               if (getFractalAtPoint(p.X, y, p.Y)) {  // Fractal node
+                       solid_below = true;
+                       air_count = 0;
+               } else if (solid_below) {  // Air above solid node
+                       air_count++;
+                       // 3 to account for snowblock dust
+                       if (air_count == 3)
+                               return y - 2;
+               }
+       }
+
+       return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
+}
+
+
+void MapgenFractal::makeChunk(BlockMakeData *data)
+{
+       // Pre-conditions
+       assert(data->vmanip);
+       assert(data->nodedef);
+       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
+               data->blockpos_requested.Y >= data->blockpos_min.Y &&
+               data->blockpos_requested.Z >= data->blockpos_min.Z);
+       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
+               data->blockpos_requested.Y <= data->blockpos_max.Y &&
+               data->blockpos_requested.Z <= data->blockpos_max.Z);
+
+       this->generating = true;
+       this->vm   = data->vmanip;
+       this->ndef = data->nodedef;
+       //TimeTaker t("makeChunk");
+
+       v3s16 blockpos_min = data->blockpos_min;
+       v3s16 blockpos_max = data->blockpos_max;
+       node_min = blockpos_min * MAP_BLOCKSIZE;
+       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
+       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+
+       blockseed = getBlockSeed2(full_node_min, seed);
+
+       // Generate base terrain, mountains, and ridges with initial heightmaps
+       s16 stone_surface_max_y = generateTerrain();
+
+       // Create heightmap
+       updateHeightmap(node_min, node_max);
+
+       // Init biome generator, place biome-specific nodes, and build biomemap
+       biomegen->calcBiomeNoise(node_min);
+
+       MgStoneType mgstone_type;
+       content_t biome_stone;
+       generateBiomes(&mgstone_type, &biome_stone);
+
+       if (flags & MG_CAVES)
+               generateCaves(stone_surface_max_y, large_cave_depth);
+
+       if (flags & MG_DUNGEONS)
+               generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
+
+       // Generate the registered decorations
+       if (flags & MG_DECORATIONS)
+               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+
+       // Generate the registered ores
+       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+
+       // Sprinkle some dust on top after everything else was generated
+       dustTopNodes();
+
+       //printf("makeChunk: %dms\n", t.stop());
+
+       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
+
+       if (flags & MG_LIGHT)
+               calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
+                       full_node_min, full_node_max);
+
+       //setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE,
+       //                      node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF);
+
+       this->generating = false;
+}
+
+
+bool MapgenFractal::getFractalAtPoint(s16 x, s16 y, s16 z)
+{
+       float cx, cy, cz, cw, ox, oy, oz, ow;
+
+       if (julia) {  // Julia set
+               cx = julia_x;
+               cy = julia_y;
+               cz = julia_z;
+               cw = julia_w;
+               ox = (float)x / scale.X - offset.X;
+               oy = (float)y / scale.Y - offset.Y;
+               oz = (float)z / scale.Z - offset.Z;
+               ow = slice_w;
+       } else {  // Mandelbrot set
+               cx = (float)x / scale.X - offset.X;
+               cy = (float)y / scale.Y - offset.Y;
+               cz = (float)z / scale.Z - offset.Z;
+               cw = slice_w;
+               ox = 0.0f;
+               oy = 0.0f;
+               oz = 0.0f;
+               ow = 0.0f;
+       }
+
+       float nx = 0.0f;
+       float ny = 0.0f;
+       float nz = 0.0f;
+       float nw = 0.0f;
+
+       for (u16 iter = 0; iter < iterations; iter++) {
+               switch (formula) {
+               default:
+               case 1: // 4D "Roundy"
+                       nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
+                       ny = 2.0f * (ox * oy + oz * ow) + cy;
+                       nz = 2.0f * (ox * oz + oy * ow) + cz;
+                       nw = 2.0f * (ox * ow + oy * oz) + cw;
+                       break;
+               case 2: // 4D "Squarry"
+                       nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
+                       ny = 2.0f * (ox * oy + oz * ow) + cy;
+                       nz = 2.0f * (ox * oz + oy * ow) + cz;
+                       nw = 2.0f * (ox * ow - oy * oz) + cw;
+                       break;
+               case 3: // 4D "Mandy Cousin"
+                       nx = ox * ox - oy * oy - oz * oz + ow * ow + cx;
+                       ny = 2.0f * (ox * oy + oz * ow) + cy;
+                       nz = 2.0f * (ox * oz + oy * ow) + cz;
+                       nw = 2.0f * (ox * ow + oy * oz) + cw;
+                       break;
+               case 4: // 4D "Variation"
+                       nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
+                       ny = 2.0f * (ox * oy + oz * ow) + cy;
+                       nz = 2.0f * (ox * oz - oy * ow) + cz;
+                       nw = 2.0f * (ox * ow + oy * oz) + cw;
+                       break;
+               case 5: // 3D "Mandelbrot/Mandelbar"
+                       nx = ox * ox - oy * oy - oz * oz + cx;
+                       ny = 2.0f * ox * oy + cy;
+                       nz = -2.0f * ox * oz + cz;
+                       break;
+               case 6: // 3D "Christmas Tree"
+                       // Altering the formula here is necessary to avoid division by zero
+                       if (fabs(oz) < 0.000000001f) {
+                               nx = ox * ox - oy * oy - oz * oz + cx;
+                               ny = 2.0f * oy * ox + cy;
+                               nz = 4.0f * oz * ox + cz;
+                       } else {
+                               float a = (2.0f * ox) / (sqrt(oy * oy + oz * oz));
+                               nx = ox * ox - oy * oy - oz * oz + cx;
+                               ny = a * (oy * oy - oz * oz) + cy;
+                               nz = a * 2.0f * oy * oz + cz;
+                       }
+                       break;
+               case 7: // 3D "Mandelbulb"
+                       if (fabs(oy) < 0.000000001f) {
+                               nx = ox * ox - oz * oz + cx;
+                               ny = cy;
+                               nz = -2.0f * oz * sqrt(ox * ox) + cz;
+                       } else {
+                               float a = 1.0f - (oz * oz) / (ox * ox + oy * oy);
+                               nx = (ox * ox - oy * oy) * a + cx;
+                               ny = 2.0f * ox * oy * a + cy;
+                               nz = -2.0f * oz * sqrt(ox * ox + oy * oy) + cz;
+                       }
+                       break;
+               case 8: // 3D "Cosine Mandelbulb"
+                       if (fabs(oy) < 0.000000001f) {
+                               nx = 2.0f * ox * oz + cx;
+                               ny = 4.0f * oy * oz + cy;
+                               nz = oz * oz - ox * ox - oy * oy + cz;
+                       } else {
+                               float a = (2.0f * oz) / sqrt(ox * ox + oy * oy);
+                               nx = (ox * ox - oy * oy) * a + cx;
+                               ny = 2.0f * ox * oy * a + cy;
+                               nz = oz * oz - ox * ox - oy * oy + cz;
+                       }
+                       break;
+               case 9: // 4D "Mandelbulb"
+                       float rxy = sqrt(ox * ox + oy * oy);
+                       float rxyz = sqrt(ox * ox + oy * oy + oz * oz);
+                       if (fabs(ow) < 0.000000001f && fabs(oz) < 0.000000001f) {
+                               nx = (ox * ox - oy * oy) + cx;
+                               ny = 2.0f * ox * oy + cy;
+                               nz = -2.0f * rxy * oz + cz;
+                               nw = 2.0f * rxyz * ow + cw;
+                       } else {
+                               float a = 1.0f - (ow * ow) / (rxyz * rxyz);
+                               float b = a * (1.0f - (oz * oz) / (rxy * rxy));
+                               nx = (ox * ox - oy * oy) * b + cx;
+                               ny = 2.0f * ox * oy * b + cy;
+                               nz = -2.0f * rxy * oz * a + cz;
+                               nw = 2.0f * rxyz * ow + cw;
+                       }
+                       break;
+               }
+
+               if (nx * nx + ny * ny + nz * nz + nw * nw > 4.0f)
+                       return false;
+
+               ox = nx;
+               oy = ny;
+               oz = nz;
+               ow = nw;
+       }
+
+       return true;
+}
+
+
+s16 MapgenFractal::generateTerrain()
+{
+       MapNode n_air(CONTENT_AIR);
+       MapNode n_stone(c_stone);
+       MapNode n_water(c_water_source);
+
+       s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+       u32 index2d = 0;
+
+       noise_seabed->perlinMap2D(node_min.X, node_min.Z);
+
+       for (s16 z = node_min.Z; z <= node_max.Z; z++) {
+               for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
+                       u32 vi = vm->m_area.index(node_min.X, y, z);
+                       for (s16 x = node_min.X; x <= node_max.X; x++, vi++, index2d++) {
+                               if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
+                                       s16 seabed_height = noise_seabed->result[index2d];
+
+                                       if (y <= seabed_height || getFractalAtPoint(x, y, z)) {
+                                               vm->m_data[vi] = n_stone;
+                                               if (y > stone_surface_max_y)
+                                                       stone_surface_max_y = y;
+                                       } else if (y <= water_level) {
+                                               vm->m_data[vi] = n_water;
+                                       } else {
+                                               vm->m_data[vi] = n_air;
+                                       }
+                               }
+                       }
+                       index2d -= ystride;
+               }
+               index2d += ystride;
+       }
+
+       return stone_surface_max_y;
+}
diff --git a/src/mapgen/mapgen_fractal.h b/src/mapgen/mapgen_fractal.h
new file mode 100644 (file)
index 0000000..be49415
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+Minetest
+Copyright (C) 2015-2017 paramat
+Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+Fractal formulas from http://www.bugman123.com/Hypercomplex/index.html
+by Paul Nylander, and from http://www.fractalforums.com, thank you.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "mapgen.h"
+
+class BiomeManager;
+
+extern FlagDesc flagdesc_mapgen_fractal[];
+
+struct MapgenFractalParams : public MapgenParams
+{
+       u32 spflags = 0;
+       float cave_width = 0.09f;
+       s16 large_cave_depth = -33;
+       s16 lava_depth = -256;
+       u16 fractal = 1;
+       u16 iterations = 11;
+       v3f scale = v3f(4096.0, 1024.0, 4096.0);
+       v3f offset = v3f(1.52, 0.0, 0.0);
+       float slice_w = 0.0f;
+       float julia_x = 0.267f;
+       float julia_y = 0.2f;
+       float julia_z = 0.133f;
+       float julia_w = 0.067f;
+       NoiseParams np_seabed;
+       NoiseParams np_filler_depth;
+       NoiseParams np_cave1;
+       NoiseParams np_cave2;
+
+       MapgenFractalParams();
+       ~MapgenFractalParams() = default;
+
+       void readParams(const Settings *settings);
+       void writeParams(Settings *settings) const;
+};
+
+class MapgenFractal : public MapgenBasic
+{
+public:
+       MapgenFractal(int mapgenid, MapgenFractalParams *params, EmergeManager *emerge);
+       ~MapgenFractal();
+
+       virtual MapgenType getType() const { return MAPGEN_FRACTAL; }
+
+       virtual void makeChunk(BlockMakeData *data);
+       int getSpawnLevelAtPoint(v2s16 p);
+       bool getFractalAtPoint(s16 x, s16 y, s16 z);
+       s16 generateTerrain();
+
+private:
+       u16 formula;
+       bool julia;
+
+       s16 large_cave_depth;
+       u16 fractal;
+       u16 iterations;
+       v3f scale;
+       v3f offset;
+       float slice_w;
+       float julia_x;
+       float julia_y;
+       float julia_z;
+       float julia_w;
+       Noise *noise_seabed;
+};
diff --git a/src/mapgen/mapgen_singlenode.cpp b/src/mapgen/mapgen_singlenode.cpp
new file mode 100644 (file)
index 0000000..ee8746b
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+Minetest
+Copyright (C) 2013-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "mapgen_singlenode.h"
+#include "voxel.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "nodedef.h"
+#include "voxelalgorithms.h"
+#include "emerge.h"
+
+
+MapgenSinglenode::MapgenSinglenode(int mapgenid,
+       MapgenParams *params, EmergeManager *emerge)
+       : Mapgen(mapgenid, params, emerge)
+{
+       flags = params->flags;
+
+       INodeDefManager *ndef = emerge->ndef;
+
+       c_node = ndef->getId("mapgen_singlenode");
+       if (c_node == CONTENT_IGNORE)
+               c_node = CONTENT_AIR;
+
+       MapNode n_node(c_node);
+       set_light = (ndef->get(n_node).sunlight_propagates) ? LIGHT_SUN : 0x00;
+}
+
+
+//////////////////////// Map generator
+
+void MapgenSinglenode::makeChunk(BlockMakeData *data)
+{
+       // Pre-conditions
+       assert(data->vmanip);
+       assert(data->nodedef);
+       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
+               data->blockpos_requested.Y >= data->blockpos_min.Y &&
+               data->blockpos_requested.Z >= data->blockpos_min.Z);
+       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
+               data->blockpos_requested.Y <= data->blockpos_max.Y &&
+               data->blockpos_requested.Z <= data->blockpos_max.Z);
+
+       this->generating = true;
+       this->vm   = data->vmanip;
+       this->ndef = data->nodedef;
+
+       v3s16 blockpos_min = data->blockpos_min;
+       v3s16 blockpos_max = data->blockpos_max;
+
+       // Area of central chunk
+       v3s16 node_min = blockpos_min * MAP_BLOCKSIZE;
+       v3s16 node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+
+       blockseed = getBlockSeed2(node_min, data->seed);
+
+       MapNode n_node(c_node);
+
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 y = node_min.Y; y <= node_max.Y; y++) {
+               u32 i = vm->m_area.index(node_min.X, y, z);
+               for (s16 x = node_min.X; x <= node_max.X; x++) {
+                       if (vm->m_data[i].getContent() == CONTENT_IGNORE)
+                               vm->m_data[i] = n_node;
+                       i++;
+               }
+       }
+
+       // Add top and bottom side of water to transforming_liquid queue
+       updateLiquid(&data->transforming_liquid, node_min, node_max);
+
+       // Set lighting
+       if ((flags & MG_LIGHT) && set_light == LIGHT_SUN)
+               setLighting(LIGHT_SUN, node_min, node_max);
+
+       this->generating = false;
+}
+
+
+int MapgenSinglenode::getSpawnLevelAtPoint(v2s16 p)
+{
+       return 0;
+}
diff --git a/src/mapgen/mapgen_singlenode.h b/src/mapgen/mapgen_singlenode.h
new file mode 100644 (file)
index 0000000..7678613
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+Minetest
+Copyright (C) 2013-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "mapgen.h"
+
+struct MapgenSinglenodeParams : public MapgenParams
+{
+       MapgenSinglenodeParams() = default;
+       ~MapgenSinglenodeParams() = default;
+
+       void readParams(const Settings *settings) {}
+       void writeParams(Settings *settings) const {}
+};
+
+class MapgenSinglenode : public Mapgen
+{
+public:
+       u32 flags;
+       content_t c_node;
+       u8 set_light;
+
+       MapgenSinglenode(int mapgenid, MapgenParams *params, EmergeManager *emerge);
+       ~MapgenSinglenode() = default;
+
+       virtual MapgenType getType() const { return MAPGEN_SINGLENODE; }
+
+       void makeChunk(BlockMakeData *data);
+       int getSpawnLevelAtPoint(v2s16 p);
+};
diff --git a/src/mapgen/mapgen_v5.cpp b/src/mapgen/mapgen_v5.cpp
new file mode 100644 (file)
index 0000000..5f54872
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+Minetest
+Copyright (C) 2014-2017 paramat
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "content_sao.h"
+#include "nodedef.h"
+#include "voxelalgorithms.h"
+//#include "profiler.h" // For TimeTaker
+#include "settings.h" // For g_settings
+#include "emerge.h"
+#include "dungeongen.h"
+#include "cavegen.h"
+#include "mg_biome.h"
+#include "mg_ore.h"
+#include "mg_decoration.h"
+#include "mapgen_v5.h"
+
+
+FlagDesc flagdesc_mapgen_v5[] = {
+       {"caverns", MGV5_CAVERNS},
+       {NULL,      0}
+};
+
+
+MapgenV5::MapgenV5(int mapgenid, MapgenV5Params *params, EmergeManager *emerge)
+       : MapgenBasic(mapgenid, params, emerge)
+{
+       spflags          = params->spflags;
+       cave_width       = params->cave_width;
+       large_cave_depth = params->large_cave_depth;
+       lava_depth       = params->lava_depth;
+       cavern_limit     = params->cavern_limit;
+       cavern_taper     = params->cavern_taper;
+       cavern_threshold = params->cavern_threshold;
+
+       // Terrain noise
+       noise_filler_depth = new Noise(&params->np_filler_depth, seed, csize.X, csize.Z);
+       noise_factor       = new Noise(&params->np_factor,       seed, csize.X, csize.Z);
+       noise_height       = new Noise(&params->np_height,       seed, csize.X, csize.Z);
+
+       // 3D terrain noise
+       // 1-up 1-down overgeneration
+       noise_ground = new Noise(&params->np_ground, seed, csize.X, csize.Y + 2, csize.Z);
+       // 1 down overgeneration
+       MapgenBasic::np_cave1  = params->np_cave1;
+       MapgenBasic::np_cave2  = params->np_cave2;
+       MapgenBasic::np_cavern = params->np_cavern;
+}
+
+
+MapgenV5::~MapgenV5()
+{
+       delete noise_filler_depth;
+       delete noise_factor;
+       delete noise_height;
+       delete noise_ground;
+}
+
+
+MapgenV5Params::MapgenV5Params():
+       np_filler_depth (0, 1,  v3f(150, 150, 150), 261,    4, 0.7,  2.0),
+       np_factor       (0, 1,  v3f(250, 250, 250), 920381, 3, 0.45, 2.0),
+       np_height       (0, 10, v3f(250, 250, 250), 84174,  4, 0.5,  2.0),
+       np_ground       (0, 40, v3f(80,  80,  80),  983240, 4, 0.55, 2.0, NOISE_FLAG_EASED),
+       np_cave1        (0, 12, v3f(50,  50,  50),  52534,  4, 0.5,  2.0),
+       np_cave2        (0, 12, v3f(50,  50,  50),  10325,  4, 0.5,  2.0),
+       np_cavern       (0, 1,  v3f(384, 128, 384), 723,    5, 0.63, 2.0)
+{
+}
+
+
+void MapgenV5Params::readParams(const Settings *settings)
+{
+       settings->getFlagStrNoEx("mgv5_spflags",        spflags, flagdesc_mapgen_v5);
+       settings->getFloatNoEx("mgv5_cave_width",       cave_width);
+       settings->getS16NoEx("mgv5_large_cave_depth",   large_cave_depth);
+       settings->getS16NoEx("mgv5_lava_depth",         lava_depth);
+       settings->getS16NoEx("mgv5_cavern_limit",       cavern_limit);
+       settings->getS16NoEx("mgv5_cavern_taper",       cavern_taper);
+       settings->getFloatNoEx("mgv5_cavern_threshold", cavern_threshold);
+
+       settings->getNoiseParams("mgv5_np_filler_depth", np_filler_depth);
+       settings->getNoiseParams("mgv5_np_factor",       np_factor);
+       settings->getNoiseParams("mgv5_np_height",       np_height);
+       settings->getNoiseParams("mgv5_np_ground",       np_ground);
+       settings->getNoiseParams("mgv5_np_cave1",        np_cave1);
+       settings->getNoiseParams("mgv5_np_cave2",        np_cave2);
+       settings->getNoiseParams("mgv5_np_cavern",       np_cavern);
+}
+
+
+void MapgenV5Params::writeParams(Settings *settings) const
+{
+       settings->setFlagStr("mgv5_spflags",        spflags, flagdesc_mapgen_v5, U32_MAX);
+       settings->setFloat("mgv5_cave_width",       cave_width);
+       settings->setS16("mgv5_large_cave_depth",   large_cave_depth);
+       settings->setS16("mgv5_lava_depth",         lava_depth);
+       settings->setS16("mgv5_cavern_limit",       cavern_limit);
+       settings->setS16("mgv5_cavern_taper",       cavern_taper);
+       settings->setFloat("mgv5_cavern_threshold", cavern_threshold);
+
+       settings->setNoiseParams("mgv5_np_filler_depth", np_filler_depth);
+       settings->setNoiseParams("mgv5_np_factor",       np_factor);
+       settings->setNoiseParams("mgv5_np_height",       np_height);
+       settings->setNoiseParams("mgv5_np_ground",       np_ground);
+       settings->setNoiseParams("mgv5_np_cave1",        np_cave1);
+       settings->setNoiseParams("mgv5_np_cave2",        np_cave2);
+       settings->setNoiseParams("mgv5_np_cavern",       np_cavern);
+}
+
+
+int MapgenV5::getSpawnLevelAtPoint(v2s16 p)
+{
+
+       float f = 0.55 + NoisePerlin2D(&noise_factor->np, p.X, p.Y, seed);
+       if (f < 0.01)
+               f = 0.01;
+       else if (f >= 1.0)
+               f *= 1.6;
+       float h = NoisePerlin2D(&noise_height->np, p.X, p.Y, seed);
+
+       // noise_height 'offset' is the average level of terrain. At least 50% of
+       // terrain will be below this.
+       // Raising the maximum spawn level above 'water_level + 16' is necessary
+       // for when noise_height 'offset' is set much higher than water_level.
+       s16 max_spawn_y = MYMAX(noise_height->np.offset, water_level + 16);
+
+       // Starting spawn search at max_spawn_y + 128 ensures 128 nodes of open
+       // space above spawn position. Avoids spawning in possibly sealed voids.
+       for (s16 y = max_spawn_y + 128; y >= water_level; y--) {
+               float n_ground = NoisePerlin3D(&noise_ground->np, p.X, y, p.Y, seed);
+
+               if (n_ground * f > y - h) {  // If solid
+                       if (y < water_level || y > max_spawn_y)
+                               return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
+
+                       // y + 2 because y is surface and due to biome 'dust' nodes.
+                       return y + 2;
+               }
+       }
+       // Unsuitable spawn position, no ground found
+       return MAX_MAP_GENERATION_LIMIT;
+}
+
+
+void MapgenV5::makeChunk(BlockMakeData *data)
+{
+       // Pre-conditions
+       assert(data->vmanip);
+       assert(data->nodedef);
+       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
+               data->blockpos_requested.Y >= data->blockpos_min.Y &&
+               data->blockpos_requested.Z >= data->blockpos_min.Z);
+       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
+               data->blockpos_requested.Y <= data->blockpos_max.Y &&
+               data->blockpos_requested.Z <= data->blockpos_max.Z);
+
+       this->generating = true;
+       this->vm   = data->vmanip;
+       this->ndef = data->nodedef;
+       //TimeTaker t("makeChunk");
+
+       v3s16 blockpos_min = data->blockpos_min;
+       v3s16 blockpos_max = data->blockpos_max;
+       node_min = blockpos_min * MAP_BLOCKSIZE;
+       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
+       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+
+       // Create a block-specific seed
+       blockseed = getBlockSeed2(full_node_min, seed);
+
+       // Generate base terrain
+       s16 stone_surface_max_y = generateBaseTerrain();
+
+       // Create heightmap
+       updateHeightmap(node_min, node_max);
+
+       // Init biome generator, place biome-specific nodes, and build biomemap
+       biomegen->calcBiomeNoise(node_min);
+
+       MgStoneType mgstone_type;
+       content_t biome_stone;
+       generateBiomes(&mgstone_type, &biome_stone);
+
+       // Generate caverns, tunnels and classic caves
+       if (flags & MG_CAVES) {
+               bool near_cavern = false;
+               // Generate caverns
+               if (spflags & MGV5_CAVERNS)
+                       near_cavern = generateCaverns(stone_surface_max_y);
+               // Generate tunnels and classic caves
+               if (near_cavern)
+                       // Disable classic caves in this mapchunk by setting
+                       // 'large cave depth' to world base. Avoids excessive liquid in
+                       // large caverns and floating blobs of overgenerated liquid.
+                       generateCaves(stone_surface_max_y, -MAX_MAP_GENERATION_LIMIT);
+               else
+                       generateCaves(stone_surface_max_y, large_cave_depth);
+       }
+
+       // Generate dungeons and desert temples
+       if (flags & MG_DUNGEONS)
+               generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
+
+       // Generate the registered decorations
+       if (flags & MG_DECORATIONS)
+               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+
+       // Generate the registered ores
+       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+
+       // Sprinkle some dust on top after everything else was generated
+       dustTopNodes();
+
+       //printf("makeChunk: %dms\n", t.stop());
+
+       // Add top and bottom side of water to transforming_liquid queue
+       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
+
+       // Calculate lighting
+       if (flags & MG_LIGHT) {
+               calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
+                       full_node_min, full_node_max);
+       }
+
+       this->generating = false;
+}
+
+
+int MapgenV5::generateBaseTerrain()
+{
+       u32 index = 0;
+       u32 index2d = 0;
+       int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+
+       noise_factor->perlinMap2D(node_min.X, node_min.Z);
+       noise_height->perlinMap2D(node_min.X, node_min.Z);
+       noise_ground->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+
+       for (s16 z=node_min.Z; z<=node_max.Z; z++) {
+               for (s16 y=node_min.Y - 1; y<=node_max.Y + 1; y++) {
+                       u32 vi = vm->m_area.index(node_min.X, y, z);
+                       for (s16 x=node_min.X; x<=node_max.X; x++, vi++, index++, index2d++) {
+                               if (vm->m_data[vi].getContent() != CONTENT_IGNORE)
+                                       continue;
+
+                               float f = 0.55 + noise_factor->result[index2d];
+                               if (f < 0.01)
+                                       f = 0.01;
+                               else if (f >= 1.0)
+                                       f *= 1.6;
+                               float h = noise_height->result[index2d];
+
+                               if (noise_ground->result[index] * f < y - h) {
+                                       if (y <= water_level)
+                                               vm->m_data[vi] = MapNode(c_water_source);
+                                       else
+                                               vm->m_data[vi] = MapNode(CONTENT_AIR);
+                               } else {
+                                       vm->m_data[vi] = MapNode(c_stone);
+                                       if (y > stone_surface_max_y)
+                                               stone_surface_max_y = y;
+                               }
+                       }
+                       index2d -= ystride;
+               }
+               index2d += ystride;
+       }
+
+       return stone_surface_max_y;
+}
diff --git a/src/mapgen/mapgen_v5.h b/src/mapgen/mapgen_v5.h
new file mode 100644 (file)
index 0000000..44b0a09
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+Minetest
+Copyright (C) 2014-2017 paramat
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "mapgen.h"
+
+///////// Mapgen V5 flags
+#define MGV5_CAVERNS 0x01
+
+class BiomeManager;
+
+extern FlagDesc flagdesc_mapgen_v5[];
+
+struct MapgenV5Params : public MapgenParams
+{
+       u32 spflags = MGV5_CAVERNS;
+       float cave_width = 0.125f;
+       s16 large_cave_depth = -256;
+       s16 lava_depth = -256;
+       s16 cavern_limit = -256;
+       s16 cavern_taper = 256;
+       float cavern_threshold = 0.7f;
+
+       NoiseParams np_filler_depth;
+       NoiseParams np_factor;
+       NoiseParams np_height;
+       NoiseParams np_ground;
+       NoiseParams np_cave1;
+       NoiseParams np_cave2;
+       NoiseParams np_cavern;
+
+       MapgenV5Params();
+       ~MapgenV5Params() = default;
+
+       void readParams(const Settings *settings);
+       void writeParams(Settings *settings) const;
+};
+
+class MapgenV5 : public MapgenBasic
+{
+public:
+       MapgenV5(int mapgenid, MapgenV5Params *params, EmergeManager *emerge);
+       ~MapgenV5();
+
+       virtual MapgenType getType() const { return MAPGEN_V5; }
+
+       virtual void makeChunk(BlockMakeData *data);
+       int getSpawnLevelAtPoint(v2s16 p);
+       int generateBaseTerrain();
+
+private:
+       s16 large_cave_depth;
+       Noise *noise_factor;
+       Noise *noise_height;
+       Noise *noise_ground;
+};
diff --git a/src/mapgen/mapgen_v6.cpp b/src/mapgen/mapgen_v6.cpp
new file mode 100644 (file)
index 0000000..6dcf834
--- /dev/null
@@ -0,0 +1,1123 @@
+/*
+Minetest
+Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+//#include "serverobject.h"
+#include "content_sao.h"
+#include "nodedef.h"
+#include "voxelalgorithms.h"
+//#include "profiler.h" // For TimeTaker
+#include "settings.h" // For g_settings
+#include "emerge.h"
+#include "dungeongen.h"
+#include "cavegen.h"
+#include "treegen.h"
+#include "mg_ore.h"
+#include "mg_decoration.h"
+#include "mapgen_v6.h"
+
+
+FlagDesc flagdesc_mapgen_v6[] = {
+       {"jungles",    MGV6_JUNGLES},
+       {"biomeblend", MGV6_BIOMEBLEND},
+       {"mudflow",    MGV6_MUDFLOW},
+       {"snowbiomes", MGV6_SNOWBIOMES},
+       {"flat",       MGV6_FLAT},
+       {"trees",      MGV6_TREES},
+       {NULL,         0}
+};
+
+
+/////////////////////////////////////////////////////////////////////////////
+
+
+MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge)
+       : Mapgen(mapgenid, params, emerge)
+{
+       m_emerge = emerge;
+       ystride = csize.X; //////fix this
+
+       heightmap = new s16[csize.X * csize.Z];
+
+       spflags     = params->spflags;
+       freq_desert = params->freq_desert;
+       freq_beach  = params->freq_beach;
+
+       np_cave        = &params->np_cave;
+       np_humidity    = &params->np_humidity;
+       np_trees       = &params->np_trees;
+       np_apple_trees = &params->np_apple_trees;
+
+       //// Create noise objects
+       noise_terrain_base   = new Noise(&params->np_terrain_base,   seed, csize.X, csize.Y);
+       noise_terrain_higher = new Noise(&params->np_terrain_higher, seed, csize.X, csize.Y);
+       noise_steepness      = new Noise(&params->np_steepness,      seed, csize.X, csize.Y);
+       noise_height_select  = new Noise(&params->np_height_select,  seed, csize.X, csize.Y);
+       noise_mud            = new Noise(&params->np_mud,            seed, csize.X, csize.Y);
+       noise_beach          = new Noise(&params->np_beach,          seed, csize.X, csize.Y);
+       noise_biome          = new Noise(&params->np_biome,          seed,
+                       csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
+       noise_humidity       = new Noise(&params->np_humidity,       seed,
+                       csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
+
+       //// Resolve nodes to be used
+       INodeDefManager *ndef = emerge->ndef;
+
+       c_stone           = ndef->getId("mapgen_stone");
+       c_dirt            = ndef->getId("mapgen_dirt");
+       c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass");
+       c_sand            = ndef->getId("mapgen_sand");
+       c_water_source    = ndef->getId("mapgen_water_source");
+       c_lava_source     = ndef->getId("mapgen_lava_source");
+       c_gravel          = ndef->getId("mapgen_gravel");
+       c_desert_stone    = ndef->getId("mapgen_desert_stone");
+       c_desert_sand     = ndef->getId("mapgen_desert_sand");
+       c_dirt_with_snow  = ndef->getId("mapgen_dirt_with_snow");
+       c_snow            = ndef->getId("mapgen_snow");
+       c_snowblock       = ndef->getId("mapgen_snowblock");
+       c_ice             = ndef->getId("mapgen_ice");
+
+       if (c_gravel == CONTENT_IGNORE)
+               c_gravel = c_stone;
+       if (c_desert_stone == CONTENT_IGNORE)
+               c_desert_stone = c_stone;
+       if (c_desert_sand == CONTENT_IGNORE)
+               c_desert_sand = c_sand;
+       if (c_dirt_with_snow == CONTENT_IGNORE)
+               c_dirt_with_snow = c_dirt_with_grass;
+       if (c_snow == CONTENT_IGNORE)
+               c_snow = CONTENT_AIR;
+       if (c_snowblock == CONTENT_IGNORE)
+               c_snowblock = c_dirt_with_grass;
+       if (c_ice == CONTENT_IGNORE)
+               c_ice = c_water_source;
+
+       c_cobble             = ndef->getId("mapgen_cobble");
+       c_mossycobble        = ndef->getId("mapgen_mossycobble");
+       c_stair_cobble       = ndef->getId("mapgen_stair_cobble");
+       c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone");
+
+       if (c_mossycobble == CONTENT_IGNORE)
+               c_mossycobble = c_cobble;
+       if (c_stair_cobble == CONTENT_IGNORE)
+               c_stair_cobble = c_cobble;
+       if (c_stair_desert_stone == CONTENT_IGNORE)
+               c_stair_desert_stone = c_desert_stone;
+}
+
+
+MapgenV6::~MapgenV6()
+{
+       delete noise_terrain_base;
+       delete noise_terrain_higher;
+       delete noise_steepness;
+       delete noise_height_select;
+       delete noise_mud;
+       delete noise_beach;
+       delete noise_biome;
+       delete noise_humidity;
+
+       delete[] heightmap;
+}
+
+
+MapgenV6Params::MapgenV6Params():
+       np_terrain_base   (-4,   20.0, v3f(250.0, 250.0, 250.0), 82341,  5, 0.6,  2.0),
+       np_terrain_higher (20,   16.0, v3f(500.0, 500.0, 500.0), 85039,  5, 0.6,  2.0),
+       np_steepness      (0.85, 0.5,  v3f(125.0, 125.0, 125.0), -932,   5, 0.7,  2.0),
+       np_height_select  (0,    1.0,  v3f(250.0, 250.0, 250.0), 4213,   5, 0.69, 2.0),
+       np_mud            (4,    2.0,  v3f(200.0, 200.0, 200.0), 91013,  3, 0.55, 2.0),
+       np_beach          (0,    1.0,  v3f(250.0, 250.0, 250.0), 59420,  3, 0.50, 2.0),
+       np_biome          (0,    1.0,  v3f(500.0, 500.0, 500.0), 9130,   3, 0.50, 2.0),
+       np_cave           (6,    6.0,  v3f(250.0, 250.0, 250.0), 34329,  3, 0.50, 2.0),
+       np_humidity       (0.5,  0.5,  v3f(500.0, 500.0, 500.0), 72384,  3, 0.50, 2.0),
+       np_trees          (0,    1.0,  v3f(125.0, 125.0, 125.0), 2,      4, 0.66, 2.0),
+       np_apple_trees    (0,    1.0,  v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0)
+{
+}
+
+
+void MapgenV6Params::readParams(const Settings *settings)
+{
+       settings->getFlagStrNoEx("mgv6_spflags", spflags, flagdesc_mapgen_v6);
+       settings->getFloatNoEx("mgv6_freq_desert", freq_desert);
+       settings->getFloatNoEx("mgv6_freq_beach",  freq_beach);
+
+       settings->getNoiseParams("mgv6_np_terrain_base",   np_terrain_base);
+       settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
+       settings->getNoiseParams("mgv6_np_steepness",      np_steepness);
+       settings->getNoiseParams("mgv6_np_height_select",  np_height_select);
+       settings->getNoiseParams("mgv6_np_mud",            np_mud);
+       settings->getNoiseParams("mgv6_np_beach",          np_beach);
+       settings->getNoiseParams("mgv6_np_biome",          np_biome);
+       settings->getNoiseParams("mgv6_np_cave",           np_cave);
+       settings->getNoiseParams("mgv6_np_humidity",       np_humidity);
+       settings->getNoiseParams("mgv6_np_trees",          np_trees);
+       settings->getNoiseParams("mgv6_np_apple_trees",    np_apple_trees);
+}
+
+
+void MapgenV6Params::writeParams(Settings *settings) const
+{
+       settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6, U32_MAX);
+       settings->setFloat("mgv6_freq_desert", freq_desert);
+       settings->setFloat("mgv6_freq_beach",  freq_beach);
+
+       settings->setNoiseParams("mgv6_np_terrain_base",   np_terrain_base);
+       settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
+       settings->setNoiseParams("mgv6_np_steepness",      np_steepness);
+       settings->setNoiseParams("mgv6_np_height_select",  np_height_select);
+       settings->setNoiseParams("mgv6_np_mud",            np_mud);
+       settings->setNoiseParams("mgv6_np_beach",          np_beach);
+       settings->setNoiseParams("mgv6_np_biome",          np_biome);
+       settings->setNoiseParams("mgv6_np_cave",           np_cave);
+       settings->setNoiseParams("mgv6_np_humidity",       np_humidity);
+       settings->setNoiseParams("mgv6_np_trees",          np_trees);
+       settings->setNoiseParams("mgv6_np_apple_trees",    np_apple_trees);
+}
+
+
+//////////////////////// Some helper functions for the map generator
+
+// Returns Y one under area minimum if not found
+s16 MapgenV6::find_stone_level(v2s16 p2d)
+{
+       const v3s16 &em = vm->m_area.getExtent();
+       s16 y_nodes_max = vm->m_area.MaxEdge.Y;
+       s16 y_nodes_min = vm->m_area.MinEdge.Y;
+       u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
+       s16 y;
+
+       for (y = y_nodes_max; y >= y_nodes_min; y--) {
+               content_t c = vm->m_data[i].getContent();
+               if (c != CONTENT_IGNORE && (c == c_stone || c == c_desert_stone))
+                       break;
+
+               vm->m_area.add_y(em, i, -1);
+       }
+       return (y >= y_nodes_min) ? y : y_nodes_min - 1;
+}
+
+
+// Required by mapgen.h
+bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
+{
+       /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
+                       seed, v2s16(blockpos.X, blockpos.Z));*/
+       // Nah, this is just a heuristic, just return something
+       s16 minimum_groundlevel = water_level;
+
+       if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
+               return true;
+
+       return false;
+}
+
+
+//////////////////////// Base terrain height functions
+
+float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
+       float steepness, float height_select)
+{
+       float base   = 1 + terrain_base;
+       float higher = 1 + terrain_higher;
+
+       // Limit higher ground level to at least base
+       if(higher < base)
+               higher = base;
+
+       // Steepness factor of cliffs
+       float b = steepness;
+       b = rangelim(b, 0.0, 1000.0);
+       b = 5 * b * b * b * b * b * b * b;
+       b = rangelim(b, 0.5, 1000.0);
+
+       // Values 1.5...100 give quite horrible looking slopes
+       if (b > 1.5 && b < 100.0)
+               b = (b < 10.0) ? 1.5 : 100.0;
+
+       float a_off = -0.20; // Offset to more low
+       float a = 0.5 + b * (a_off + height_select);
+       a = rangelim(a, 0.0, 1.0); // Limit
+
+       return base * (1.0 - a) + higher * a;
+}
+
+
+float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
+{
+       if (spflags & MGV6_FLAT)
+               return water_level;
+
+       float terrain_base   = NoisePerlin2D_PO(&noise_terrain_base->np,
+                                                       p.X, 0.5, p.Y, 0.5, seed);
+       float terrain_higher = NoisePerlin2D_PO(&noise_terrain_higher->np,
+                                                       p.X, 0.5, p.Y, 0.5, seed);
+       float steepness      = NoisePerlin2D_PO(&noise_steepness->np,
+                                                       p.X, 0.5, p.Y, 0.5, seed);
+       float height_select  = NoisePerlin2D_PO(&noise_height_select->np,
+                                                       p.X, 0.5, p.Y, 0.5, seed);
+
+       return baseTerrainLevel(terrain_base, terrain_higher,
+                                                       steepness, height_select);
+}
+
+
+float MapgenV6::baseTerrainLevelFromMap(v2s16 p)
+{
+       int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
+       return baseTerrainLevelFromMap(index);
+}
+
+
+float MapgenV6::baseTerrainLevelFromMap(int index)
+{
+       if (spflags & MGV6_FLAT)
+               return water_level;
+
+       float terrain_base   = noise_terrain_base->result[index];
+       float terrain_higher = noise_terrain_higher->result[index];
+       float steepness      = noise_steepness->result[index];
+       float height_select  = noise_height_select->result[index];
+
+       return baseTerrainLevel(terrain_base, terrain_higher,
+                                                       steepness, height_select);
+}
+
+
+s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
+{
+       return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
+}
+
+
+int MapgenV6::getGroundLevelAtPoint(v2s16 p)
+{
+       return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
+}
+
+
+int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
+{
+       s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
+       if (level_at_point <= water_level ||
+                       level_at_point > water_level + 16)
+               return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
+
+       return level_at_point;
+}
+
+
+//////////////////////// Noise functions
+
+float MapgenV6::getMudAmount(v2s16 p)
+{
+       int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
+       return getMudAmount(index);
+}
+
+
+bool MapgenV6::getHaveBeach(v2s16 p)
+{
+       int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
+       return getHaveBeach(index);
+}
+
+
+BiomeV6Type MapgenV6::getBiome(v2s16 p)
+{
+       int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
+                       + (p.X - full_node_min.X);
+       return getBiome(index, p);
+}
+
+
+float MapgenV6::getHumidity(v2s16 p)
+{
+       /*double noise = noise2d_perlin(
+               0.5+(float)p.X/500, 0.5+(float)p.Y/500,
+               seed+72384, 4, 0.66);
+       noise = (noise + 1.0)/2.0;*/
+
+       int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
+                       + (p.X - full_node_min.X);
+       float noise = noise_humidity->result[index];
+
+       if (noise < 0.0)
+               noise = 0.0;
+       if (noise > 1.0)
+               noise = 1.0;
+       return noise;
+}
+
+
+float MapgenV6::getTreeAmount(v2s16 p)
+{
+       /*double noise = noise2d_perlin(
+                       0.5+(float)p.X/125, 0.5+(float)p.Y/125,
+                       seed+2, 4, 0.66);*/
+
+       float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
+       float zeroval = -0.39;
+       if (noise < zeroval)
+               return 0;
+
+       return 0.04 * (noise - zeroval) / (1.0 - zeroval);
+}
+
+
+bool MapgenV6::getHaveAppleTree(v2s16 p)
+{
+       /*is_apple_tree = noise2d_perlin(
+               0.5+(float)p.X/100, 0.5+(float)p.Z/100,
+               data->seed+342902, 3, 0.45) > 0.2;*/
+
+       float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
+
+       return noise > 0.2;
+}
+
+
+float MapgenV6::getMudAmount(int index)
+{
+       if (spflags & MGV6_FLAT)
+               return MGV6_AVERAGE_MUD_AMOUNT;
+
+       /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
+                       0.5+(float)p.X/200, 0.5+(float)p.Y/200,
+                       seed+91013, 3, 0.55));*/
+
+       return noise_mud->result[index];
+}
+
+
+bool MapgenV6::getHaveBeach(int index)
+{
+       // Determine whether to have sand here
+       /*double sandnoise = noise2d_perlin(
+                       0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
+                       seed+59420, 3, 0.50);*/
+
+       float sandnoise = noise_beach->result[index];
+       return (sandnoise > freq_beach);
+}
+
+
+BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
+{
+       // Just do something very simple as for now
+       /*double d = noise2d_perlin(
+                       0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
+                       seed+9130, 3, 0.50);*/
+
+       float d = noise_biome->result[index];
+       float h = noise_humidity->result[index];
+
+       if (spflags & MGV6_SNOWBIOMES) {
+               float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
+
+               if (d > MGV6_FREQ_HOT + blend) {
+                       if (h > MGV6_FREQ_JUNGLE + blend)
+                               return BT_JUNGLE;
+
+                       return BT_DESERT;
+               }
+
+               if (d < MGV6_FREQ_SNOW + blend) {
+                       if (h > MGV6_FREQ_TAIGA + blend)
+                               return BT_TAIGA;
+
+                       return BT_TUNDRA;
+               }
+
+               return BT_NORMAL;
+       }
+
+       if (d > freq_desert)
+               return BT_DESERT;
+
+       if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
+                       ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
+               return BT_DESERT;
+
+       if ((spflags & MGV6_JUNGLES) && h > 0.75)
+               return BT_JUNGLE;
+
+       return BT_NORMAL;
+
+}
+
+
+u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
+{
+       s32 x = p.X, y = p.Y, z = p.Z;
+       return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
+}
+
+
+//////////////////////// Map generator
+
+void MapgenV6::makeChunk(BlockMakeData *data)
+{
+       // Pre-conditions
+       assert(data->vmanip);
+       assert(data->nodedef);
+       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
+               data->blockpos_requested.Y >= data->blockpos_min.Y &&
+               data->blockpos_requested.Z >= data->blockpos_min.Z);
+       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
+               data->blockpos_requested.Y <= data->blockpos_max.Y &&
+               data->blockpos_requested.Z <= data->blockpos_max.Z);
+
+       this->generating = true;
+       this->vm   = data->vmanip;
+       this->ndef = data->nodedef;
+
+       // Hack: use minimum block coords for old code that assumes a single block
+       v3s16 blockpos_min = data->blockpos_min;
+       v3s16 blockpos_max = data->blockpos_max;
+
+       // Area of central chunk
+       node_min = blockpos_min * MAP_BLOCKSIZE;
+       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+
+       // Full allocated area
+       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
+       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+
+       central_area_size = node_max - node_min + v3s16(1, 1, 1);
+       assert(central_area_size.X == central_area_size.Z);
+
+       // Create a block-specific seed
+       blockseed = get_blockseed(data->seed, full_node_min);
+
+       // Make some noise
+       calculateNoise();
+
+       // Maximum height of the stone surface and obstacles.
+       // This is used to guide the cave generation
+       s16 stone_surface_max_y;
+
+       // Generate general ground level to full area
+       stone_surface_max_y = generateGround();
+
+       // Create initial heightmap to limit caves
+       updateHeightmap(node_min, node_max);
+
+       const s16 max_spread_amount = MAP_BLOCKSIZE;
+       // Limit dirt flow area by 1 because mud is flown into neighbors.
+       s16 mudflow_minpos = -max_spread_amount + 1;
+       s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
+
+       // Loop this part, it will make stuff look older and newer nicely
+       const u32 age_loops = 2;
+       for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
+               // Make caves (this code is relatively horrible)
+               if (flags & MG_CAVES)
+                       generateCaves(stone_surface_max_y);
+
+               // Add mud to the central chunk
+               addMud();
+
+               // Flow mud away from steep edges
+               if (spflags & MGV6_MUDFLOW)
+                       flowMud(mudflow_minpos, mudflow_maxpos);
+
+       }
+
+       // Update heightmap after mudflow
+       updateHeightmap(node_min, node_max);
+
+       // Add dungeons
+       if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) {
+               DungeonParams dp;
+
+               dp.seed             = seed;
+               dp.c_water          = c_water_source;
+               dp.c_river_water    = c_water_source;
+
+               dp.only_in_ground   = true;
+               dp.corridor_len_min = 1;
+               dp.corridor_len_max = 13;
+               dp.rooms_min        = 2;
+               dp.rooms_max        = 16;
+               dp.y_min            = -MAX_MAP_GENERATION_LIMIT;
+               dp.y_max            = MAX_MAP_GENERATION_LIMIT;
+
+               dp.np_density
+                       = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0);
+               dp.np_alt_wall
+                       = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
+
+               if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
+                       dp.c_wall              = c_desert_stone;
+                       dp.c_alt_wall          = CONTENT_IGNORE;
+                       dp.c_stair             = c_stair_desert_stone;
+
+                       dp.diagonal_dirs       = true;
+                       dp.holesize            = v3s16(2, 3, 2);
+                       dp.room_size_min       = v3s16(6, 9, 6);
+                       dp.room_size_max       = v3s16(10, 11, 10);
+                       dp.room_size_large_min = v3s16(10, 13, 10);
+                       dp.room_size_large_max = v3s16(18, 21, 18);
+                       dp.notifytype          = GENNOTIFY_TEMPLE;
+               } else {
+                       dp.c_wall              = c_cobble;
+                       dp.c_alt_wall          = c_mossycobble;
+                       dp.c_stair             = c_stair_cobble;
+
+                       dp.diagonal_dirs       = false;
+                       dp.holesize            = v3s16(1, 2, 1);
+                       dp.room_size_min       = v3s16(4, 4, 4);
+                       dp.room_size_max       = v3s16(8, 6, 8);
+                       dp.room_size_large_min = v3s16(8, 8, 8);
+                       dp.room_size_large_max = v3s16(16, 16, 16);
+                       dp.notifytype          = GENNOTIFY_DUNGEON;
+               }
+
+               DungeonGen dgen(ndef, &gennotify, &dp);
+               dgen.generate(vm, blockseed, full_node_min, full_node_max);
+       }
+
+       // Add top and bottom side of water to transforming_liquid queue
+       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
+
+       // Add surface nodes
+       growGrass();
+
+       // Generate some trees, and add grass, if a jungle
+       if (spflags & MGV6_TREES)
+               placeTreesAndJungleGrass();
+
+       // Generate the registered decorations
+       if (flags & MG_DECORATIONS)
+               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+
+       // Generate the registered ores
+       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+
+       // Calculate lighting
+       if (flags & MG_LIGHT)
+               calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
+                       node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
+                       full_node_min, full_node_max);
+
+       this->generating = false;
+}
+
+
+void MapgenV6::calculateNoise()
+{
+       int x = node_min.X;
+       int z = node_min.Z;
+       int fx = full_node_min.X;
+       int fz = full_node_min.Z;
+
+       if (!(spflags & MGV6_FLAT)) {
+               noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
+               noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
+               noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
+               noise_height_select->perlinMap2D_PO(x, 0.5, z, 0.5);
+               noise_mud->perlinMap2D_PO(x, 0.5, z, 0.5);
+       }
+
+       noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
+
+       noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
+       noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
+       // Humidity map does not need range limiting 0 to 1,
+       // only humidity at point does
+}
+
+
+int MapgenV6::generateGround()
+{
+       //TimeTaker timer1("Generating ground level");
+       MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
+       MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
+       MapNode n_ice(c_ice);
+       int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+
+       u32 index = 0;
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+               // Surface height
+               s16 surface_y = (s16)baseTerrainLevelFromMap(index);
+
+               // Log it
+               if (surface_y > stone_surface_max_y)
+                       stone_surface_max_y = surface_y;
+
+               BiomeV6Type bt = getBiome(v2s16(x, z));
+
+               // Fill ground with stone
+               const v3s16 &em = vm->m_area.getExtent();
+               u32 i = vm->m_area.index(x, node_min.Y, z);
+               for (s16 y = node_min.Y; y <= node_max.Y; y++) {
+                       if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
+                               if (y <= surface_y) {
+                                       vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
+                                                       && bt == BT_DESERT) ?
+                                               n_desert_stone : n_stone;
+                               } else if (y <= water_level) {
+                                       vm->m_data[i] = (y >= MGV6_ICE_BASE
+                                                       && bt == BT_TUNDRA) ?
+                                               n_ice : n_water_source;
+                               } else {
+                                       vm->m_data[i] = n_air;
+                               }
+                       }
+                       vm->m_area.add_y(em, i, 1);
+               }
+       }
+
+       return stone_surface_max_y;
+}
+
+
+void MapgenV6::addMud()
+{
+       // 15ms @cs=8
+       //TimeTaker timer1("add mud");
+       MapNode n_dirt(c_dirt), n_gravel(c_gravel);
+       MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
+       MapNode addnode;
+
+       u32 index = 0;
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+               // Randomize mud amount
+               s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
+
+               // Find ground level
+               s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
+
+               // Handle area not found
+               if (surface_y == vm->m_area.MinEdge.Y - 1)
+                       continue;
+
+               BiomeV6Type bt = getBiome(v2s16(x, z));
+               addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
+
+               if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
+                       addnode = n_sand;
+               } else if (mud_add_amount <= 0) {
+                       mud_add_amount = 1 - mud_add_amount;
+                       addnode = n_gravel;
+               } else if (bt != BT_DESERT && getHaveBeach(index) &&
+                               surface_y + mud_add_amount <= water_level + 2) {
+                       addnode = n_sand;
+               }
+
+               if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
+                       mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
+
+               /* If topmost node is grass, change it to mud.  It might be if it was
+               // flown to there from a neighboring chunk and then converted.
+               u32 i = vm->m_area.index(x, surface_y, z);
+               if (vm->m_data[i].getContent() == c_dirt_with_grass)
+                       vm->m_data[i] = n_dirt;*/
+
+               // Add mud on ground
+               s16 mudcount = 0;
+               const v3s16 &em = vm->m_area.getExtent();
+               s16 y_start = surface_y + 1;
+               u32 i = vm->m_area.index(x, y_start, z);
+               for (s16 y = y_start; y <= node_max.Y; y++) {
+                       if (mudcount >= mud_add_amount)
+                               break;
+
+                       vm->m_data[i] = addnode;
+                       mudcount++;
+
+                       vm->m_area.add_y(em, i, 1);
+               }
+       }
+}
+
+
+void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
+{
+       // 340ms @cs=8
+       //TimeTaker timer1("flow mud");
+
+       // Iterate a few times
+       for (s16 k = 0; k < 3; k++) {
+               for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
+               for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
+                       // Invert coordinates every 2nd iteration
+                       if (k % 2 == 0) {
+                               x = mudflow_maxpos - (x - mudflow_minpos);
+                               z = mudflow_maxpos - (z - mudflow_minpos);
+                       }
+
+                       // Node position in 2d
+                       v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
+
+                       const v3s16 &em = vm->m_area.getExtent();
+                       u32 i = vm->m_area.index(p2d.X, node_max.Y, p2d.Y);
+                       s16 y = node_max.Y;
+
+                       while (y >= node_min.Y) {
+
+                       for (;; y--) {
+                               MapNode *n = NULL;
+                               // Find mud
+                               for (; y >= node_min.Y; y--) {
+                                       n = &vm->m_data[i];
+                                       if (n->getContent() == c_dirt ||
+                                                       n->getContent() == c_dirt_with_grass ||
+                                                       n->getContent() == c_gravel)
+                                               break;
+
+                                       vm->m_area.add_y(em, i, -1);
+                               }
+
+                               // Stop if out of area
+                               //if(vmanip.m_area.contains(i) == false)
+                               if (y < node_min.Y)
+                                       break;
+
+                               if (n->getContent() == c_dirt ||
+                                               n->getContent() == c_dirt_with_grass) {
+                                       // Make it exactly mud
+                                       n->setContent(c_dirt);
+
+                                       // Don't flow it if the stuff under it is not mud
+                                       {
+                                               u32 i2 = i;
+                                               vm->m_area.add_y(em, i2, -1);
+                                               // Cancel if out of area
+                                               if (!vm->m_area.contains(i2))
+                                                       continue;
+                                               MapNode *n2 = &vm->m_data[i2];
+                                               if (n2->getContent() != c_dirt &&
+                                                               n2->getContent() != c_dirt_with_grass)
+                                                       continue;
+                                       }
+                               }
+
+                               v3s16 dirs4[4] = {
+                                       v3s16(0, 0, 1), // back
+                                       v3s16(1, 0, 0), // right
+                                       v3s16(0, 0, -1), // front
+                                       v3s16(-1, 0, 0), // left
+                               };
+
+                               // Check that upper is walkable. Cancel
+                               // dropping if upper keeps it in place.
+                               u32 i3 = i;
+                               vm->m_area.add_y(em, i3, 1);
+                               MapNode *n3 = NULL;
+
+                               if (vm->m_area.contains(i3)) {
+                                       n3 = &vm->m_data[i3];
+                                       if (ndef->get(*n3).walkable)
+                                               continue;
+                               }
+
+                               // Drop mud on side
+                               for (const v3s16 &dirp : dirs4) {
+                                       u32 i2 = i;
+                                       // Move to side
+                                       vm->m_area.add_p(em, i2, dirp);
+                                       // Fail if out of area
+                                       if (!vm->m_area.contains(i2))
+                                               continue;
+                                       // Check that side is air
+                                       MapNode *n2 = &vm->m_data[i2];
+                                       if (ndef->get(*n2).walkable)
+                                               continue;
+                                       // Check that under side is air
+                                       vm->m_area.add_y(em, i2, -1);
+                                       if (!vm->m_area.contains(i2))
+                                               continue;
+                                       n2 = &vm->m_data[i2];
+                                       if (ndef->get(*n2).walkable)
+                                               continue;
+                                       // Loop further down until not air
+                                       bool dropped_to_unknown = false;
+                                       do {
+                                               vm->m_area.add_y(em, i2, -1);
+                                               n2 = &vm->m_data[i2];
+                                               // if out of known area
+                                               if (!vm->m_area.contains(i2) ||
+                                                               n2->getContent() == CONTENT_IGNORE) {
+                                                       dropped_to_unknown = true;
+                                                       break;
+                                               }
+                                       } while (!ndef->get(*n2).walkable);
+                                       // Loop one up so that we're in air
+                                       vm->m_area.add_y(em, i2, 1);
+
+                                       // Move mud to new place. Outside mapchunk remove
+                                       // any decorations above removed or placed mud.
+                                       if (!dropped_to_unknown)
+                                               moveMud(i, i2, i3, p2d, em);
+
+                                       // Done
+                                       break;
+                               }
+                       }
+                       }
+               }
+       }
+}
+
+
+void MapgenV6::moveMud(u32 remove_index, u32 place_index,
+       u32 above_remove_index, v2s16 pos, v3s16 em)
+{
+       MapNode n_air(CONTENT_AIR);
+       // Copy mud from old place to new place
+       vm->m_data[place_index] = vm->m_data[remove_index];
+       // Set old place to be air
+       vm->m_data[remove_index] = n_air;
+       // Outside the mapchunk decorations may need to be removed if above removed
+       // mud or if half-buried in placed mud. Placed mud is to the side of pos so
+       // use 'pos.X >= node_max.X' etc.
+       if (pos.X >= node_max.X || pos.X <= node_min.X ||
+                       pos.Y >= node_max.Z || pos.Y <= node_min.Z) {
+               // 'above remove' node is above removed mud. If it is not air, water or
+               // 'ignore' it is a decoration that needs removing. Also search upwards
+               // to remove a possible stacked decoration.
+               // Check for 'ignore' because stacked decorations can penetrate into
+               // 'ignore' nodes above the mapchunk.
+               while (vm->m_area.contains(above_remove_index) &&
+                               vm->m_data[above_remove_index].getContent() != CONTENT_AIR &&
+                               vm->m_data[above_remove_index].getContent() != c_water_source &&
+                               vm->m_data[above_remove_index].getContent() != CONTENT_IGNORE) {
+                       vm->m_data[above_remove_index] = n_air;
+                       vm->m_area.add_y(em, above_remove_index, 1);
+               }
+               // Mud placed may have partially-buried a stacked decoration, search
+               // above and remove.
+               vm->m_area.add_y(em, place_index, 1);
+               while (vm->m_area.contains(place_index) &&
+                               vm->m_data[place_index].getContent() != CONTENT_AIR &&
+                               vm->m_data[place_index].getContent() != c_water_source &&
+                               vm->m_data[place_index].getContent() != CONTENT_IGNORE) {
+                       vm->m_data[place_index] = n_air;
+                       vm->m_area.add_y(em, place_index, 1);
+               }
+       }
+}
+
+
+void MapgenV6::placeTreesAndJungleGrass()
+{
+       //TimeTaker t("placeTrees");
+       if (node_max.Y < water_level)
+               return;
+
+       PseudoRandom grassrandom(blockseed + 53);
+       content_t c_junglegrass = ndef->getId("mapgen_junglegrass");
+       // if we don't have junglegrass, don't place cignore... that's bad
+       if (c_junglegrass == CONTENT_IGNORE)
+               c_junglegrass = CONTENT_AIR;
+       MapNode n_junglegrass(c_junglegrass);
+       const v3s16 &em = vm->m_area.getExtent();
+
+       // Divide area into parts
+       s16 div = 8;
+       s16 sidelen = central_area_size.X / div;
+       double area = sidelen * sidelen;
+
+       // N.B.  We must add jungle grass first, since tree leaves will
+       // obstruct the ground, giving us a false ground level
+       for (s16 z0 = 0; z0 < div; z0++)
+       for (s16 x0 = 0; x0 < div; x0++) {
+               // Center position of part of division
+               v2s16 p2d_center(
+                       node_min.X + sidelen / 2 + sidelen * x0,
+                       node_min.Z + sidelen / 2 + sidelen * z0
+               );
+               // Minimum edge of part of division
+               v2s16 p2d_min(
+                       node_min.X + sidelen * x0,
+                       node_min.Z + sidelen * z0
+               );
+               // Maximum edge of part of division
+               v2s16 p2d_max(
+                       node_min.X + sidelen + sidelen * x0 - 1,
+                       node_min.Z + sidelen + sidelen * z0 - 1
+               );
+
+               // Get biome at center position of part of division
+               BiomeV6Type bt = getBiome(p2d_center);
+
+               // Amount of trees
+               u32 tree_count;
+               if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
+                       tree_count = area * getTreeAmount(p2d_center);
+                       if (bt == BT_JUNGLE)
+                               tree_count *= 4;
+               } else {
+                       tree_count = 0;
+               }
+
+               // Add jungle grass
+               if (bt == BT_JUNGLE) {
+                       float humidity = getHumidity(p2d_center);
+                       u32 grass_count = 5 * humidity * tree_count;
+                       for (u32 i = 0; i < grass_count; i++) {
+                               s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
+                               s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
+                               int mapindex = central_area_size.X * (z - node_min.Z)
+                                                               + (x - node_min.X);
+                               s16 y = heightmap[mapindex];
+                               if (y < water_level)
+                                       continue;
+
+                               u32 vi = vm->m_area.index(x, y, z);
+                               // place on dirt_with_grass, since we know it is exposed to sunlight
+                               if (vm->m_data[vi].getContent() == c_dirt_with_grass) {
+                                       vm->m_area.add_y(em, vi, 1);
+                                       vm->m_data[vi] = n_junglegrass;
+                               }
+                       }
+               }
+
+               // Put trees in random places on part of division
+               for (u32 i = 0; i < tree_count; i++) {
+                       s16 x = myrand_range(p2d_min.X, p2d_max.X);
+                       s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
+                       int mapindex = central_area_size.X * (z - node_min.Z)
+                                                       + (x - node_min.X);
+                       s16 y = heightmap[mapindex];
+                       // Don't make a tree under water level
+                       // Don't make a tree so high that it doesn't fit
+                       if (y < water_level || y > node_max.Y - 6)
+                               continue;
+
+                       v3s16 p(x, y, z);
+                       // Trees grow only on mud and grass
+                       {
+                               u32 i = vm->m_area.index(p);
+                               content_t c = vm->m_data[i].getContent();
+                               if (c != c_dirt &&
+                                               c != c_dirt_with_grass &&
+                                               c != c_dirt_with_snow)
+                                       continue;
+                       }
+                       p.Y++;
+
+                       // Make a tree
+                       if (bt == BT_JUNGLE) {
+                               treegen::make_jungletree(*vm, p, ndef, myrand());
+                       } else if (bt == BT_TAIGA) {
+                               treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand());
+                       } else if (bt == BT_NORMAL) {
+                               bool is_apple_tree = (myrand_range(0, 3) == 0) &&
+                                                       getHaveAppleTree(v2s16(x, z));
+                               treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
+                       }
+               }
+       }
+       //printf("placeTreesAndJungleGrass: %dms\n", t.stop());
+}
+
+
+void MapgenV6::growGrass() // Add surface nodes
+{
+       MapNode n_dirt_with_grass(c_dirt_with_grass);
+       MapNode n_dirt_with_snow(c_dirt_with_snow);
+       MapNode n_snowblock(c_snowblock);
+       MapNode n_snow(c_snow);
+       const v3s16 &em = vm->m_area.getExtent();
+
+       u32 index = 0;
+       for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
+       for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) {
+               // Find the lowest surface to which enough light ends up to make
+               // grass grow.  Basically just wait until not air and not leaves.
+               s16 surface_y = 0;
+               {
+                       u32 i = vm->m_area.index(x, node_max.Y, z);
+                       s16 y;
+                       // Go to ground level
+                       for (y = node_max.Y; y >= full_node_min.Y; y--) {
+                               MapNode &n = vm->m_data[i];
+                               if (ndef->get(n).param_type != CPT_LIGHT ||
+                                               ndef->get(n).liquid_type != LIQUID_NONE ||
+                                               n.getContent() == c_ice)
+                                       break;
+                               vm->m_area.add_y(em, i, -1);
+                       }
+                       surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
+               }
+
+               BiomeV6Type bt = getBiome(index, v2s16(x, z));
+               u32 i = vm->m_area.index(x, surface_y, z);
+               content_t c = vm->m_data[i].getContent();
+               if (surface_y >= water_level - 20) {
+                       if (bt == BT_TAIGA && c == c_dirt) {
+                               vm->m_data[i] = n_dirt_with_snow;
+                       } else if (bt == BT_TUNDRA) {
+                               if (c == c_dirt) {
+                                       vm->m_data[i] = n_snowblock;
+                                       vm->m_area.add_y(em, i, -1);
+                                       vm->m_data[i] = n_dirt_with_snow;
+                               } else if (c == c_stone && surface_y < node_max.Y) {
+                                       vm->m_area.add_y(em, i, 1);
+                                       vm->m_data[i] = n_snowblock;
+                               }
+                       } else if (c == c_dirt) {
+                               vm->m_data[i] = n_dirt_with_grass;
+                       }
+               }
+       }
+}
+
+
+void MapgenV6::generateCaves(int max_stone_y)
+{
+       float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed);
+       int volume_nodes = (node_max.X - node_min.X + 1) *
+                                          (node_max.Y - node_min.Y + 1) * MAP_BLOCKSIZE;
+       cave_amount = MYMAX(0.0, cave_amount);
+       u32 caves_count = cave_amount * volume_nodes / 50000;
+       u32 bruises_count = 1;
+       PseudoRandom ps(blockseed + 21343);
+       PseudoRandom ps2(blockseed + 1032);
+
+       if (ps.range(1, 6) == 1)
+               bruises_count = ps.range(0, ps.range(0, 2));
+
+       if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
+               caves_count   /= 3;
+               bruises_count /= 3;
+       }
+
+       for (u32 i = 0; i < caves_count + bruises_count; i++) {
+               CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source);
+
+               bool large_cave = (i >= caves_count);
+               cave.makeCave(vm, node_min, node_max, &ps, &ps2,
+                       large_cave, max_stone_y, heightmap);
+       }
+}
diff --git a/src/mapgen/mapgen_v6.h b/src/mapgen/mapgen_v6.h
new file mode 100644 (file)
index 0000000..9c87942
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+Minetest
+Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "mapgen.h"
+#include "noise.h"
+
+#define MGV6_AVERAGE_MUD_AMOUNT 4
+#define MGV6_DESERT_STONE_BASE -32
+#define MGV6_ICE_BASE 0
+#define MGV6_FREQ_HOT 0.4
+#define MGV6_FREQ_SNOW -0.4
+#define MGV6_FREQ_TAIGA 0.5
+#define MGV6_FREQ_JUNGLE 0.5
+
+//////////// Mapgen V6 flags
+#define MGV6_JUNGLES    0x01
+#define MGV6_BIOMEBLEND 0x02
+#define MGV6_MUDFLOW    0x04
+#define MGV6_SNOWBIOMES 0x08
+#define MGV6_FLAT       0x10
+#define MGV6_TREES      0x20
+
+
+extern FlagDesc flagdesc_mapgen_v6[];
+
+
+enum BiomeV6Type
+{
+       BT_NORMAL,
+       BT_DESERT,
+       BT_JUNGLE,
+       BT_TUNDRA,
+       BT_TAIGA,
+};
+
+
+struct MapgenV6Params : public MapgenParams {
+       u32 spflags = MGV6_JUNGLES | MGV6_SNOWBIOMES | MGV6_TREES |
+               MGV6_BIOMEBLEND | MGV6_MUDFLOW;
+       float freq_desert = 0.45f;
+       float freq_beach = 0.15f;
+       NoiseParams np_terrain_base;
+       NoiseParams np_terrain_higher;
+       NoiseParams np_steepness;
+       NoiseParams np_height_select;
+       NoiseParams np_mud;
+       NoiseParams np_beach;
+       NoiseParams np_biome;
+       NoiseParams np_cave;
+       NoiseParams np_humidity;
+       NoiseParams np_trees;
+       NoiseParams np_apple_trees;
+
+       MapgenV6Params();
+       ~MapgenV6Params() = default;
+
+       void readParams(const Settings *settings);
+       void writeParams(Settings *settings) const;
+};
+
+
+class MapgenV6 : public Mapgen {
+public:
+       EmergeManager *m_emerge;
+
+       int ystride;
+       u32 spflags;
+
+       v3s16 node_min;
+       v3s16 node_max;
+       v3s16 full_node_min;
+       v3s16 full_node_max;
+       v3s16 central_area_size;
+
+       Noise *noise_terrain_base;
+       Noise *noise_terrain_higher;
+       Noise *noise_steepness;
+       Noise *noise_height_select;
+       Noise *noise_mud;
+       Noise *noise_beach;
+       Noise *noise_biome;
+       Noise *noise_humidity;
+       NoiseParams *np_cave;
+       NoiseParams *np_humidity;
+       NoiseParams *np_trees;
+       NoiseParams *np_apple_trees;
+       float freq_desert;
+       float freq_beach;
+
+       content_t c_stone;
+       content_t c_dirt;
+       content_t c_dirt_with_grass;
+       content_t c_sand;
+       content_t c_water_source;
+       content_t c_lava_source;
+       content_t c_gravel;
+       content_t c_desert_stone;
+       content_t c_desert_sand;
+       content_t c_dirt_with_snow;
+       content_t c_snow;
+       content_t c_snowblock;
+       content_t c_ice;
+
+       content_t c_cobble;
+       content_t c_mossycobble;
+       content_t c_stair_cobble;
+       content_t c_stair_desert_stone;
+
+       MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge);
+       ~MapgenV6();
+
+       virtual MapgenType getType() const { return MAPGEN_V6; }
+
+       void makeChunk(BlockMakeData *data);
+       int getGroundLevelAtPoint(v2s16 p);
+       int getSpawnLevelAtPoint(v2s16 p);
+
+       float baseTerrainLevel(float terrain_base, float terrain_higher,
+               float steepness, float height_select);
+       virtual float baseTerrainLevelFromNoise(v2s16 p);
+       virtual float baseTerrainLevelFromMap(v2s16 p);
+       virtual float baseTerrainLevelFromMap(int index);
+
+       s16 find_stone_level(v2s16 p2d);
+       bool block_is_underground(u64 seed, v3s16 blockpos);
+       s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision);
+
+       float getHumidity(v2s16 p);
+       float getTreeAmount(v2s16 p);
+       bool getHaveAppleTree(v2s16 p);
+       float getMudAmount(v2s16 p);
+       virtual float getMudAmount(int index);
+       bool getHaveBeach(v2s16 p);
+       bool getHaveBeach(int index);
+       BiomeV6Type getBiome(v2s16 p);
+       BiomeV6Type getBiome(int index, v2s16 p);
+
+       u32 get_blockseed(u64 seed, v3s16 p);
+
+       virtual void calculateNoise();
+       int generateGround();
+       void addMud();
+       void flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos);
+       void moveMud(u32 remove_index, u32 place_index,
+               u32 above_remove_index, v2s16 pos, v3s16 em);
+       void growGrass();
+       void placeTreesAndJungleGrass();
+       virtual void generateCaves(int max_stone_y);
+};
diff --git a/src/mapgen/mapgen_v7.cpp b/src/mapgen/mapgen_v7.cpp
new file mode 100644 (file)
index 0000000..4047801
--- /dev/null
@@ -0,0 +1,743 @@
+/*
+Minetest
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "content_sao.h"
+#include "nodedef.h"
+#include "voxelalgorithms.h"
+//#include "profiler.h" // For TimeTaker
+#include "settings.h" // For g_settings
+#include "emerge.h"
+#include "dungeongen.h"
+#include "cavegen.h"
+#include "mg_biome.h"
+#include "mg_ore.h"
+#include "mg_decoration.h"
+#include "mapgen_v7.h"
+
+
+FlagDesc flagdesc_mapgen_v7[] = {
+       {"mountains",   MGV7_MOUNTAINS},
+       {"ridges",      MGV7_RIDGES},
+       {"floatlands",  MGV7_FLOATLANDS},
+       {"caverns",     MGV7_CAVERNS},
+       {NULL,          0}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+MapgenV7::MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge)
+       : MapgenBasic(mapgenid, params, emerge)
+{
+       spflags             = params->spflags;
+       mount_zero_level    = params->mount_zero_level;
+       cave_width          = params->cave_width;
+       large_cave_depth    = params->large_cave_depth;
+       lava_depth          = params->lava_depth;
+       float_mount_density = params->float_mount_density;
+       floatland_level     = params->floatland_level;
+       shadow_limit        = params->shadow_limit;
+       cavern_limit        = params->cavern_limit;
+       cavern_taper        = params->cavern_taper;
+       cavern_threshold    = params->cavern_threshold;
+
+       // This is to avoid a divide-by-zero.
+       // Parameter will be saved to map_meta.txt in limited form.
+       params->float_mount_height = MYMAX(params->float_mount_height, 1.0f);
+       float_mount_height   = params->float_mount_height;
+
+       // 2D noise
+       noise_terrain_base    = new Noise(&params->np_terrain_base,    seed, csize.X, csize.Z);
+       noise_terrain_alt     = new Noise(&params->np_terrain_alt,     seed, csize.X, csize.Z);
+       noise_terrain_persist = new Noise(&params->np_terrain_persist, seed, csize.X, csize.Z);
+       noise_height_select   = new Noise(&params->np_height_select,   seed, csize.X, csize.Z);
+       noise_filler_depth    = new Noise(&params->np_filler_depth,    seed, csize.X, csize.Z);
+
+       if (spflags & MGV7_MOUNTAINS)
+               noise_mount_height = new Noise(&params->np_mount_height, seed, csize.X, csize.Z);
+
+       if (spflags & MGV7_FLOATLANDS) {
+               noise_floatland_base    = new Noise(&params->np_floatland_base,    seed, csize.X, csize.Z);
+               noise_float_base_height = new Noise(&params->np_float_base_height, seed, csize.X, csize.Z);
+       }
+
+       if (spflags & MGV7_RIDGES) {
+               noise_ridge_uwater = new Noise(&params->np_ridge_uwater, seed, csize.X, csize.Z);
+       // 3D noise, 1-up 1-down overgeneration
+               noise_ridge = new Noise(&params->np_ridge, seed, csize.X, csize.Y + 2, csize.Z);
+       }
+       // 3D noise, 1 up, 1 down overgeneration
+       if ((spflags & MGV7_MOUNTAINS) || (spflags & MGV7_FLOATLANDS))
+               noise_mountain = new Noise(&params->np_mountain, seed, csize.X, csize.Y + 2, csize.Z);
+       // 3D noise, 1 down overgeneration
+       MapgenBasic::np_cave1  = params->np_cave1;
+       MapgenBasic::np_cave2  = params->np_cave2;
+       MapgenBasic::np_cavern = params->np_cavern;
+}
+
+
+MapgenV7::~MapgenV7()
+{
+       delete noise_terrain_base;
+       delete noise_terrain_alt;
+       delete noise_terrain_persist;
+       delete noise_height_select;
+       delete noise_filler_depth;
+
+       if (spflags & MGV7_MOUNTAINS)
+               delete noise_mount_height;
+
+       if (spflags & MGV7_FLOATLANDS) {
+               delete noise_floatland_base;
+               delete noise_float_base_height;
+       }
+
+       if (spflags & MGV7_RIDGES) {
+               delete noise_ridge_uwater;
+               delete noise_ridge;
+       }
+
+       if ((spflags & MGV7_MOUNTAINS) || (spflags & MGV7_FLOATLANDS))
+               delete noise_mountain;
+}
+
+
+MapgenV7Params::MapgenV7Params():
+       np_terrain_base      (4,    70,   v3f(600,  600,  600),  82341, 5, 0.6,  2.0),
+       np_terrain_alt       (4,    25,   v3f(600,  600,  600),  5934,  5, 0.6,  2.0),
+       np_terrain_persist   (0.6,  0.1,  v3f(2000, 2000, 2000), 539,   3, 0.6,  2.0),
+       np_height_select     (-8,   16,   v3f(500,  500,  500),  4213,  6, 0.7,  2.0),
+       np_filler_depth      (0,    1.2,  v3f(150,  150,  150),  261,   3, 0.7,  2.0),
+       np_mount_height      (256,  112,  v3f(1000, 1000, 1000), 72449, 3, 0.6,  2.0),
+       np_ridge_uwater      (0,    1,    v3f(1000, 1000, 1000), 85039, 5, 0.6,  2.0),
+       np_floatland_base    (-0.6, 1.5,  v3f(600,  600,  600),  114,   5, 0.6,  2.0),
+       np_float_base_height (48,   24,   v3f(300,  300,  300),  907,   4, 0.7,  2.0),
+       np_mountain          (-0.6, 1,    v3f(250,  350,  250),  5333,  5, 0.63, 2.0),
+       np_ridge             (0,    1,    v3f(100,  100,  100),  6467,  4, 0.75, 2.0),
+       np_cavern            (0,    1,    v3f(384,  128,  384),  723,   5, 0.63, 2.0),
+       np_cave1             (0,    12,   v3f(61,   61,   61),   52534, 3, 0.5,  2.0),
+       np_cave2             (0,    12,   v3f(67,   67,   67),   10325, 3, 0.5,  2.0)
+{
+}
+
+
+void MapgenV7Params::readParams(const Settings *settings)
+{
+       settings->getFlagStrNoEx("mgv7_spflags",           spflags, flagdesc_mapgen_v7);
+       settings->getS16NoEx("mgv7_mount_zero_level",      mount_zero_level);
+       settings->getFloatNoEx("mgv7_cave_width",          cave_width);
+       settings->getS16NoEx("mgv7_large_cave_depth",      large_cave_depth);
+       settings->getS16NoEx("mgv7_lava_depth",            lava_depth);
+       settings->getFloatNoEx("mgv7_float_mount_density", float_mount_density);
+       settings->getFloatNoEx("mgv7_float_mount_height",  float_mount_height);
+       settings->getS16NoEx("mgv7_floatland_level",       floatland_level);
+       settings->getS16NoEx("mgv7_shadow_limit",          shadow_limit);
+       settings->getS16NoEx("mgv7_cavern_limit",          cavern_limit);
+       settings->getS16NoEx("mgv7_cavern_taper",          cavern_taper);
+       settings->getFloatNoEx("mgv7_cavern_threshold",    cavern_threshold);
+
+       settings->getNoiseParams("mgv7_np_terrain_base",      np_terrain_base);
+       settings->getNoiseParams("mgv7_np_terrain_alt",       np_terrain_alt);
+       settings->getNoiseParams("mgv7_np_terrain_persist",   np_terrain_persist);
+       settings->getNoiseParams("mgv7_np_height_select",     np_height_select);
+       settings->getNoiseParams("mgv7_np_filler_depth",      np_filler_depth);
+       settings->getNoiseParams("mgv7_np_mount_height",      np_mount_height);
+       settings->getNoiseParams("mgv7_np_ridge_uwater",      np_ridge_uwater);
+       settings->getNoiseParams("mgv7_np_floatland_base",    np_floatland_base);
+       settings->getNoiseParams("mgv7_np_float_base_height", np_float_base_height);
+       settings->getNoiseParams("mgv7_np_mountain",          np_mountain);
+       settings->getNoiseParams("mgv7_np_ridge",             np_ridge);
+       settings->getNoiseParams("mgv7_np_cavern",            np_cavern);
+       settings->getNoiseParams("mgv7_np_cave1",             np_cave1);
+       settings->getNoiseParams("mgv7_np_cave2",             np_cave2);
+}
+
+
+void MapgenV7Params::writeParams(Settings *settings) const
+{
+       settings->setFlagStr("mgv7_spflags",           spflags, flagdesc_mapgen_v7, U32_MAX);
+       settings->setS16("mgv7_mount_zero_level",      mount_zero_level);
+       settings->setFloat("mgv7_cave_width",          cave_width);
+       settings->setS16("mgv7_large_cave_depth",      large_cave_depth);
+       settings->setS16("mgv7_lava_depth",            lava_depth);
+       settings->setFloat("mgv7_float_mount_density", float_mount_density);
+       settings->setFloat("mgv7_float_mount_height",  float_mount_height);
+       settings->setS16("mgv7_floatland_level",       floatland_level);
+       settings->setS16("mgv7_shadow_limit",          shadow_limit);
+       settings->setS16("mgv7_cavern_limit",          cavern_limit);
+       settings->setS16("mgv7_cavern_taper",          cavern_taper);
+       settings->setFloat("mgv7_cavern_threshold",    cavern_threshold);
+
+       settings->setNoiseParams("mgv7_np_terrain_base",      np_terrain_base);
+       settings->setNoiseParams("mgv7_np_terrain_alt",       np_terrain_alt);
+       settings->setNoiseParams("mgv7_np_terrain_persist",   np_terrain_persist);
+       settings->setNoiseParams("mgv7_np_height_select",     np_height_select);
+       settings->setNoiseParams("mgv7_np_filler_depth",      np_filler_depth);
+       settings->setNoiseParams("mgv7_np_mount_height",      np_mount_height);
+       settings->setNoiseParams("mgv7_np_ridge_uwater",      np_ridge_uwater);
+       settings->setNoiseParams("mgv7_np_floatland_base",    np_floatland_base);
+       settings->setNoiseParams("mgv7_np_float_base_height", np_float_base_height);
+       settings->setNoiseParams("mgv7_np_mountain",          np_mountain);
+       settings->setNoiseParams("mgv7_np_ridge",             np_ridge);
+       settings->setNoiseParams("mgv7_np_cavern",            np_cavern);
+       settings->setNoiseParams("mgv7_np_cave1",             np_cave1);
+       settings->setNoiseParams("mgv7_np_cave2",             np_cave2);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+int MapgenV7::getSpawnLevelAtPoint(v2s16 p)
+{
+       // If rivers are enabled, first check if in a river
+       if (spflags & MGV7_RIDGES) {
+               float width = 0.2;
+               float uwatern = NoisePerlin2D(&noise_ridge_uwater->np, p.X, p.Y, seed) * 2;
+               if (fabs(uwatern) <= width)
+                       return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
+       }
+
+       // Terrain noise 'offset' is the average level of that terrain.
+       // At least 50% of terrain will be below the higher of base and alt terrain
+       // 'offset's.
+       // Raising the maximum spawn level above 'water_level + 16' is necessary
+       // for when terrain 'offset's are set much higher than water_level.
+       s16 max_spawn_y = MYMAX(MYMAX(noise_terrain_alt->np.offset,
+                       noise_terrain_base->np.offset),
+                       water_level + 16);
+       // Base terrain calculation
+       s16 y = baseTerrainLevelAtPoint(p.X, p.Y);
+
+       // If mountains are disabled, terrain level is base terrain level.
+       // Avoids mid-air spawn where mountain terrain would have been.
+       if (!(spflags & MGV7_MOUNTAINS)) {
+               if (y < water_level || y > max_spawn_y)
+                       return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
+
+               // y + 2 because y is surface level and due to biome 'dust'
+               return y + 2;
+       }
+
+       // Search upwards for first node without mountain terrain
+       int iters = 256;
+       while (iters > 0 && y <= max_spawn_y) {
+               if (!getMountainTerrainAtPoint(p.X, y + 1, p.Y)) {
+                       if (y <= water_level || y > max_spawn_y)
+                               return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
+
+                       // y + 1 due to biome 'dust'
+                       return y + 1;
+               }
+               y++;
+               iters--;
+       }
+
+       // Unsuitable spawn point
+       return MAX_MAP_GENERATION_LIMIT;
+}
+
+
+void MapgenV7::makeChunk(BlockMakeData *data)
+{
+       // Pre-conditions
+       assert(data->vmanip);
+       assert(data->nodedef);
+       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
+               data->blockpos_requested.Y >= data->blockpos_min.Y &&
+               data->blockpos_requested.Z >= data->blockpos_min.Z);
+       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
+               data->blockpos_requested.Y <= data->blockpos_max.Y &&
+               data->blockpos_requested.Z <= data->blockpos_max.Z);
+
+       this->generating = true;
+       this->vm   = data->vmanip;
+       this->ndef = data->nodedef;
+       //TimeTaker t("makeChunk");
+
+       v3s16 blockpos_min = data->blockpos_min;
+       v3s16 blockpos_max = data->blockpos_max;
+       node_min = blockpos_min * MAP_BLOCKSIZE;
+       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
+       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+
+       blockseed = getBlockSeed2(full_node_min, seed);
+
+       // Generate base and mountain terrain
+       // An initial heightmap is no longer created here for use in generateRidgeTerrain()
+       s16 stone_surface_max_y = generateTerrain();
+
+       // Generate rivers
+       if (spflags & MGV7_RIDGES)
+               generateRidgeTerrain();
+
+       // Create heightmap
+       updateHeightmap(node_min, node_max);
+
+       // Init biome generator, place biome-specific nodes, and build biomemap
+       biomegen->calcBiomeNoise(node_min);
+
+       MgStoneType mgstone_type;
+       content_t biome_stone;
+       generateBiomes(&mgstone_type, &biome_stone);
+
+       // Generate caverns, tunnels and classic caves
+       if (flags & MG_CAVES) {
+               bool near_cavern = false;
+               // Generate caverns
+               if (spflags & MGV7_CAVERNS)
+                       near_cavern = generateCaverns(stone_surface_max_y);
+               // Generate tunnels and classic caves
+               if (near_cavern)
+                       // Disable classic caves in this mapchunk by setting
+                       // 'large cave depth' to world base. Avoids excessive liquid in
+                       // large caverns and floating blobs of overgenerated liquid.
+                       generateCaves(stone_surface_max_y, -MAX_MAP_GENERATION_LIMIT);
+               else
+                       generateCaves(stone_surface_max_y, large_cave_depth);
+       }
+
+       // Generate dungeons
+       if (flags & MG_DUNGEONS)
+               generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
+
+       // Generate the registered decorations
+       if (flags & MG_DECORATIONS)
+               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+
+       // Generate the registered ores
+       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+
+       // Sprinkle some dust on top after everything else was generated
+       dustTopNodes();
+
+       //printf("makeChunk: %dms\n", t.stop());
+
+       // Update liquids
+       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
+
+       // Calculate lighting
+       // Limit floatland shadow
+       bool propagate_shadow = !((spflags & MGV7_FLOATLANDS) &&
+               node_min.Y <= shadow_limit && node_max.Y >= shadow_limit);
+
+       if (flags & MG_LIGHT)
+               calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
+                       full_node_min, full_node_max, propagate_shadow);
+
+       //setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE,
+       //                      node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF);
+
+       this->generating = false;
+}
+
+
+float MapgenV7::baseTerrainLevelAtPoint(s16 x, s16 z)
+{
+       float hselect = NoisePerlin2D(&noise_height_select->np, x, z, seed);
+       hselect = rangelim(hselect, 0.0, 1.0);
+
+       float persist = NoisePerlin2D(&noise_terrain_persist->np, x, z, seed);
+
+       noise_terrain_base->np.persist = persist;
+       float height_base = NoisePerlin2D(&noise_terrain_base->np, x, z, seed);
+
+       noise_terrain_alt->np.persist = persist;
+       float height_alt = NoisePerlin2D(&noise_terrain_alt->np, x, z, seed);
+
+       if (height_alt > height_base)
+               return height_alt;
+
+       return (height_base * hselect) + (height_alt * (1.0 - hselect));
+}
+
+
+float MapgenV7::baseTerrainLevelFromMap(int index)
+{
+       float hselect     = rangelim(noise_height_select->result[index], 0.0, 1.0);
+       float height_base = noise_terrain_base->result[index];
+       float height_alt  = noise_terrain_alt->result[index];
+
+       if (height_alt > height_base)
+               return height_alt;
+
+       return (height_base * hselect) + (height_alt * (1.0 - hselect));
+}
+
+
+bool MapgenV7::getMountainTerrainAtPoint(s16 x, s16 y, s16 z)
+{
+       float mnt_h_n =
+                       MYMAX(NoisePerlin2D(&noise_mount_height->np, x, z, seed), 1.0f);
+       float density_gradient = -((float)(y - mount_zero_level) / mnt_h_n);
+       float mnt_n = NoisePerlin3D(&noise_mountain->np, x, y, z, seed);
+
+       return mnt_n + density_gradient >= 0.0;
+}
+
+
+bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y)
+{
+       float mounthn = MYMAX(noise_mount_height->result[idx_xz], 1.0f);
+       float density_gradient = -((float)(y - mount_zero_level) / mounthn);
+       float mountn = noise_mountain->result[idx_xyz];
+
+       return mountn + density_gradient >= 0.0;
+}
+
+
+bool MapgenV7::getFloatlandMountainFromMap(int idx_xyz, int idx_xz, s16 y)
+{
+       // Make rim 2 nodes thick to match floatland base terrain
+       float density_gradient = (y >= floatland_level) ?
+               -pow((float)(y - floatland_level) / float_mount_height, 0.75f) :
+               -pow((float)(floatland_level - 1 - y) / float_mount_height, 0.75f);
+
+       float floatn = noise_mountain->result[idx_xyz] + float_mount_density;
+
+       return floatn + density_gradient >= 0.0f;
+}
+
+
+void MapgenV7::floatBaseExtentFromMap(s16 *float_base_min, s16 *float_base_max, int idx_xz)
+{
+       // '+1' to avoid a layer of stone at y = MAX_MAP_GENERATION_LIMIT
+       s16 base_min = MAX_MAP_GENERATION_LIMIT + 1;
+       s16 base_max = MAX_MAP_GENERATION_LIMIT;
+
+       float n_base = noise_floatland_base->result[idx_xz];
+       if (n_base > 0.0f) {
+               float n_base_height =
+                               MYMAX(noise_float_base_height->result[idx_xz], 1.0f);
+               float amp = n_base * n_base_height;
+               float ridge = n_base_height / 3.0f;
+               base_min = floatland_level - amp / 1.5f;
+
+               if (amp > ridge * 2.0f) {
+                       // Lake bed
+                       base_max = floatland_level - (amp - ridge * 2.0f) / 2.0f;
+               } else {
+                       // Hills and ridges
+                       float diff = fabs(amp - ridge) / ridge;
+                       // Smooth ridges using the 'smoothstep function'
+                       float smooth_diff = diff * diff * (3.0f - 2.0f * diff);
+                       base_max = floatland_level + ridge - smooth_diff * ridge;
+               }
+       }
+
+       *float_base_min = base_min;
+       *float_base_max = base_max;
+}
+
+
+int MapgenV7::generateTerrain()
+{
+       MapNode n_air(CONTENT_AIR);
+       MapNode n_stone(c_stone);
+       MapNode n_water(c_water_source);
+
+       //// Calculate noise for terrain generation
+       noise_terrain_persist->perlinMap2D(node_min.X, node_min.Z);
+       float *persistmap = noise_terrain_persist->result;
+
+       noise_terrain_base->perlinMap2D(node_min.X, node_min.Z, persistmap);
+       noise_terrain_alt->perlinMap2D(node_min.X, node_min.Z, persistmap);
+       noise_height_select->perlinMap2D(node_min.X, node_min.Z);
+
+       if ((spflags & MGV7_MOUNTAINS) || (spflags & MGV7_FLOATLANDS)) {
+               noise_mountain->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+       }
+
+       if (spflags & MGV7_MOUNTAINS) {
+               noise_mount_height->perlinMap2D(node_min.X, node_min.Z);
+       }
+
+       if (spflags & MGV7_FLOATLANDS) {
+               noise_floatland_base->perlinMap2D(node_min.X, node_min.Z);
+               noise_float_base_height->perlinMap2D(node_min.X, node_min.Z);
+       }
+
+       //// Place nodes
+       const v3s16 &em = vm->m_area.getExtent();
+       s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+       u32 index2d = 0;
+
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) {
+               s16 surface_y = baseTerrainLevelFromMap(index2d);
+               if (surface_y > stone_surface_max_y)
+                       stone_surface_max_y = surface_y;
+
+               // Get extent of floatland base terrain
+               // '+1' to avoid a layer of stone at y = MAX_MAP_GENERATION_LIMIT
+               s16 float_base_min = MAX_MAP_GENERATION_LIMIT + 1;
+               s16 float_base_max = MAX_MAP_GENERATION_LIMIT;
+               if (spflags & MGV7_FLOATLANDS)
+                       floatBaseExtentFromMap(&float_base_min, &float_base_max, index2d);
+
+               u32 vi = vm->m_area.index(x, node_min.Y - 1, z);
+               u32 index3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X);
+
+               for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
+                       if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
+                               if (y <= surface_y) {
+                                       vm->m_data[vi] = n_stone;  // Base terrain
+                               } else if ((spflags & MGV7_MOUNTAINS) &&
+                                               getMountainTerrainFromMap(index3d, index2d, y)) {
+                                       vm->m_data[vi] = n_stone;  // Mountain terrain
+                                       if (y > stone_surface_max_y)
+                                               stone_surface_max_y = y;
+                               } else if ((spflags & MGV7_FLOATLANDS) &&
+                                               ((y >= float_base_min && y <= float_base_max) ||
+                                               getFloatlandMountainFromMap(index3d, index2d, y))) {
+                                       vm->m_data[vi] = n_stone;  // Floatland terrain
+                                       stone_surface_max_y = node_max.Y;
+                               } else if (y <= water_level) {
+                                       vm->m_data[vi] = n_water;  // Ground level water
+                               } else if ((spflags & MGV7_FLOATLANDS) &&
+                                               (y >= float_base_max && y <= floatland_level)) {
+                                       vm->m_data[vi] = n_water;  // Floatland water
+                               } else {
+                                       vm->m_data[vi] = n_air;
+                               }
+                       }
+                       vm->m_area.add_y(em, vi, 1);
+                       index3d += ystride;
+               }
+       }
+
+       return stone_surface_max_y;
+}
+
+
+void MapgenV7::generateRidgeTerrain()
+{
+       if (node_max.Y < water_level - 16 ||
+                       ((spflags & MGV7_FLOATLANDS) && node_max.Y > shadow_limit))
+               return;
+
+       noise_ridge->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+       noise_ridge_uwater->perlinMap2D(node_min.X, node_min.Z);
+
+       MapNode n_water(c_water_source);
+       MapNode n_air(CONTENT_AIR);
+       u32 index = 0;
+       float width = 0.2;
+
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
+               u32 vi = vm->m_area.index(node_min.X, y, z);
+               for (s16 x = node_min.X; x <= node_max.X; x++, index++, vi++) {
+                       int j = (z - node_min.Z) * csize.X + (x - node_min.X);
+
+                       float uwatern = noise_ridge_uwater->result[j] * 2;
+                       if (fabs(uwatern) > width)
+                               continue;
+
+                       float altitude = y - water_level;
+                       float height_mod = (altitude + 17) / 2.5;
+                       float width_mod  = width - fabs(uwatern);
+                       float nridge = noise_ridge->result[index] * MYMAX(altitude, 0) / 7.0;
+
+                       if (nridge + width_mod * height_mod < 0.6)
+                               continue;
+
+                       vm->m_data[vi] = (y > water_level) ? n_air : n_water;
+               }
+       }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//// Code Boneyard
+////
+//// Much of the stuff here has potential to become useful again at some point
+//// in the future, but we don't want it to get lost or forgotten in version
+//// control.
+////
+
+#if 0
+int MapgenV7::generateMountainTerrain(s16 ymax)
+{
+       MapNode n_stone(c_stone);
+       u32 j = 0;
+
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
+               u32 vi = vm->m_area.index(node_min.X, y, z);
+               for (s16 x = node_min.X; x <= node_max.X; x++) {
+                       int index = (z - node_min.Z) * csize.X + (x - node_min.X);
+                       content_t c = vm->m_data[vi].getContent();
+
+                       if (getMountainTerrainFromMap(j, index, y)
+                                       && (c == CONTENT_AIR || c == c_water_source)) {
+                               vm->m_data[vi] = n_stone;
+                               if (y > ymax)
+                                       ymax = y;
+                       }
+
+                       vi++;
+                       j++;
+               }
+       }
+
+       return ymax;
+}
+#endif
+
+
+#if 0
+void MapgenV7::carveRivers() {
+       MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
+       MapNode n_stone(c_stone);
+       u32 index = 0;
+
+       int river_depth = 4;
+
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+               float terrain_mod  = noise_terrain_mod->result[index];
+               NoiseParams *np = noise_terrain_river->np;
+               np.persist = noise_terrain_persist->result[index];
+               float terrain_river = NoisePerlin2DNoTxfm(np, x, z, seed);
+               float height = terrain_river * (1 - abs(terrain_mod)) *
+                                               noise_terrain_river->np.scale;
+               height = log(height * height); //log(h^3) is pretty interesting for terrain
+
+               s16 y = heightmap[index];
+               if (height < 1.0 && y > river_depth &&
+                       y - river_depth >= node_min.Y && y <= node_max.Y) {
+
+                       for (s16 ry = y; ry != y - river_depth; ry--) {
+                               u32 vi = vm->m_area.index(x, ry, z);
+                               vm->m_data[vi] = n_air;
+                       }
+
+                       u32 vi = vm->m_area.index(x, y - river_depth, z);
+                       vm->m_data[vi] = n_water_source;
+               }
+       }
+}
+#endif
+
+
+#if 0
+void MapgenV7::addTopNodes()
+{
+       v3s16 em = vm->m_area.getExtent();
+       s16 ntopnodes;
+       u32 index = 0;
+
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
+               Biome *biome = bmgr->biomes[biomemap[index]];
+
+               //////////////////// First, add top nodes below the ridge
+               s16 y = ridge_heightmap[index];
+
+               // This cutoff is good enough, but not perfect.
+               // It will cut off potentially placed top nodes at chunk boundaries
+               if (y < node_min.Y)
+                       continue;
+               if (y > node_max.Y) {
+                       y = node_max.Y; // Let's see if we can still go downward anyway
+                       u32 vi = vm->m_area.index(x, y, z);
+                       content_t c = vm->m_data[vi].getContent();
+                       if (ndef->get(c).walkable)
+                               continue;
+               }
+
+               // N.B.  It is necessary to search downward since ridge_heightmap[i]
+               // might not be the actual height, just the lowest part in the chunk
+               // where a ridge had been carved
+               u32 i = vm->m_area.index(x, y, z);
+               for (; y >= node_min.Y; y--) {
+                       content_t c = vm->m_data[i].getContent();
+                       if (ndef->get(c).walkable)
+                               break;
+                       vm->m_area.add_y(em, i, -1);
+               }
+
+               if (y != node_min.Y - 1 && y >= water_level) {
+                       ridge_heightmap[index] = y; //update ridgeheight
+                       ntopnodes = biome->top_depth;
+                       for (; y <= node_max.Y && ntopnodes; y++) {
+                               ntopnodes--;
+                               vm->m_data[i] = MapNode(biome->c_top);
+                               vm->m_area.add_y(em, i, 1);
+                       }
+                       // If dirt, grow grass on it.
+                       if (y > water_level - 10 &&
+                               vm->m_data[i].getContent() == CONTENT_AIR) {
+                               vm->m_area.add_y(em, i, -1);
+                               if (vm->m_data[i].getContent() == c_dirt)
+                                       vm->m_data[i] = MapNode(c_dirt_with_grass);
+                       }
+               }
+
+               //////////////////// Now, add top nodes on top of the ridge
+               y = heightmap[index];
+               if (y > node_max.Y) {
+                       y = node_max.Y; // Let's see if we can still go downward anyway
+                       u32 vi = vm->m_area.index(x, y, z);
+                       content_t c = vm->m_data[vi].getContent();
+                       if (ndef->get(c).walkable)
+                               continue;
+               }
+
+               i = vm->m_area.index(x, y, z);
+               for (; y >= node_min.Y; y--) {
+                       content_t c = vm->m_data[i].getContent();
+                       if (ndef->get(c).walkable)
+                               break;
+                       vm->m_area.add_y(em, i, -1);
+               }
+
+               if (y != node_min.Y - 1) {
+                       ntopnodes = biome->top_depth;
+                       // Let's see if we've already added it...
+                       if (y == ridge_heightmap[index] + ntopnodes - 1)
+                               continue;
+
+                       for (; y <= node_max.Y && ntopnodes; y++) {
+                               ntopnodes--;
+                               vm->m_data[i] = MapNode(biome->c_top);
+                               vm->m_area.add_y(em, i, 1);
+                       }
+                       // If dirt, grow grass on it.
+                       if (y > water_level - 10 &&
+                               vm->m_data[i].getContent() == CONTENT_AIR) {
+                               vm->m_area.add_y(em, i, -1);
+                               if (vm->m_data[i].getContent() == c_dirt)
+                                       vm->m_data[i] = MapNode(c_dirt_with_grass);
+                       }
+               }
+       }
+}
+#endif
diff --git a/src/mapgen/mapgen_v7.h b/src/mapgen/mapgen_v7.h
new file mode 100644 (file)
index 0000000..6fb7dc4
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+Minetest
+Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "mapgen.h"
+
+///////////// Mapgen V7 flags
+#define MGV7_MOUNTAINS   0x01
+#define MGV7_RIDGES      0x02
+#define MGV7_FLOATLANDS  0x04
+#define MGV7_CAVERNS     0x08
+#define MGV7_BIOMEREPEAT 0x10 // Now unused
+
+class BiomeManager;
+
+extern FlagDesc flagdesc_mapgen_v7[];
+
+
+struct MapgenV7Params : public MapgenParams {
+       u32 spflags = MGV7_MOUNTAINS | MGV7_RIDGES | MGV7_CAVERNS;
+       s16 mount_zero_level = 0;
+       float cave_width = 0.09f;
+       s16 large_cave_depth = -33;
+       s16 lava_depth = -256;
+       float float_mount_density = 0.6f;
+       float float_mount_height = 128.0f;
+       s16 floatland_level = 1280;
+       s16 shadow_limit = 1024;
+       s16 cavern_limit = -256;
+       s16 cavern_taper = 256;
+       float cavern_threshold = 0.7f;
+
+       NoiseParams np_terrain_base;
+       NoiseParams np_terrain_alt;
+       NoiseParams np_terrain_persist;
+       NoiseParams np_height_select;
+       NoiseParams np_filler_depth;
+       NoiseParams np_mount_height;
+       NoiseParams np_ridge_uwater;
+       NoiseParams np_floatland_base;
+       NoiseParams np_float_base_height;
+       NoiseParams np_mountain;
+       NoiseParams np_ridge;
+       NoiseParams np_cavern;
+       NoiseParams np_cave1;
+       NoiseParams np_cave2;
+
+       MapgenV7Params();
+       ~MapgenV7Params() = default;
+
+       void readParams(const Settings *settings);
+       void writeParams(Settings *settings) const;
+};
+
+class MapgenV7 : public MapgenBasic {
+public:
+       MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge);
+       ~MapgenV7();
+
+       virtual MapgenType getType() const { return MAPGEN_V7; }
+
+       virtual void makeChunk(BlockMakeData *data);
+       int getSpawnLevelAtPoint(v2s16 p);
+
+       float baseTerrainLevelAtPoint(s16 x, s16 z);
+       float baseTerrainLevelFromMap(int index);
+       bool getMountainTerrainAtPoint(s16 x, s16 y, s16 z);
+       bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y);
+       bool getFloatlandMountainFromMap(int idx_xyz, int idx_xz, s16 y);
+       void floatBaseExtentFromMap(s16 *float_base_min, s16 *float_base_max, int idx_xz);
+
+       int generateTerrain();
+       void generateRidgeTerrain();
+
+private:
+       s16 mount_zero_level;
+       s16 large_cave_depth;
+       float float_mount_density;
+       float float_mount_height;
+       s16 floatland_level;
+       s16 shadow_limit;
+
+       Noise *noise_terrain_base;
+       Noise *noise_terrain_alt;
+       Noise *noise_terrain_persist;
+       Noise *noise_height_select;
+       Noise *noise_mount_height;
+       Noise *noise_ridge_uwater;
+       Noise *noise_floatland_base;
+       Noise *noise_float_base_height;
+       Noise *noise_mountain;
+       Noise *noise_ridge;
+};
diff --git a/src/mapgen/mapgen_valleys.cpp b/src/mapgen/mapgen_valleys.cpp
new file mode 100644 (file)
index 0000000..a13bb45
--- /dev/null
@@ -0,0 +1,743 @@
+/*
+Minetest Valleys C
+Copyright (C) 2016-2017 Duane Robertson <duane@duanerobertson.com>
+Copyright (C) 2016-2017 paramat
+
+Based on Valleys Mapgen by Gael de Sailly
+ (https://forum.minetest.net/viewtopic.php?f=9&t=11430)
+and mapgen_v7, mapgen_flat by kwolekr and paramat.
+
+Licensing changed by permission of Gael de Sailly.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "mapgen.h"
+#include "voxel.h"
+#include "noise.h"
+#include "mapblock.h"
+#include "mapnode.h"
+#include "map.h"
+#include "nodedef.h"
+#include "voxelalgorithms.h"
+#include "settings.h" // For g_settings
+#include "emerge.h"
+#include "dungeongen.h"
+#include "mg_biome.h"
+#include "mg_ore.h"
+#include "mg_decoration.h"
+#include "mapgen_valleys.h"
+#include "cavegen.h"
+
+
+//#undef NDEBUG
+//#include "assert.h"
+
+//#include "util/timetaker.h"
+//#include "profiler.h"
+
+
+//static Profiler mapgen_prof;
+//Profiler *mapgen_profiler = &mapgen_prof;
+
+static FlagDesc flagdesc_mapgen_valleys[] = {
+       {"altitude_chill", MGVALLEYS_ALT_CHILL},
+       {"humid_rivers",   MGVALLEYS_HUMID_RIVERS},
+       {NULL,             0}
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+MapgenValleys::MapgenValleys(int mapgenid, MapgenValleysParams *params, EmergeManager *emerge)
+       : MapgenBasic(mapgenid, params, emerge)
+{
+       // NOTE: MapgenValleys has a hard dependency on BiomeGenOriginal
+       m_bgen = (BiomeGenOriginal *)biomegen;
+
+       BiomeParamsOriginal *bp = (BiomeParamsOriginal *)params->bparams;
+
+       spflags            = params->spflags;
+       altitude_chill     = params->altitude_chill;
+       large_cave_depth   = params->large_cave_depth;
+       lava_features_lim  = rangelim(params->lava_features, 0, 10);
+       massive_cave_depth = params->massive_cave_depth;
+       river_depth_bed    = params->river_depth + 1.f;
+       river_size_factor  = params->river_size / 100.f;
+       water_features_lim = rangelim(params->water_features, 0, 10);
+       cave_width         = params->cave_width;
+
+       //// 2D Terrain noise
+       noise_filler_depth       = new Noise(&params->np_filler_depth,       seed, csize.X, csize.Z);
+       noise_inter_valley_slope = new Noise(&params->np_inter_valley_slope, seed, csize.X, csize.Z);
+       noise_rivers             = new Noise(&params->np_rivers,             seed, csize.X, csize.Z);
+       noise_terrain_height     = new Noise(&params->np_terrain_height,     seed, csize.X, csize.Z);
+       noise_valley_depth       = new Noise(&params->np_valley_depth,       seed, csize.X, csize.Z);
+       noise_valley_profile     = new Noise(&params->np_valley_profile,     seed, csize.X, csize.Z);
+
+       //// 3D Terrain noise
+       // 1-up 1-down overgeneration
+       noise_inter_valley_fill = new Noise(&params->np_inter_valley_fill, seed, csize.X, csize.Y + 2, csize.Z);
+       // 1-down overgeneraion
+       noise_cave1             = new Noise(&params->np_cave1,             seed, csize.X, csize.Y + 1, csize.Z);
+       noise_cave2             = new Noise(&params->np_cave2,             seed, csize.X, csize.Y + 1, csize.Z);
+       noise_massive_caves     = new Noise(&params->np_massive_caves,     seed, csize.X, csize.Y + 1, csize.Z);
+
+       humid_rivers       = (spflags & MGVALLEYS_HUMID_RIVERS);
+       use_altitude_chill = (spflags & MGVALLEYS_ALT_CHILL);
+       humidity_adjust    = bp->np_humidity.offset - 50.f;
+
+       // a small chance of overflows if the settings are very high
+       cave_water_max_height = water_level + MYMAX(0, water_features_lim - 4) * 50;
+       lava_max_height       = water_level + MYMAX(0, lava_features_lim - 4) * 50;
+
+       tcave_cache = new float[csize.Y + 2];
+}
+
+
+MapgenValleys::~MapgenValleys()
+{
+       delete noise_cave1;
+       delete noise_cave2;
+       delete noise_filler_depth;
+       delete noise_inter_valley_fill;
+       delete noise_inter_valley_slope;
+       delete noise_rivers;
+       delete noise_massive_caves;
+       delete noise_terrain_height;
+       delete noise_valley_depth;
+       delete noise_valley_profile;
+
+       delete[] tcave_cache;
+}
+
+
+MapgenValleysParams::MapgenValleysParams():
+       np_cave1              (0,     12,   v3f(61,   61,   61),   52534, 3, 0.5,   2.0),
+       np_cave2              (0,     12,   v3f(67,   67,   67),   10325, 3, 0.5,   2.0),
+       np_filler_depth       (0.f,   1.2f, v3f(256,  256,  256),  1605,  3, 0.5f,  2.f),
+       np_inter_valley_fill  (0.f,   1.f,  v3f(256,  512,  256),  1993,  6, 0.8f,  2.f),
+       np_inter_valley_slope (0.5f,  0.5f, v3f(128,  128,  128),  746,   1, 1.f,   2.f),
+       np_rivers             (0.f,   1.f,  v3f(256,  256,  256),  -6050, 5, 0.6f,  2.f),
+       np_massive_caves      (0.f,   1.f,  v3f(768,  256,  768),  59033, 6, 0.63f, 2.f),
+       np_terrain_height     (-10.f, 50.f, v3f(1024, 1024, 1024), 5202,  6, 0.4f,  2.f),
+       np_valley_depth       (5.f,   4.f,  v3f(512,  512,  512),  -1914, 1, 1.f,   2.f),
+       np_valley_profile     (0.6f,  0.5f, v3f(512,  512,  512),  777,   1, 1.f,   2.f)
+{
+}
+
+
+void MapgenValleysParams::readParams(const Settings *settings)
+{
+       settings->getFlagStrNoEx("mgvalleys_spflags",        spflags, flagdesc_mapgen_valleys);
+       settings->getU16NoEx("mgvalleys_altitude_chill",     altitude_chill);
+       settings->getS16NoEx("mgvalleys_large_cave_depth",   large_cave_depth);
+       settings->getU16NoEx("mgvalleys_lava_features",      lava_features);
+       settings->getS16NoEx("mgvalleys_massive_cave_depth", massive_cave_depth);
+       settings->getU16NoEx("mgvalleys_river_depth",        river_depth);
+       settings->getU16NoEx("mgvalleys_river_size",         river_size);
+       settings->getU16NoEx("mgvalleys_water_features",     water_features);
+       settings->getFloatNoEx("mgvalleys_cave_width",       cave_width);
+
+       settings->getNoiseParams("mgvalleys_np_cave1",              np_cave1);
+       settings->getNoiseParams("mgvalleys_np_cave2",              np_cave2);
+       settings->getNoiseParams("mgvalleys_np_filler_depth",       np_filler_depth);
+       settings->getNoiseParams("mgvalleys_np_inter_valley_fill",  np_inter_valley_fill);
+       settings->getNoiseParams("mgvalleys_np_inter_valley_slope", np_inter_valley_slope);
+       settings->getNoiseParams("mgvalleys_np_rivers",             np_rivers);
+       settings->getNoiseParams("mgvalleys_np_massive_caves",      np_massive_caves);
+       settings->getNoiseParams("mgvalleys_np_terrain_height",     np_terrain_height);
+       settings->getNoiseParams("mgvalleys_np_valley_depth",       np_valley_depth);
+       settings->getNoiseParams("mgvalleys_np_valley_profile",     np_valley_profile);
+}
+
+
+void MapgenValleysParams::writeParams(Settings *settings) const
+{
+       settings->setFlagStr("mgvalleys_spflags",        spflags, flagdesc_mapgen_valleys, U32_MAX);
+       settings->setU16("mgvalleys_altitude_chill",     altitude_chill);
+       settings->setS16("mgvalleys_large_cave_depth",   large_cave_depth);
+       settings->setU16("mgvalleys_lava_features",      lava_features);
+       settings->setS16("mgvalleys_massive_cave_depth", massive_cave_depth);
+       settings->setU16("mgvalleys_river_depth",        river_depth);
+       settings->setU16("mgvalleys_river_size",         river_size);
+       settings->setU16("mgvalleys_water_features",     water_features);
+       settings->setFloat("mgvalleys_cave_width",       cave_width);
+
+       settings->setNoiseParams("mgvalleys_np_cave1",              np_cave1);
+       settings->setNoiseParams("mgvalleys_np_cave2",              np_cave2);
+       settings->setNoiseParams("mgvalleys_np_filler_depth",       np_filler_depth);
+       settings->setNoiseParams("mgvalleys_np_inter_valley_fill",  np_inter_valley_fill);
+       settings->setNoiseParams("mgvalleys_np_inter_valley_slope", np_inter_valley_slope);
+       settings->setNoiseParams("mgvalleys_np_rivers",             np_rivers);
+       settings->setNoiseParams("mgvalleys_np_massive_caves",      np_massive_caves);
+       settings->setNoiseParams("mgvalleys_np_terrain_height",     np_terrain_height);
+       settings->setNoiseParams("mgvalleys_np_valley_depth",       np_valley_depth);
+       settings->setNoiseParams("mgvalleys_np_valley_profile",     np_valley_profile);
+}
+
+
+///////////////////////////////////////
+
+
+void MapgenValleys::makeChunk(BlockMakeData *data)
+{
+       // Pre-conditions
+       assert(data->vmanip);
+       assert(data->nodedef);
+       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
+               data->blockpos_requested.Y >= data->blockpos_min.Y &&
+               data->blockpos_requested.Z >= data->blockpos_min.Z);
+       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
+               data->blockpos_requested.Y <= data->blockpos_max.Y &&
+               data->blockpos_requested.Z <= data->blockpos_max.Z);
+
+       this->generating = true;
+       this->vm = data->vmanip;
+       this->ndef = data->nodedef;
+
+       //TimeTaker t("makeChunk");
+
+       v3s16 blockpos_min = data->blockpos_min;
+       v3s16 blockpos_max = data->blockpos_max;
+       node_min = blockpos_min * MAP_BLOCKSIZE;
+       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
+       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
+
+       blockseed = getBlockSeed2(full_node_min, seed);
+
+       // Generate biome noises.  Note this must be executed strictly before
+       // generateTerrain, because generateTerrain depends on intermediate
+       // biome-related noises.
+       m_bgen->calcBiomeNoise(node_min);
+
+       // Generate noise maps and base terrain height.
+       // Modify heat and humidity maps.
+       calculateNoise();
+
+       // Generate base terrain with initial heightmaps
+       s16 stone_surface_max_y = generateTerrain();
+
+       // Recalculate heightmap
+       updateHeightmap(node_min, node_max);
+
+       // Place biome-specific nodes and build biomemap
+       MgStoneType mgstone_type;
+       content_t biome_stone;
+       generateBiomes(&mgstone_type, &biome_stone);
+
+       // Cave creation.
+       if (flags & MG_CAVES)
+               generateCaves(stone_surface_max_y, large_cave_depth);
+
+       // Dungeon creation
+       if ((flags & MG_DUNGEONS) && node_max.Y < 50)
+               generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
+
+       // Generate the registered decorations
+       if (flags & MG_DECORATIONS)
+               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
+
+       // Generate the registered ores
+       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
+
+       // Sprinkle some dust on top after everything else was generated
+       dustTopNodes();
+
+       //TimeTaker tll("liquid_lighting");
+
+       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
+
+       if (flags & MG_LIGHT)
+               calcLighting(
+                               node_min - v3s16(0, 1, 0),
+                               node_max + v3s16(0, 1, 0),
+                               full_node_min,
+                               full_node_max);
+
+       //mapgen_profiler->avg("liquid_lighting", tll.stop() / 1000.f);
+       //mapgen_profiler->avg("makeChunk", t.stop() / 1000.f);
+
+       this->generating = false;
+}
+
+
+// Populate the noise tables and do most of the
+// calculation necessary to determine terrain height.
+void MapgenValleys::calculateNoise()
+{
+       //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO);
+
+       int x = node_min.X;
+       int y = node_min.Y - 1;
+       int z = node_min.Z;
+
+       //TimeTaker tcn("actualNoise");
+
+       noise_inter_valley_slope->perlinMap2D(x, z);
+       noise_rivers->perlinMap2D(x, z);
+       noise_terrain_height->perlinMap2D(x, z);
+       noise_valley_depth->perlinMap2D(x, z);
+       noise_valley_profile->perlinMap2D(x, z);
+
+       noise_inter_valley_fill->perlinMap3D(x, y, z);
+
+       //mapgen_profiler->avg("noisemaps", tcn.stop() / 1000.f);
+
+       float heat_offset = 0.f;
+       float humidity_scale = 1.f;
+
+       // Altitude chill tends to reduce the average heat.
+       if (use_altitude_chill)
+               heat_offset = 5.f;
+
+       // River humidity tends to increase the humidity range.
+       if (humid_rivers) {
+               humidity_scale = 0.8f;
+       }
+
+       for (s32 index = 0; index < csize.X * csize.Z; index++) {
+               m_bgen->heatmap[index] += heat_offset;
+               m_bgen->humidmap[index] *= humidity_scale;
+       }
+
+       TerrainNoise tn;
+
+       u32 index = 0;
+       for (tn.z = node_min.Z; tn.z <= node_max.Z; tn.z++)
+       for (tn.x = node_min.X; tn.x <= node_max.X; tn.x++, index++) {
+               // The parameters that we actually need to generate terrain
+               //  are passed by address (and the return value).
+               tn.terrain_height    = noise_terrain_height->result[index];
+               // River noise is replaced with base terrain, which
+               // is basically the height of the water table.
+               tn.rivers            = &noise_rivers->result[index];
+               // Valley depth noise is replaced with the valley
+               // number that represents the height of terrain
+               // over rivers and is used to determine about
+               // how close a river is for humidity calculation.
+               tn.valley            = &noise_valley_depth->result[index];
+               tn.valley_profile    = noise_valley_profile->result[index];
+               // Slope noise is replaced by the calculated slope
+               // which is used to get terrain height in the slow
+               // method, to create sharper mountains.
+               tn.slope             = &noise_inter_valley_slope->result[index];
+               tn.inter_valley_fill = noise_inter_valley_fill->result[index];
+
+               // This is the actual terrain height.
+               float mount = terrainLevelFromNoise(&tn);
+               noise_terrain_height->result[index] = mount;
+       }
+}
+
+
+// This keeps us from having to maintain two similar sets of
+//  complicated code to determine ground level.
+float MapgenValleys::terrainLevelFromNoise(TerrainNoise *tn)
+{
+       // The square function changes the behaviour of this noise:
+       //  very often small, and sometimes very high.
+       float valley_d = MYSQUARE(*tn->valley);
+
+       // valley_d is here because terrain is generally higher where valleys
+       //  are deep (mountains). base represents the height of the
+       //  rivers, most of the surface is above.
+       float base = tn->terrain_height + valley_d;
+
+       // "river" represents the distance from the river, in arbitrary units.
+       float river = fabs(*tn->rivers) - river_size_factor;
+
+       // Use the curve of the function 1-exp(-(x/a)^2) to model valleys.
+       //  Making "a" vary (0 < a <= 1) changes the shape of the valleys.
+       //  Try it with a geometry software !
+       //   (here x = "river" and a = valley_profile).
+       //  "valley" represents the height of the terrain, from the rivers.
+       {
+               float t = river / tn->valley_profile;
+               *tn->valley = valley_d * (1.f - exp(- MYSQUARE(t)));
+       }
+
+       // approximate height of the terrain at this point
+       float mount = base + *tn->valley;
+
+       *tn->slope *= *tn->valley;
+
+       // Rivers are placed where "river" is negative, so where the original
+       //  noise value is close to zero.
+       // Base ground is returned as rivers since it's basically the water table.
+       *tn->rivers = base;
+       if (river < 0.f) {
+               // Use the the function -sqrt(1-x^2) which models a circle.
+               float depth;
+               {
+                       float t = river / river_size_factor + 1;
+                       depth = (river_depth_bed * sqrt(MYMAX(0, 1.f - MYSQUARE(t))));
+               }
+
+               // base - depth : height of the bottom of the river
+               // water_level - 3 : don't make rivers below 3 nodes under the surface
+               // We use three because that's as low as the swamp biomes go.
+               // There is no logical equivalent to this using rangelim.
+               mount = MYMIN(MYMAX(base - depth, (float)(water_level - 3)), mount);
+
+               // Slope has no influence on rivers.
+               *tn->slope = 0.f;
+       }
+
+       return mount;
+}
+
+
+// This avoids duplicating the code in terrainLevelFromNoise, adding
+// only the final step of terrain generation without a noise map.
+float MapgenValleys::adjustedTerrainLevelFromNoise(TerrainNoise *tn)
+{
+       float mount = terrainLevelFromNoise(tn);
+       s16 y_start = myround(mount);
+
+       for (s16 y = y_start; y <= y_start + 1000; y++) {
+               float fill = NoisePerlin3D(&noise_inter_valley_fill->np, tn->x, y, tn->z, seed);
+
+               if (fill * *tn->slope < y - mount) {
+                       mount = MYMAX(y - 1, mount);
+                       break;
+               }
+       }
+
+       return mount;
+}
+
+
+int MapgenValleys::getSpawnLevelAtPoint(v2s16 p)
+{
+       // Check to make sure this isn't a request for a location in a river.
+       float rivers = NoisePerlin2D(&noise_rivers->np, p.X, p.Y, seed);
+       if (fabs(rivers) < river_size_factor)
+               return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
+
+       s16 level_at_point = terrainLevelAtPoint(p.X, p.Y);
+       if (level_at_point <= water_level ||
+                       level_at_point > water_level + 32)
+               return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
+
+       return level_at_point;
+}
+
+
+float MapgenValleys::terrainLevelAtPoint(s16 x, s16 z)
+{
+       TerrainNoise tn;
+
+       float rivers = NoisePerlin2D(&noise_rivers->np, x, z, seed);
+       float valley = NoisePerlin2D(&noise_valley_depth->np, x, z, seed);
+       float inter_valley_slope = NoisePerlin2D(&noise_inter_valley_slope->np, x, z, seed);
+
+       tn.x                 = x;
+       tn.z                 = z;
+       tn.terrain_height    = NoisePerlin2D(&noise_terrain_height->np, x, z, seed);
+       tn.rivers            = &rivers;
+       tn.valley            = &valley;
+       tn.valley_profile    = NoisePerlin2D(&noise_valley_profile->np, x, z, seed);
+       tn.slope             = &inter_valley_slope;
+       tn.inter_valley_fill = 0.f;
+
+       return adjustedTerrainLevelFromNoise(&tn);
+}
+
+
+int MapgenValleys::generateTerrain()
+{
+       // Raising this reduces the rate of evaporation.
+       static const float evaporation = 300.f;
+       // from the lua
+       static const float humidity_dropoff = 4.f;
+       // constant to convert altitude chill (compatible with lua) to heat
+       static const float alt_to_heat = 20.f;
+       // humidity reduction by altitude
+       static const float alt_to_humid = 10.f;
+
+       MapNode n_air(CONTENT_AIR);
+       MapNode n_river_water(c_river_water_source);
+       MapNode n_stone(c_stone);
+       MapNode n_water(c_water_source);
+
+       const v3s16 &em = vm->m_area.getExtent();
+       s16 surface_max_y = -MAX_MAP_GENERATION_LIMIT;
+       u32 index_2d = 0;
+
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
+               float river_y = noise_rivers->result[index_2d];
+               float surface_y = noise_terrain_height->result[index_2d];
+               float slope = noise_inter_valley_slope->result[index_2d];
+               float t_heat = m_bgen->heatmap[index_2d];
+
+               heightmap[index_2d] = -MAX_MAP_GENERATION_LIMIT;
+
+               if (surface_y > surface_max_y)
+                       surface_max_y = ceil(surface_y);
+
+               if (humid_rivers) {
+                       // Derive heat from (base) altitude. This will be most correct
+                       // at rivers, since other surface heights may vary below.
+                       if (use_altitude_chill && (surface_y > 0.f || river_y > 0.f))
+                               t_heat -= alt_to_heat * MYMAX(surface_y, river_y) / altitude_chill;
+
+                       // If humidity is low or heat is high, lower the water table.
+                       float delta = m_bgen->humidmap[index_2d] - 50.f;
+                       if (delta < 0.f) {
+                               float t_evap = (t_heat - 32.f) / evaporation;
+                               river_y += delta * MYMAX(t_evap, 0.08f);
+                       }
+               }
+
+               u32 index_3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X);
+               u32 index_data = vm->m_area.index(x, node_min.Y - 1, z);
+
+               // Mapgens concern themselves with stone and water.
+               for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
+                       if (vm->m_data[index_data].getContent() == CONTENT_IGNORE) {
+                               float fill = noise_inter_valley_fill->result[index_3d];
+                               float surface_delta = (float)y - surface_y;
+                               bool river = y + 1 < river_y;
+
+                               if (slope * fill > surface_delta) {
+                                       // ground
+                                       vm->m_data[index_data] = n_stone;
+                                       if (y > heightmap[index_2d])
+                                               heightmap[index_2d] = y;
+                                       if (y > surface_max_y)
+                                               surface_max_y = y;
+                               } else if (y <= water_level) {
+                                       // sea
+                                       vm->m_data[index_data] = n_water;
+                               } else if (river) {
+                                       // river
+                                       vm->m_data[index_data] = n_river_water;
+                               } else {  // air
+                                       vm->m_data[index_data] = n_air;
+                               }
+                       }
+
+                       vm->m_area.add_y(em, index_data, 1);
+                       index_3d += ystride;
+               }
+
+               if (heightmap[index_2d] == -MAX_MAP_GENERATION_LIMIT) {
+                       s16 surface_y_int = myround(surface_y);
+                       if (surface_y_int > node_max.Y + 1 || surface_y_int < node_min.Y - 1) {
+                               // If surface_y is outside the chunk, it's good enough.
+                               heightmap[index_2d] = surface_y_int;
+                       } else {
+                               // If the ground is outside of this chunk, but surface_y
+                               // is within the chunk, give a value outside.
+                               heightmap[index_2d] = node_min.Y - 2;
+                       }
+               }
+
+               if (humid_rivers) {
+                       // Use base ground (water table) in a riverbed, to
+                       // avoid an unnatural rise in humidity.
+                       float t_alt = MYMAX(noise_rivers->result[index_2d], (float)heightmap[index_2d]);
+                       float humid = m_bgen->humidmap[index_2d];
+                       float water_depth = (t_alt - river_y) / humidity_dropoff;
+                       humid *= 1.f + pow(0.5f, MYMAX(water_depth, 1.f));
+
+                       // Reduce humidity with altitude (ignoring riverbeds).
+                       // This is similar to the lua version's seawater adjustment,
+                       // but doesn't increase the base humidity, which causes
+                       // problems with the default biomes.
+                       if (t_alt > 0.f)
+                               humid -= alt_to_humid * t_alt / altitude_chill;
+
+                       m_bgen->humidmap[index_2d] = humid;
+               }
+
+               // Assign the heat adjusted by any changed altitudes.
+               // The altitude will change about half the time.
+               if (use_altitude_chill) {
+                       // ground height ignoring riverbeds
+                       float t_alt = MYMAX(noise_rivers->result[index_2d], (float)heightmap[index_2d]);
+                       if (humid_rivers && heightmap[index_2d] == (s16)myround(surface_y))
+                               // The altitude hasn't changed. Use the first result.
+                               m_bgen->heatmap[index_2d] = t_heat;
+                       else if (t_alt > 0.f)
+                               m_bgen->heatmap[index_2d] -= alt_to_heat * t_alt / altitude_chill;
+               }
+       }
+
+       return surface_max_y;
+}
+
+void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth)
+{
+       if (max_stone_y < node_min.Y)
+               return;
+
+       noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+       noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+
+       PseudoRandom ps(blockseed + 72202);
+
+       MapNode n_air(CONTENT_AIR);
+       MapNode n_lava(c_lava_source);
+       MapNode n_water(c_river_water_source);
+
+       const v3s16 &em = vm->m_area.getExtent();
+
+       // Cave blend distance near YMIN, YMAX
+       const float massive_cave_blend = 128.f;
+       // noise threshold for massive caves
+       const float massive_cave_threshold = 0.6f;
+       // mct: 1 = small rare caves, 0.5 1/3rd ground volume, 0 = 1/2 ground volume.
+
+       float yblmin = -mapgen_limit + massive_cave_blend * 1.5f;
+       float yblmax = massive_cave_depth - massive_cave_blend * 1.5f;
+       bool made_a_big_one = false;
+
+       // Cache the tcave values as they only vary by altitude.
+       if (node_max.Y <= massive_cave_depth) {
+               noise_massive_caves->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
+
+               for (s16 y = node_min.Y - 1; y <= node_max.Y; y++) {
+                       float tcave = massive_cave_threshold;
+
+                       if (y < yblmin) {
+                               float t = (yblmin - y) / massive_cave_blend;
+                               tcave += MYSQUARE(t);
+                       } else if (y > yblmax) {
+                               float t = (y - yblmax) / massive_cave_blend;
+                               tcave += MYSQUARE(t);
+                       }
+
+                       tcave_cache[y - node_min.Y + 1] = tcave;
+               }
+       }
+
+       // lava_depth varies between one and ten as you approach
+       //  the bottom of the world.
+       s16 lava_depth = ceil((lava_max_height - node_min.Y + 1) * 10.f / mapgen_limit);
+       // This allows random lava spawns to be less common at the surface.
+       s16 lava_chance = MYCUBE(lava_features_lim) * lava_depth;
+       // water_depth varies between ten and one on the way down.
+       s16 water_depth = ceil((mapgen_limit - abs(node_min.Y) + 1) * 10.f / mapgen_limit);
+       // This allows random water spawns to be more common at the surface.
+       s16 water_chance = MYCUBE(water_features_lim) * water_depth;
+
+       // Reduce the odds of overflows even further.
+       if (node_max.Y > water_level) {
+               lava_chance /= 3;
+               water_chance /= 3;
+       }
+
+       u32 index_2d = 0;
+       for (s16 z = node_min.Z; z <= node_max.Z; z++)
+       for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
+               Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index_2d]);
+               bool tunnel_air_above = false;
+               bool is_under_river = false;
+               bool underground = false;
+               u32 index_data = vm->m_area.index(x, node_max.Y, z);
+               u32 index_3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + (x - node_min.X);
+
+               // Dig caves on down loop to check for air above.
+               // Don't excavate the overgenerated stone at node_max.Y + 1,
+               // this creates a 'roof' over the tunnel, preventing light in
+               // tunnels at mapchunk borders when generating mapchunks upwards.
+               // This 'roof' is removed when the mapchunk above is generated.
+               for (s16 y = node_max.Y; y >= node_min.Y - 1; y--,
+                               index_3d -= ystride,
+                               vm->m_area.add_y(em, index_data, -1)) {
+
+                       float terrain = noise_terrain_height->result[index_2d];
+
+                       // Saves some time.
+                       if (y > terrain + 10)
+                               continue;
+
+                       if (y < terrain - 40)
+                               underground = true;
+
+                       // Dig massive caves.
+                       if (node_max.Y <= massive_cave_depth
+                                       && noise_massive_caves->result[index_3d]
+                                       > tcave_cache[y - node_min.Y + 1]) {
+                               vm->m_data[index_data] = n_air;
+                               made_a_big_one = true;
+                               continue;
+                       }
+
+                       content_t c = vm->m_data[index_data].getContent();
+                       // Detect river water to place riverbed nodes in tunnels
+                       if (c == biome->c_river_water)
+                               is_under_river = true;
+
+                       float d1 = contour(noise_cave1->result[index_3d]);
+                       float d2 = contour(noise_cave2->result[index_3d]);
+
+                       if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) {
+                               // in a tunnel
+                               vm->m_data[index_data] = n_air;
+                               tunnel_air_above = true;
+                       } else if (c == biome->c_filler || c == biome->c_stone) {
+                               if (tunnel_air_above) {
+                                       // at the tunnel floor
+                                       s16 sr = ps.range(0, 39);
+                                       u32 j = index_data;
+                                       vm->m_area.add_y(em, j, 1);
+
+                                       if (sr > terrain - y) {
+                                               // Put biome nodes in tunnels near the surface
+                                               if (is_under_river)
+                                                       vm->m_data[index_data] = MapNode(biome->c_riverbed);
+                                               else if (underground)
+                                                       vm->m_data[index_data] = MapNode(biome->c_filler);
+                                               else
+                                                       vm->m_data[index_data] = MapNode(biome->c_top);
+                                       } else if (sr < 3 && underground) {
+                                               sr = abs(ps.next());
+                                               if (lava_features_lim > 0 && y <= lava_max_height
+                                                               && c == biome->c_stone && sr < lava_chance)
+                                                       vm->m_data[j] = n_lava;
+
+                                               sr -= lava_chance;
+
+                                               // If sr < 0 then we should have already placed lava --
+                                               // don't immediately dump water on it.
+                                               if (water_features_lim > 0 && y <= cave_water_max_height
+                                                               && sr >= 0 && sr < water_chance)
+                                                       vm->m_data[j] = n_water;
+                                       }
+                               }
+
+                               tunnel_air_above = false;
+                               underground = true;
+                       } else {
+                               tunnel_air_above = false;
+                       }
+               }
+       }
+
+       if (node_max.Y <= large_cave_depth && !made_a_big_one) {
+               u32 bruises_count = ps.range(0, 2);
+               for (u32 i = 0; i < bruises_count; i++) {
+                       CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
+                               c_water_source, c_lava_source, lava_max_height);
+
+                       cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
+               }
+       }
+}
diff --git a/src/mapgen/mapgen_valleys.h b/src/mapgen/mapgen_valleys.h
new file mode 100644 (file)
index 0000000..7b5eb18
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+Minetest Valleys C
+Copyright (C) 2016-2017 Duane Robertson <duane@duanerobertson.com>
+Copyright (C) 2016-2017 paramat
+
+Based on Valleys Mapgen by Gael de Sailly
+ (https://forum.minetest.net/viewtopic.php?f=9&t=11430)
+and mapgen_v7 by kwolekr and paramat.
+
+Licensing changed by permission of Gael de Sailly.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "mapgen.h"
+
+////////////// Mapgen Valleys flags
+#define MGVALLEYS_ALT_CHILL    0x01
+#define MGVALLEYS_HUMID_RIVERS 0x02
+
+// Feed only one variable into these.
+#define MYSQUARE(x) (x) * (x)
+#define MYCUBE(x) (x) * (x) * (x)
+
+class BiomeManager;
+class BiomeGenOriginal;
+
+// Global profiler
+//class Profiler;
+//extern Profiler *mapgen_profiler;
+
+
+struct MapgenValleysParams : public MapgenParams {
+       u32 spflags = MGVALLEYS_HUMID_RIVERS | MGVALLEYS_ALT_CHILL;
+       s16 large_cave_depth = -33;
+       s16 massive_cave_depth = -256; // highest altitude of massive caves
+       u16 altitude_chill = 90; // The altitude at which temperature drops by 20C.
+       u16 lava_features = 0; // How often water will occur in caves.
+       u16 river_depth = 4; // How deep to carve river channels.
+       u16 river_size = 5; // How wide to make rivers.
+       u16 water_features = 0; // How often water will occur in caves.
+       float cave_width = 0.09f;
+       NoiseParams np_cave1;
+       NoiseParams np_cave2;
+       NoiseParams np_filler_depth;
+       NoiseParams np_inter_valley_fill;
+       NoiseParams np_inter_valley_slope;
+       NoiseParams np_rivers;
+       NoiseParams np_massive_caves;
+       NoiseParams np_terrain_height;
+       NoiseParams np_valley_depth;
+       NoiseParams np_valley_profile;
+
+       MapgenValleysParams();
+       ~MapgenValleysParams() = default;
+
+       void readParams(const Settings *settings);
+       void writeParams(Settings *settings) const;
+};
+
+struct TerrainNoise {
+       s16 x;
+       s16 z;
+       float terrain_height;
+       float *rivers;
+       float *valley;
+       float valley_profile;
+       float *slope;
+       float inter_valley_fill;
+};
+
+class MapgenValleys : public MapgenBasic {
+public:
+
+       MapgenValleys(int mapgenid, MapgenValleysParams *params, EmergeManager *emerge);
+       ~MapgenValleys();
+
+       virtual MapgenType getType() const { return MAPGEN_VALLEYS; }
+
+       virtual void makeChunk(BlockMakeData *data);
+       int getSpawnLevelAtPoint(v2s16 p);
+
+       s16 large_cave_depth;
+
+private:
+       BiomeGenOriginal *m_bgen;
+
+       bool humid_rivers;
+       bool use_altitude_chill;
+       float humidity_adjust;
+       s16 cave_water_max_height;
+       s16 lava_max_height;
+
+       float altitude_chill;
+       s16 lava_features_lim;
+       s16 massive_cave_depth;
+       float river_depth_bed;
+       float river_size_factor;
+       float *tcave_cache;
+       s16 water_features_lim;
+       Noise *noise_inter_valley_fill;
+       Noise *noise_inter_valley_slope;
+       Noise *noise_rivers;
+       Noise *noise_cave1;
+       Noise *noise_cave2;
+       Noise *noise_massive_caves;
+       Noise *noise_terrain_height;
+       Noise *noise_valley_depth;
+       Noise *noise_valley_profile;
+
+       float terrainLevelAtPoint(s16 x, s16 z);
+
+       void calculateNoise();
+
+       virtual int generateTerrain();
+       float terrainLevelFromNoise(TerrainNoise *tn);
+       float adjustedTerrainLevelFromNoise(TerrainNoise *tn);
+
+       virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth);
+};
diff --git a/src/mapgen/mg_biome.cpp b/src/mapgen/mg_biome.cpp
new file mode 100644 (file)
index 0000000..8dbb78e
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+Minetest
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "mg_biome.h"
+#include "mg_decoration.h"
+#include "emerge.h"
+#include "server.h"
+#include "nodedef.h"
+#include "map.h" //for MMVManip
+#include "util/numeric.h"
+#include "porting.h"
+#include "settings.h"
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+BiomeManager::BiomeManager(Server *server) :
+       ObjDefManager(server, OBJDEF_BIOME)
+{
+       m_server = server;
+
+       // Create default biome to be used in case none exist
+       Biome *b = new Biome;
+
+       b->name            = "Default";
+       b->flags           = 0;
+       b->depth_top       = 0;
+       b->depth_filler    = -MAX_MAP_GENERATION_LIMIT;
+       b->depth_water_top = 0;
+       b->depth_riverbed  = 0;
+       b->y_min           = -MAX_MAP_GENERATION_LIMIT;
+       b->y_max           = MAX_MAP_GENERATION_LIMIT;
+       b->heat_point      = 0.0;
+       b->humidity_point  = 0.0;
+
+       b->m_nodenames.emplace_back("mapgen_stone");
+       b->m_nodenames.emplace_back("mapgen_stone");
+       b->m_nodenames.emplace_back("mapgen_stone");
+       b->m_nodenames.emplace_back("mapgen_water_source");
+       b->m_nodenames.emplace_back("mapgen_water_source");
+       b->m_nodenames.emplace_back("mapgen_river_water_source");
+       b->m_nodenames.emplace_back("mapgen_stone");
+       b->m_nodenames.emplace_back("ignore");
+       m_ndef->pendNodeResolve(b);
+
+       add(b);
+}
+
+
+void BiomeManager::clear()
+{
+       EmergeManager *emerge = m_server->getEmergeManager();
+
+       // Remove all dangling references in Decorations
+       DecorationManager *decomgr = emerge->decomgr;
+       for (size_t i = 0; i != decomgr->getNumObjects(); i++) {
+               Decoration *deco = (Decoration *)decomgr->getRaw(i);
+               deco->biomes.clear();
+       }
+
+       // Don't delete the first biome
+       for (size_t i = 1; i < m_objects.size(); i++)
+               delete (Biome *)m_objects[i];
+
+       m_objects.resize(1);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+
+void BiomeParamsOriginal::readParams(const Settings *settings)
+{
+       settings->getNoiseParams("mg_biome_np_heat",           np_heat);
+       settings->getNoiseParams("mg_biome_np_heat_blend",     np_heat_blend);
+       settings->getNoiseParams("mg_biome_np_humidity",       np_humidity);
+       settings->getNoiseParams("mg_biome_np_humidity_blend", np_humidity_blend);
+}
+
+
+void BiomeParamsOriginal::writeParams(Settings *settings) const
+{
+       settings->setNoiseParams("mg_biome_np_heat",           np_heat);
+       settings->setNoiseParams("mg_biome_np_heat_blend",     np_heat_blend);
+       settings->setNoiseParams("mg_biome_np_humidity",       np_humidity);
+       settings->setNoiseParams("mg_biome_np_humidity_blend", np_humidity_blend);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+BiomeGenOriginal::BiomeGenOriginal(BiomeManager *biomemgr,
+       BiomeParamsOriginal *params, v3s16 chunksize)
+{
+       m_bmgr   = biomemgr;
+       m_params = params;
+       m_csize  = chunksize;
+
+       noise_heat           = new Noise(&params->np_heat,
+                                                                       params->seed, m_csize.X, m_csize.Z);
+       noise_humidity       = new Noise(&params->np_humidity,
+                                                                       params->seed, m_csize.X, m_csize.Z);
+       noise_heat_blend     = new Noise(&params->np_heat_blend,
+                                                                       params->seed, m_csize.X, m_csize.Z);
+       noise_humidity_blend = new Noise(&params->np_humidity_blend,
+                                                                       params->seed, m_csize.X, m_csize.Z);
+
+       heatmap  = noise_heat->result;
+       humidmap = noise_humidity->result;
+       biomemap = new biome_t[m_csize.X * m_csize.Z];
+}
+
+BiomeGenOriginal::~BiomeGenOriginal()
+{
+       delete []biomemap;
+
+       delete noise_heat;
+       delete noise_humidity;
+       delete noise_heat_blend;
+       delete noise_humidity_blend;
+}
+
+
+Biome *BiomeGenOriginal::calcBiomeAtPoint(v3s16 pos) const
+{
+       float heat =
+               NoisePerlin2D(&m_params->np_heat,       pos.X, pos.Z, m_params->seed) +
+               NoisePerlin2D(&m_params->np_heat_blend, pos.X, pos.Z, m_params->seed);
+       float humidity =
+               NoisePerlin2D(&m_params->np_humidity,       pos.X, pos.Z, m_params->seed) +
+               NoisePerlin2D(&m_params->np_humidity_blend, pos.X, pos.Z, m_params->seed);
+
+       return calcBiomeFromNoise(heat, humidity, pos.Y);
+}
+
+
+void BiomeGenOriginal::calcBiomeNoise(v3s16 pmin)
+{
+       m_pmin = pmin;
+
+       noise_heat->perlinMap2D(pmin.X, pmin.Z);
+       noise_humidity->perlinMap2D(pmin.X, pmin.Z);
+       noise_heat_blend->perlinMap2D(pmin.X, pmin.Z);
+       noise_humidity_blend->perlinMap2D(pmin.X, pmin.Z);
+
+       for (s32 i = 0; i < m_csize.X * m_csize.Z; i++) {
+               noise_heat->result[i]     += noise_heat_blend->result[i];
+               noise_humidity->result[i] += noise_humidity_blend->result[i];
+       }
+}
+
+
+biome_t *BiomeGenOriginal::getBiomes(s16 *heightmap)
+{
+       for (s32 i = 0; i != m_csize.X * m_csize.Z; i++) {
+               Biome *biome = calcBiomeFromNoise(
+                       noise_heat->result[i],
+                       noise_humidity->result[i],
+                       heightmap[i]);
+
+               biomemap[i] = biome->index;
+       }
+
+       return biomemap;
+}
+
+
+Biome *BiomeGenOriginal::getBiomeAtPoint(v3s16 pos) const
+{
+       return getBiomeAtIndex(
+               (pos.Z - m_pmin.Z) * m_csize.X + (pos.X - m_pmin.X),
+               pos.Y);
+}
+
+
+Biome *BiomeGenOriginal::getBiomeAtIndex(size_t index, s16 y) const
+{
+       return calcBiomeFromNoise(
+               noise_heat->result[index],
+               noise_humidity->result[index],
+               y);
+}
+
+
+Biome *BiomeGenOriginal::calcBiomeFromNoise(float heat, float humidity, s16 y) const
+{
+       Biome *b, *biome_closest = NULL;
+       float dist_min = FLT_MAX;
+
+       for (size_t i = 1; i < m_bmgr->getNumObjects(); i++) {
+               b = (Biome *)m_bmgr->getRaw(i);
+               if (!b || y > b->y_max || y < b->y_min)
+                       continue;
+
+               float d_heat     = heat     - b->heat_point;
+               float d_humidity = humidity - b->humidity_point;
+               float dist = (d_heat * d_heat) +
+                                        (d_humidity * d_humidity);
+               if (dist < dist_min) {
+                       dist_min = dist;
+                       biome_closest = b;
+               }
+       }
+
+       return biome_closest ? biome_closest : (Biome *)m_bmgr->getRaw(BIOME_NONE);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+void Biome::resolveNodeNames()
+{
+       getIdFromNrBacklog(&c_top,         "mapgen_stone",              CONTENT_AIR);
+       getIdFromNrBacklog(&c_filler,      "mapgen_stone",              CONTENT_AIR);
+       getIdFromNrBacklog(&c_stone,       "mapgen_stone",              CONTENT_AIR);
+       getIdFromNrBacklog(&c_water_top,   "mapgen_water_source",       CONTENT_AIR);
+       getIdFromNrBacklog(&c_water,       "mapgen_water_source",       CONTENT_AIR);
+       getIdFromNrBacklog(&c_river_water, "mapgen_river_water_source", CONTENT_AIR);
+       getIdFromNrBacklog(&c_riverbed,    "mapgen_stone",              CONTENT_AIR);
+       getIdFromNrBacklog(&c_dust,        "ignore",                    CONTENT_IGNORE);
+}
diff --git a/src/mapgen/mg_biome.h b/src/mapgen/mg_biome.h
new file mode 100644 (file)
index 0000000..f45238f
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+Minetest
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2014-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include "objdef.h"
+#include "nodedef.h"
+#include "noise.h"
+
+class Server;
+class Settings;
+class BiomeManager;
+
+////
+//// Biome
+////
+
+typedef u8 biome_t;
+
+#define BIOME_NONE ((biome_t)0)
+
+// TODO(hmmmm): Decide whether this is obsolete or will be used in the future
+enum BiomeType {
+       BIOMETYPE_NORMAL,
+       BIOMETYPE_LIQUID,
+       BIOMETYPE_NETHER,
+       BIOMETYPE_AETHER,
+       BIOMETYPE_FLAT,
+};
+
+class Biome : public ObjDef, public NodeResolver {
+public:
+       u32 flags;
+
+       content_t c_top;
+       content_t c_filler;
+       content_t c_stone;
+       content_t c_water_top;
+       content_t c_water;
+       content_t c_river_water;
+       content_t c_riverbed;
+       content_t c_dust;
+
+       s16 depth_top;
+       s16 depth_filler;
+       s16 depth_water_top;
+       s16 depth_riverbed;
+
+       s16 y_min;
+       s16 y_max;
+       float heat_point;
+       float humidity_point;
+
+       virtual void resolveNodeNames();
+};
+
+
+////
+//// BiomeGen
+////
+
+enum BiomeGenType {
+       BIOMEGEN_ORIGINAL,
+};
+
+struct BiomeParams {
+       virtual void readParams(const Settings *settings) = 0;
+       virtual void writeParams(Settings *settings) const = 0;
+       virtual ~BiomeParams() = default;
+
+       s32 seed;
+};
+
+class BiomeGen {
+public:
+       virtual ~BiomeGen() = default;
+
+       virtual BiomeGenType getType() const = 0;
+
+       // Calculates the biome at the exact position provided.  This function can
+       // be called at any time, but may be less efficient than the latter methods,
+       // depending on implementation.
+       virtual Biome *calcBiomeAtPoint(v3s16 pos) const = 0;
+
+       // Computes any intermediate results needed for biome generation.  Must be
+       // called before using any of: getBiomes, getBiomeAtPoint, or getBiomeAtIndex.
+       // Calling this invalidates the previous results stored in biomemap.
+       virtual void calcBiomeNoise(v3s16 pmin) = 0;
+
+       // Gets all biomes in current chunk using each corresponding element of
+       // heightmap as the y position, then stores the results by biome index in
+       // biomemap (also returned)
+       virtual biome_t *getBiomes(s16 *heightmap) = 0;
+
+       // Gets a single biome at the specified position, which must be contained
+       // in the region formed by m_pmin and (m_pmin + m_csize - 1).
+       virtual Biome *getBiomeAtPoint(v3s16 pos) const = 0;
+
+       // Same as above, but uses a raw numeric index correlating to the (x,z) position.
+       virtual Biome *getBiomeAtIndex(size_t index, s16 y) const = 0;
+
+       // Result of calcBiomes bulk computation.
+       biome_t *biomemap = nullptr;
+
+protected:
+       BiomeManager *m_bmgr = nullptr;
+       v3s16 m_pmin;
+       v3s16 m_csize;
+};
+
+
+////
+//// BiomeGen implementations
+////
+
+//
+// Original biome algorithm (Whittaker's classification + surface height)
+//
+
+struct BiomeParamsOriginal : public BiomeParams {
+       BiomeParamsOriginal() :
+               np_heat(50, 50, v3f(1000.0, 1000.0, 1000.0), 5349, 3, 0.5, 2.0),
+               np_humidity(50, 50, v3f(1000.0, 1000.0, 1000.0), 842, 3, 0.5, 2.0),
+               np_heat_blend(0, 1.5, v3f(8.0, 8.0, 8.0), 13, 2, 1.0, 2.0),
+               np_humidity_blend(0, 1.5, v3f(8.0, 8.0, 8.0), 90003, 2, 1.0, 2.0)
+       {
+       }
+
+       virtual void readParams(const Settings *settings);
+       virtual void writeParams(Settings *settings) const;
+
+       NoiseParams np_heat;
+       NoiseParams np_humidity;
+       NoiseParams np_heat_blend;
+       NoiseParams np_humidity_blend;
+};
+
+class BiomeGenOriginal : public BiomeGen {
+public:
+       BiomeGenOriginal(BiomeManager *biomemgr,
+               BiomeParamsOriginal *params, v3s16 chunksize);
+       virtual ~BiomeGenOriginal();
+
+       BiomeGenType getType() const { return BIOMEGEN_ORIGINAL; }
+
+       Biome *calcBiomeAtPoint(v3s16 pos) const;
+       void calcBiomeNoise(v3s16 pmin);
+
+       biome_t *getBiomes(s16 *heightmap);
+       Biome *getBiomeAtPoint(v3s16 pos) const;
+       Biome *getBiomeAtIndex(size_t index, s16 y) const;
+
+       Biome *calcBiomeFromNoise(float heat, float humidity, s16 y) const;
+
+       float *heatmap;
+       float *humidmap;
+
+private:
+       BiomeParamsOriginal *m_params;
+
+       Noise *noise_heat;
+       Noise *noise_humidity;
+       Noise *noise_heat_blend;
+       Noise *noise_humidity_blend;
+};
+
+
+////
+//// BiomeManager
+////
+
+class BiomeManager : public ObjDefManager {
+public:
+       BiomeManager(Server *server);
+       virtual ~BiomeManager() = default;
+
+       const char *getObjectTitle() const
+       {
+               return "biome";
+       }
+
+       static Biome *create(BiomeType type)
+       {
+               return new Biome;
+       }
+
+       BiomeGen *createBiomeGen(BiomeGenType type, BiomeParams *params, v3s16 chunksize)
+       {
+               switch (type) {
+               case BIOMEGEN_ORIGINAL:
+                       return new BiomeGenOriginal(this,
+                               (BiomeParamsOriginal *)params, chunksize);
+               default:
+                       return NULL;
+               }
+       }
+
+       static BiomeParams *createBiomeParams(BiomeGenType type)
+       {
+               switch (type) {
+               case BIOMEGEN_ORIGINAL:
+                       return new BiomeParamsOriginal;
+               default:
+                       return NULL;
+               }
+       }
+
+       virtual void clear();
+
+private:
+       Server *m_server;
+
+};
diff --git a/src/mapgen/mg_decoration.cpp b/src/mapgen/mg_decoration.cpp
new file mode 100644 (file)
index 0000000..2c2fbc6
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+Minetest
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "mg_decoration.h"
+#include "mg_schematic.h"
+#include "mapgen.h"
+#include "noise.h"
+#include "map.h"
+#include "log.h"
+#include "util/numeric.h"
+#include <algorithm>
+
+
+FlagDesc flagdesc_deco[] = {
+       {"place_center_x",  DECO_PLACE_CENTER_X},
+       {"place_center_y",  DECO_PLACE_CENTER_Y},
+       {"place_center_z",  DECO_PLACE_CENTER_Z},
+       {"force_placement", DECO_FORCE_PLACEMENT},
+       {"liquid_surface",  DECO_LIQUID_SURFACE},
+       {"all_floors",      DECO_ALL_FLOORS},
+       {"all_ceilings",    DECO_ALL_CEILINGS},
+       {NULL,              0}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+DecorationManager::DecorationManager(IGameDef *gamedef) :
+       ObjDefManager(gamedef, OBJDEF_DECORATION)
+{
+}
+
+
+size_t DecorationManager::placeAllDecos(Mapgen *mg, u32 blockseed,
+       v3s16 nmin, v3s16 nmax)
+{
+       size_t nplaced = 0;
+
+       for (size_t i = 0; i != m_objects.size(); i++) {
+               Decoration *deco = (Decoration *)m_objects[i];
+               if (!deco)
+                       continue;
+
+               nplaced += deco->placeDeco(mg, blockseed, nmin, nmax);
+               blockseed++;
+       }
+
+       return nplaced;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+void Decoration::resolveNodeNames()
+{
+       getIdsFromNrBacklog(&c_place_on);
+       getIdsFromNrBacklog(&c_spawnby);
+}
+
+
+bool Decoration::canPlaceDecoration(MMVManip *vm, v3s16 p)
+{
+       // Check if the decoration can be placed on this node
+       u32 vi = vm->m_area.index(p);
+       if (!CONTAINS(c_place_on, vm->m_data[vi].getContent()))
+               return false;
+
+       // Don't continue if there are no spawnby constraints
+       if (nspawnby == -1)
+               return true;
+
+       int nneighs = 0;
+       static const v3s16 dirs[16] = {
+               v3s16( 0, 0,  1),
+               v3s16( 0, 0, -1),
+               v3s16( 1, 0,  0),
+               v3s16(-1, 0,  0),
+               v3s16( 1, 0,  1),
+               v3s16(-1, 0,  1),
+               v3s16(-1, 0, -1),
+               v3s16( 1, 0, -1),
+
+               v3s16( 0, 1,  1),
+               v3s16( 0, 1, -1),
+               v3s16( 1, 1,  0),
+               v3s16(-1, 1,  0),
+               v3s16( 1, 1,  1),
+               v3s16(-1, 1,  1),
+               v3s16(-1, 1, -1),
+               v3s16( 1, 1, -1)
+       };
+
+       // Check these 16 neighbouring nodes for enough spawnby nodes
+       for (size_t i = 0; i != ARRLEN(dirs); i++) {
+               u32 index = vm->m_area.index(p + dirs[i]);
+               if (!vm->m_area.contains(index))
+                       continue;
+
+               if (CONTAINS(c_spawnby, vm->m_data[index].getContent()))
+                       nneighs++;
+       }
+
+       if (nneighs < nspawnby)
+               return false;
+
+       return true;
+}
+
+
+size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
+{
+       PcgRandom ps(blockseed + 53);
+       int carea_size = nmax.X - nmin.X + 1;
+
+       // Divide area into parts
+       // If chunksize is changed it may no longer be divisable by sidelen
+       if (carea_size % sidelen)
+               sidelen = carea_size;
+
+       s16 divlen = carea_size / sidelen;
+       int area = sidelen * sidelen;
+
+       for (s16 z0 = 0; z0 < divlen; z0++)
+       for (s16 x0 = 0; x0 < divlen; x0++) {
+               v2s16 p2d_center( // Center position of part of division
+                       nmin.X + sidelen / 2 + sidelen * x0,
+                       nmin.Z + sidelen / 2 + sidelen * z0
+               );
+               v2s16 p2d_min( // Minimum edge of part of division
+                       nmin.X + sidelen * x0,
+                       nmin.Z + sidelen * z0
+               );
+               v2s16 p2d_max( // Maximum edge of part of division
+                       nmin.X + sidelen + sidelen * x0 - 1,
+                       nmin.Z + sidelen + sidelen * z0 - 1
+               );
+
+               // Amount of decorations
+               float nval = (flags & DECO_USE_NOISE) ?
+                       NoisePerlin2D(&np, p2d_center.X, p2d_center.Y, mapseed) :
+                       fill_ratio;
+               u32 deco_count = 0;
+               float deco_count_f = (float)area * nval;
+               if (deco_count_f >= 1.f) {
+                       deco_count = deco_count_f;
+               } else if (deco_count_f > 0.f) {
+                       // For low density decorations calculate a chance for 1 decoration
+                       if (ps.range(1000) <= deco_count_f * 1000.f)
+                               deco_count = 1;
+               }
+
+               for (u32 i = 0; i < deco_count; i++) {
+                       s16 x = ps.range(p2d_min.X, p2d_max.X);
+                       s16 z = ps.range(p2d_min.Y, p2d_max.Y);
+                       int mapindex = carea_size * (z - nmin.Z) + (x - nmin.X);
+
+                       if ((flags & DECO_ALL_FLOORS) ||
+                                       (flags & DECO_ALL_CEILINGS)) {
+                               // All-surfaces decorations
+                               // Check biome of column
+                               if (mg->biomemap && !biomes.empty()) {
+                                       std::unordered_set<u8>::const_iterator iter =
+                                               biomes.find(mg->biomemap[mapindex]);
+                                       if (iter == biomes.end())
+                                               continue;
+                               }
+
+                               // Get all floors and ceilings in node column
+                               u16 size = (nmax.Y - nmin.Y + 1) / 2;
+                               s16 floors[size];
+                               s16 ceilings[size];
+                               u16 num_floors = 0;
+                               u16 num_ceilings = 0;
+
+                               mg->getSurfaces(v2s16(x, z), nmin.Y, nmax.Y,
+                                       floors, ceilings, &num_floors, &num_ceilings);
+
+                               if ((flags & DECO_ALL_FLOORS) && num_floors > 0) {
+                                       // Floor decorations
+                                       for (u16 fi = 0; fi < num_floors; fi++) {
+                                               s16 y = floors[fi];
+                                               if (y < y_min || y > y_max)
+                                                       continue;
+
+                                               v3s16 pos(x, y, z);
+                                               if (generate(mg->vm, &ps, pos, false))
+                                                       mg->gennotify.addEvent(
+                                                                       GENNOTIFY_DECORATION, pos, index);
+                                       }
+                               }
+
+                               if ((flags & DECO_ALL_CEILINGS) && num_ceilings > 0) {
+                                       // Ceiling decorations
+                                       for (u16 ci = 0; ci < num_ceilings; ci++) {
+                                               s16 y = ceilings[ci];
+                                               if (y < y_min || y > y_max)
+                                                       continue;
+
+                                               v3s16 pos(x, y, z);
+                                               if (generate(mg->vm, &ps, pos, true))
+                                                       mg->gennotify.addEvent(
+                                                                       GENNOTIFY_DECORATION, pos, index);
+                                       }
+                               }
+                       } else { // Heightmap decorations
+                               s16 y = -MAX_MAP_GENERATION_LIMIT;
+                               if (flags & DECO_LIQUID_SURFACE)
+                                       y = mg->findLiquidSurface(v2s16(x, z), nmin.Y, nmax.Y);
+                               else if (mg->heightmap)
+                                       y = mg->heightmap[mapindex];
+                               else
+                                       y = mg->findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
+
+                               if (y < y_min || y > y_max || y < nmin.Y || y > nmax.Y)
+                                       continue;
+
+                               if (mg->biomemap && !biomes.empty()) {
+                                       std::unordered_set<u8>::const_iterator iter =
+                                               biomes.find(mg->biomemap[mapindex]);
+                                       if (iter == biomes.end())
+                                               continue;
+                               }
+
+                               v3s16 pos(x, y, z);
+                               if (generate(mg->vm, &ps, pos, false))
+                                       mg->gennotify.addEvent(GENNOTIFY_DECORATION, pos, index);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+void DecoSimple::resolveNodeNames()
+{
+       Decoration::resolveNodeNames();
+       getIdsFromNrBacklog(&c_decos);
+}
+
+
+size_t DecoSimple::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling)
+{
+       // Don't bother if there aren't any decorations to place
+       if (c_decos.empty())
+               return 0;
+
+       if (!canPlaceDecoration(vm, p))
+               return 0;
+
+       // Check for placement outside the voxelmanip volume
+       if (ceiling) {
+               // Ceiling decorations
+               // 'place offset y' is inverted
+               if (p.Y - place_offset_y - std::max(deco_height, deco_height_max) <
+                               vm->m_area.MinEdge.Y)
+                       return 0;
+
+               if (p.Y - 1 - place_offset_y > vm->m_area.MaxEdge.Y)
+                       return 0;
+
+       } else { // Heightmap and floor decorations
+               if (p.Y + place_offset_y + std::max(deco_height, deco_height_max) >
+                               vm->m_area.MaxEdge.Y)
+                       return 0;
+
+               if (p.Y + 1 + place_offset_y < vm->m_area.MinEdge.Y)
+                       return 0;
+       }
+
+       content_t c_place = c_decos[pr->range(0, c_decos.size() - 1)];
+       s16 height = (deco_height_max > 0) ?
+               pr->range(deco_height, deco_height_max) : deco_height;
+       u8 param2 = (deco_param2_max > 0) ?
+               pr->range(deco_param2, deco_param2_max) : deco_param2;
+       bool force_placement = (flags & DECO_FORCE_PLACEMENT);
+
+       const v3s16 &em = vm->m_area.getExtent();
+       u32 vi = vm->m_area.index(p);
+
+       if (ceiling) {
+               // Ceiling decorations
+               // 'place offset y' is inverted
+               vm->m_area.add_y(em, vi, -place_offset_y);
+
+               for (int i = 0; i < height; i++) {
+                       vm->m_area.add_y(em, vi, -1);
+                       content_t c = vm->m_data[vi].getContent();
+                       if (c != CONTENT_AIR && c != CONTENT_IGNORE && !force_placement)
+                               break;
+
+                       vm->m_data[vi] = MapNode(c_place, 0, param2);
+               }
+       } else { // Heightmap and floor decorations
+               vm->m_area.add_y(em, vi, place_offset_y);
+
+               for (int i = 0; i < height; i++) {
+                       vm->m_area.add_y(em, vi, 1);
+                       content_t c = vm->m_data[vi].getContent();
+                       if (c != CONTENT_AIR && c != CONTENT_IGNORE && !force_placement)
+                               break;
+
+                       vm->m_data[vi] = MapNode(c_place, 0, param2);
+               }
+       }
+
+       return 1;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+size_t DecoSchematic::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling)
+{
+       // Schematic could have been unloaded but not the decoration
+       // In this case generate() does nothing (but doesn't *fail*)
+       if (schematic == NULL)
+               return 0;
+
+       if (!canPlaceDecoration(vm, p))
+               return 0;
+
+       if (flags & DECO_PLACE_CENTER_Y) {
+               p.Y -= (schematic->size.Y - 1) / 2;
+       } else {
+               // Only apply 'place offset y' if not 'deco place center y'
+               if (ceiling)
+                       // Shift down so that schematic top layer is level with ceiling
+                       // 'place offset y' is inverted
+                       p.Y -= (place_offset_y + schematic->size.Y - 1);
+               else
+                       p.Y += place_offset_y;
+       }
+
+       // Check schematic top and base are in voxelmanip
+       if (p.Y + schematic->size.Y - 1 > vm->m_area.MaxEdge.Y)
+               return 0;
+
+       if (p.Y < vm->m_area.MinEdge.Y)
+               return 0;
+
+       if (flags & DECO_PLACE_CENTER_X)
+               p.X -= (schematic->size.X - 1) / 2;
+       if (flags & DECO_PLACE_CENTER_Z)
+               p.Z -= (schematic->size.Z - 1) / 2;
+
+       Rotation rot = (rotation == ROTATE_RAND) ?
+               (Rotation)pr->range(ROTATE_0, ROTATE_270) : rotation;
+       bool force_placement = (flags & DECO_FORCE_PLACEMENT);
+
+       schematic->blitToVManip(vm, p, rot, force_placement);
+
+       return 1;
+}
diff --git a/src/mapgen/mg_decoration.h b/src/mapgen/mg_decoration.h
new file mode 100644 (file)
index 0000000..1ca632f
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+Minetest
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include <unordered_set>
+#include "objdef.h"
+#include "noise.h"
+#include "nodedef.h"
+
+class Mapgen;
+class MMVManip;
+class PcgRandom;
+class Schematic;
+
+enum DecorationType {
+       DECO_SIMPLE,
+       DECO_SCHEMATIC,
+       DECO_LSYSTEM
+};
+
+#define DECO_PLACE_CENTER_X  0x01
+#define DECO_PLACE_CENTER_Y  0x02
+#define DECO_PLACE_CENTER_Z  0x04
+#define DECO_USE_NOISE       0x08
+#define DECO_FORCE_PLACEMENT 0x10
+#define DECO_LIQUID_SURFACE  0x20
+#define DECO_ALL_FLOORS      0x40
+#define DECO_ALL_CEILINGS    0x80
+
+extern FlagDesc flagdesc_deco[];
+
+
+class Decoration : public ObjDef, public NodeResolver {
+public:
+       Decoration() = default;
+       virtual ~Decoration() = default;
+
+       virtual void resolveNodeNames();
+
+       bool canPlaceDecoration(MMVManip *vm, v3s16 p);
+       size_t placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+
+       virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling) = 0;
+
+       u32 flags = 0;
+       int mapseed = 0;
+       std::vector<content_t> c_place_on;
+       s16 sidelen = 1;
+       s16 y_min;
+       s16 y_max;
+       float fill_ratio = 0.0f;
+       NoiseParams np;
+       std::vector<content_t> c_spawnby;
+       s16 nspawnby;
+       s16 place_offset_y = 0;
+
+       std::unordered_set<u8> biomes;
+};
+
+
+class DecoSimple : public Decoration {
+public:
+       virtual void resolveNodeNames();
+       virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling);
+
+       std::vector<content_t> c_decos;
+       s16 deco_height;
+       s16 deco_height_max;
+       u8 deco_param2;
+       u8 deco_param2_max;
+};
+
+
+class DecoSchematic : public Decoration {
+public:
+       DecoSchematic() = default;
+
+       virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling);
+
+       Rotation rotation;
+       Schematic *schematic = nullptr;
+};
+
+
+/*
+class DecoLSystem : public Decoration {
+public:
+       virtual void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+};
+*/
+
+
+class DecorationManager : public ObjDefManager {
+public:
+       DecorationManager(IGameDef *gamedef);
+       virtual ~DecorationManager() = default;
+
+       const char *getObjectTitle() const
+       {
+               return "decoration";
+       }
+
+       static Decoration *create(DecorationType type)
+       {
+               switch (type) {
+               case DECO_SIMPLE:
+                       return new DecoSimple;
+               case DECO_SCHEMATIC:
+                       return new DecoSchematic;
+               //case DECO_LSYSTEM:
+               //      return new DecoLSystem;
+               default:
+                       return NULL;
+               }
+       }
+
+       size_t placeAllDecos(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+};
diff --git a/src/mapgen/mg_ore.cpp b/src/mapgen/mg_ore.cpp
new file mode 100644 (file)
index 0000000..979135e
--- /dev/null
@@ -0,0 +1,483 @@
+/*
+Minetest
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "mg_ore.h"
+#include "mapgen.h"
+#include "noise.h"
+#include "map.h"
+#include "log.h"
+#include "util/numeric.h"
+#include <algorithm>
+
+
+FlagDesc flagdesc_ore[] = {
+       {"absheight",                 OREFLAG_ABSHEIGHT}, // Non-functional
+       {"puff_cliffs",               OREFLAG_PUFF_CLIFFS},
+       {"puff_additive_composition", OREFLAG_PUFF_ADDITIVE},
+       {NULL,                        0}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+OreManager::OreManager(IGameDef *gamedef) :
+       ObjDefManager(gamedef, OBJDEF_ORE)
+{
+}
+
+
+size_t OreManager::placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
+{
+       size_t nplaced = 0;
+
+       for (size_t i = 0; i != m_objects.size(); i++) {
+               Ore *ore = (Ore *)m_objects[i];
+               if (!ore)
+                       continue;
+
+               nplaced += ore->placeOre(mg, blockseed, nmin, nmax);
+               blockseed++;
+       }
+
+       return nplaced;
+}
+
+
+void OreManager::clear()
+{
+       for (ObjDef *object : m_objects) {
+               Ore *ore = (Ore *) object;
+               delete ore;
+       }
+       m_objects.clear();
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+Ore::~Ore()
+{
+       delete noise;
+}
+
+
+void Ore::resolveNodeNames()
+{
+       getIdFromNrBacklog(&c_ore, "", CONTENT_AIR);
+       getIdsFromNrBacklog(&c_wherein);
+}
+
+
+size_t Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
+{
+       if (nmin.Y > y_max || nmax.Y < y_min)
+               return 0;
+
+       int actual_ymin = MYMAX(nmin.Y, y_min);
+       int actual_ymax = MYMIN(nmax.Y, y_max);
+       if (clust_size >= actual_ymax - actual_ymin + 1)
+               return 0;
+
+       nmin.Y = actual_ymin;
+       nmax.Y = actual_ymax;
+       generate(mg->vm, mg->seed, blockseed, nmin, nmax, mg->biomemap);
+
+       return 1;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
+       v3s16 nmin, v3s16 nmax, u8 *biomemap)
+{
+       PcgRandom pr(blockseed);
+       MapNode n_ore(c_ore, 0, ore_param2);
+
+       u32 sizex  = (nmax.X - nmin.X + 1);
+       u32 volume = (nmax.X - nmin.X + 1) *
+                                (nmax.Y - nmin.Y + 1) *
+                                (nmax.Z - nmin.Z + 1);
+       u32 csize     = clust_size;
+       u32 cvolume    = csize * csize * csize;
+       u32 nclusters = volume / clust_scarcity;
+
+       for (u32 i = 0; i != nclusters; i++) {
+               int x0 = pr.range(nmin.X, nmax.X - csize + 1);
+               int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
+               int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
+
+               if ((flags & OREFLAG_USE_NOISE) &&
+                       (NoisePerlin3D(&np, x0, y0, z0, mapseed) < nthresh))
+                       continue;
+
+               if (biomemap && !biomes.empty()) {
+                       u32 index = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
+                       std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
+                       if (it == biomes.end())
+                               continue;
+               }
+
+               for (u32 z1 = 0; z1 != csize; z1++)
+               for (u32 y1 = 0; y1 != csize; y1++)
+               for (u32 x1 = 0; x1 != csize; x1++) {
+                       if (pr.range(1, cvolume) > clust_num_ores)
+                               continue;
+
+                       u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
+                       if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
+                               continue;
+
+                       vm->m_data[i] = n_ore;
+               }
+       }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
+       v3s16 nmin, v3s16 nmax, u8 *biomemap)
+{
+       PcgRandom pr(blockseed + 4234);
+       MapNode n_ore(c_ore, 0, ore_param2);
+
+       u16 max_height = column_height_max;
+       int y_start_min = nmin.Y + max_height;
+       int y_start_max = nmax.Y - max_height;
+
+       int y_start = y_start_min < y_start_max ?
+               pr.range(y_start_min, y_start_max) :
+               (y_start_min + y_start_max) / 2;
+
+       if (!noise) {
+               int sx = nmax.X - nmin.X + 1;
+               int sz = nmax.Z - nmin.Z + 1;
+               noise = new Noise(&np, 0, sx, sz);
+       }
+       noise->seed = mapseed + y_start;
+       noise->perlinMap2D(nmin.X, nmin.Z);
+
+       size_t index = 0;
+       for (int z = nmin.Z; z <= nmax.Z; z++)
+       for (int x = nmin.X; x <= nmax.X; x++, index++) {
+               float noiseval = noise->result[index];
+               if (noiseval < nthresh)
+                       continue;
+
+               if (biomemap && !biomes.empty()) {
+                       std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
+                       if (it == biomes.end())
+                               continue;
+               }
+
+               u16 height = pr.range(column_height_min, column_height_max);
+               int ymidpoint = y_start + noiseval;
+               int y0 = MYMAX(nmin.Y, ymidpoint - height * (1 - column_midpoint_factor));
+               int y1 = MYMIN(nmax.Y, y0 + height - 1);
+
+               for (int y = y0; y <= y1; y++) {
+                       u32 i = vm->m_area.index(x, y, z);
+                       if (!vm->m_area.contains(i))
+                               continue;
+                       if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
+                               continue;
+
+                       vm->m_data[i] = n_ore;
+               }
+       }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+OrePuff::~OrePuff()
+{
+       delete noise_puff_top;
+       delete noise_puff_bottom;
+}
+
+
+void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed,
+       v3s16 nmin, v3s16 nmax, u8 *biomemap)
+{
+       PcgRandom pr(blockseed + 4234);
+       MapNode n_ore(c_ore, 0, ore_param2);
+
+       int y_start = pr.range(nmin.Y, nmax.Y);
+
+       if (!noise) {
+               int sx = nmax.X - nmin.X + 1;
+               int sz = nmax.Z - nmin.Z + 1;
+               noise = new Noise(&np, 0, sx, sz);
+               noise_puff_top = new Noise(&np_puff_top, 0, sx, sz);
+               noise_puff_bottom = new Noise(&np_puff_bottom, 0, sx, sz);
+       }
+
+       noise->seed = mapseed + y_start;
+       noise->perlinMap2D(nmin.X, nmin.Z);
+       bool noise_generated = false;
+
+       size_t index = 0;
+       for (int z = nmin.Z; z <= nmax.Z; z++)
+       for (int x = nmin.X; x <= nmax.X; x++, index++) {
+               float noiseval = noise->result[index];
+               if (noiseval < nthresh)
+                       continue;
+
+               if (biomemap && !biomes.empty()) {
+                       std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
+                       if (it == biomes.end())
+                               continue;
+               }
+
+               if (!noise_generated) {
+                       noise_generated = true;
+                       noise_puff_top->perlinMap2D(nmin.X, nmin.Z);
+                       noise_puff_bottom->perlinMap2D(nmin.X, nmin.Z);
+               }
+
+               float ntop    = noise_puff_top->result[index];
+               float nbottom = noise_puff_bottom->result[index];
+
+               if (!(flags & OREFLAG_PUFF_CLIFFS)) {
+                       float ndiff = noiseval - nthresh;
+                       if (ndiff < 1.0f) {
+                               ntop *= ndiff;
+                               nbottom *= ndiff;
+                       }
+               }
+
+               int ymid = y_start;
+               int y0 = ymid - nbottom;
+               int y1 = ymid + ntop;
+
+               if ((flags & OREFLAG_PUFF_ADDITIVE) && (y0 > y1))
+                       SWAP(int, y0, y1);
+
+               for (int y = y0; y <= y1; y++) {
+                       u32 i = vm->m_area.index(x, y, z);
+                       if (!vm->m_area.contains(i))
+                               continue;
+                       if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
+                               continue;
+
+                       vm->m_data[i] = n_ore;
+               }
+       }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed,
+       v3s16 nmin, v3s16 nmax, u8 *biomemap)
+{
+       PcgRandom pr(blockseed + 2404);
+       MapNode n_ore(c_ore, 0, ore_param2);
+
+       u32 sizex  = (nmax.X - nmin.X + 1);
+       u32 volume = (nmax.X - nmin.X + 1) *
+                                (nmax.Y - nmin.Y + 1) *
+                                (nmax.Z - nmin.Z + 1);
+       u32 csize  = clust_size;
+       u32 nblobs = volume / clust_scarcity;
+
+       if (!noise)
+               noise = new Noise(&np, mapseed, csize, csize, csize);
+
+       for (u32 i = 0; i != nblobs; i++) {
+               int x0 = pr.range(nmin.X, nmax.X - csize + 1);
+               int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
+               int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
+
+               if (biomemap && !biomes.empty()) {
+                       u32 bmapidx = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
+                       std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[bmapidx]);
+                       if (it == biomes.end())
+                               continue;
+               }
+
+               bool noise_generated = false;
+               noise->seed = blockseed + i;
+
+               size_t index = 0;
+               for (u32 z1 = 0; z1 != csize; z1++)
+               for (u32 y1 = 0; y1 != csize; y1++)
+               for (u32 x1 = 0; x1 != csize; x1++, index++) {
+                       u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
+                       if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
+                               continue;
+
+                       // Lazily generate noise only if there's a chance of ore being placed
+                       // This simple optimization makes calls 6x faster on average
+                       if (!noise_generated) {
+                               noise_generated = true;
+                               noise->perlinMap3D(x0, y0, z0);
+                       }
+
+                       float noiseval = noise->result[index];
+
+                       float xdist = (s32)x1 - (s32)csize / 2;
+                       float ydist = (s32)y1 - (s32)csize / 2;
+                       float zdist = (s32)z1 - (s32)csize / 2;
+
+                       noiseval -= (sqrt(xdist * xdist + ydist * ydist + zdist * zdist) / csize);
+
+                       if (noiseval < nthresh)
+                               continue;
+
+                       vm->m_data[i] = n_ore;
+               }
+       }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+OreVein::~OreVein()
+{
+       delete noise2;
+}
+
+
+void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
+       v3s16 nmin, v3s16 nmax, u8 *biomemap)
+{
+       PcgRandom pr(blockseed + 520);
+       MapNode n_ore(c_ore, 0, ore_param2);
+
+       u32 sizex = (nmax.X - nmin.X + 1);
+
+       if (!noise) {
+               int sx = nmax.X - nmin.X + 1;
+               int sy = nmax.Y - nmin.Y + 1;
+               int sz = nmax.Z - nmin.Z + 1;
+               noise  = new Noise(&np, mapseed, sx, sy, sz);
+               noise2 = new Noise(&np, mapseed + 436, sx, sy, sz);
+       }
+       bool noise_generated = false;
+
+       size_t index = 0;
+       for (int z = nmin.Z; z <= nmax.Z; z++)
+       for (int y = nmin.Y; y <= nmax.Y; y++)
+       for (int x = nmin.X; x <= nmax.X; x++, index++) {
+               u32 i = vm->m_area.index(x, y, z);
+               if (!vm->m_area.contains(i))
+                       continue;
+               if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
+                       continue;
+
+               if (biomemap && !biomes.empty()) {
+                       u32 bmapidx = sizex * (z - nmin.Z) + (x - nmin.X);
+                       std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[bmapidx]);
+                       if (it == biomes.end())
+                               continue;
+               }
+
+               // Same lazy generation optimization as in OreBlob
+               if (!noise_generated) {
+                       noise_generated = true;
+                       noise->perlinMap3D(nmin.X, nmin.Y, nmin.Z);
+                       noise2->perlinMap3D(nmin.X, nmin.Y, nmin.Z);
+               }
+
+               // randval ranges from -1..1
+               float randval   = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f;
+               float noiseval  = contour(noise->result[index]);
+               float noiseval2 = contour(noise2->result[index]);
+               if (noiseval * noiseval2 + randval * random_factor < nthresh)
+                       continue;
+
+               vm->m_data[i] = n_ore;
+       }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+OreStratum::~OreStratum()
+{
+       delete noise_stratum_thickness;
+}
+
+
+void OreStratum::generate(MMVManip *vm, int mapseed, u32 blockseed,
+       v3s16 nmin, v3s16 nmax, u8 *biomemap)
+{
+       PcgRandom pr(blockseed + 4234);
+       MapNode n_ore(c_ore, 0, ore_param2);
+
+       if (flags & OREFLAG_USE_NOISE) {
+               if (!(noise && noise_stratum_thickness)) {
+                       int sx = nmax.X - nmin.X + 1;
+                       int sz = nmax.Z - nmin.Z + 1;
+                       noise = new Noise(&np, 0, sx, sz);
+                       noise_stratum_thickness = new Noise(&np_stratum_thickness, 0, sx, sz);
+               }
+               noise->perlinMap2D(nmin.X, nmin.Z);
+               noise_stratum_thickness->perlinMap2D(nmin.X, nmin.Z);
+       }
+
+       size_t index = 0;
+
+       for (int z = nmin.Z; z <= nmax.Z; z++)
+       for (int x = nmin.X; x <= nmax.X; x++, index++) {
+               if (biomemap && !biomes.empty()) {
+                       std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
+                       if (it == biomes.end())
+                               continue;
+               }
+
+               int y0;
+               int y1;
+
+               if (flags & OREFLAG_USE_NOISE) {
+                       float nmid = noise->result[index];
+                       float nhalfthick = noise_stratum_thickness->result[index] / 2.0f;
+                       y0 = MYMAX(nmin.Y, nmid - nhalfthick);
+                       y1 = MYMIN(nmax.Y, nmid + nhalfthick);
+               } else {
+                       y0 = nmin.Y;
+                       y1 = nmax.Y;
+               }
+
+               for (int y = y0; y <= y1; y++) {
+                       if (pr.range(1, clust_scarcity) != 1)
+                               continue;
+
+                       u32 i = vm->m_area.index(x, y, z);
+                       if (!vm->m_area.contains(i))
+                               continue;
+                       if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
+                               continue;
+
+                       vm->m_data[i] = n_ore;
+               }
+       }
+}
diff --git a/src/mapgen/mg_ore.h b/src/mapgen/mg_ore.h
new file mode 100644 (file)
index 0000000..e715f34
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+Minetest
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include <unordered_set>
+#include "objdef.h"
+#include "noise.h"
+#include "nodedef.h"
+
+class Noise;
+class Mapgen;
+class MMVManip;
+
+/////////////////// Ore generation flags
+
+#define OREFLAG_ABSHEIGHT     0x01 // Non-functional but kept to not break flags
+#define OREFLAG_PUFF_CLIFFS   0x02
+#define OREFLAG_PUFF_ADDITIVE 0x04
+#define OREFLAG_USE_NOISE     0x08
+
+enum OreType {
+       ORE_SCATTER,
+       ORE_SHEET,
+       ORE_PUFF,
+       ORE_BLOB,
+       ORE_VEIN,
+       ORE_STRATUM,
+};
+
+extern FlagDesc flagdesc_ore[];
+
+class Ore : public ObjDef, public NodeResolver {
+public:
+       static const bool NEEDS_NOISE = false;
+
+       content_t c_ore;                  // the node to place
+       std::vector<content_t> c_wherein; // the nodes to be placed in
+       u32 clust_scarcity; // ore cluster has a 1-in-clust_scarcity chance of appearing at a node
+       s16 clust_num_ores; // how many ore nodes are in a chunk
+       s16 clust_size;     // how large (in nodes) a chunk of ore is
+       s16 y_min;
+       s16 y_max;
+       u8 ore_param2;          // to set node-specific attributes
+       u32 flags = 0;          // attributes for this ore
+       float nthresh;      // threshold for noise at which an ore is placed
+       NoiseParams np;     // noise for distribution of clusters (NULL for uniform scattering)
+       Noise *noise = nullptr;
+       std::unordered_set<u8> biomes;
+
+       Ore() = default;;
+       virtual ~Ore();
+
+       virtual void resolveNodeNames();
+
+       size_t placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
+               v3s16 nmin, v3s16 nmax, u8 *biomemap) = 0;
+};
+
+class OreScatter : public Ore {
+public:
+       static const bool NEEDS_NOISE = false;
+
+       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
+               v3s16 nmin, v3s16 nmax, u8 *biomemap);
+};
+
+class OreSheet : public Ore {
+public:
+       static const bool NEEDS_NOISE = true;
+
+       u16 column_height_min;
+       u16 column_height_max;
+       float column_midpoint_factor;
+
+       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
+               v3s16 nmin, v3s16 nmax, u8 *biomemap);
+};
+
+class OrePuff : public Ore {
+public:
+       static const bool NEEDS_NOISE = true;
+
+       NoiseParams np_puff_top;
+       NoiseParams np_puff_bottom;
+       Noise *noise_puff_top = nullptr;
+       Noise *noise_puff_bottom = nullptr;
+
+       OrePuff() = default;
+       virtual ~OrePuff();
+
+       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
+               v3s16 nmin, v3s16 nmax, u8 *biomemap);
+};
+
+class OreBlob : public Ore {
+public:
+       static const bool NEEDS_NOISE = true;
+
+       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
+               v3s16 nmin, v3s16 nmax, u8 *biomemap);
+};
+
+class OreVein : public Ore {
+public:
+       static const bool NEEDS_NOISE = true;
+
+       float random_factor;
+       Noise *noise2 = nullptr;
+
+       OreVein() = default;
+       virtual ~OreVein();
+
+       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
+               v3s16 nmin, v3s16 nmax, u8 *biomemap);
+};
+
+class OreStratum : public Ore {
+public:
+       static const bool NEEDS_NOISE = false;
+
+       NoiseParams np_stratum_thickness;
+       Noise *noise_stratum_thickness = nullptr;
+
+       OreStratum() = default;
+       virtual ~OreStratum();
+
+       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
+               v3s16 nmin, v3s16 nmax, u8 *biomemap);
+};
+
+class OreManager : public ObjDefManager {
+public:
+       OreManager(IGameDef *gamedef);
+       virtual ~OreManager() = default;
+
+       const char *getObjectTitle() const
+       {
+               return "ore";
+       }
+
+       static Ore *create(OreType type)
+       {
+               switch (type) {
+               case ORE_SCATTER:
+                       return new OreScatter;
+               case ORE_SHEET:
+                       return new OreSheet;
+               case ORE_PUFF:
+                       return new OrePuff;
+               case ORE_BLOB:
+                       return new OreBlob;
+               case ORE_VEIN:
+                       return new OreVein;
+               case ORE_STRATUM:
+                       return new OreStratum;
+               default:
+                       return nullptr;
+               }
+       }
+
+       void clear();
+
+       size_t placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
+};
diff --git a/src/mapgen/mg_schematic.cpp b/src/mapgen/mg_schematic.cpp
new file mode 100644 (file)
index 0000000..8874abd
--- /dev/null
@@ -0,0 +1,578 @@
+/*
+Minetest
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 <fstream>
+#include <typeinfo>
+#include "mg_schematic.h"
+#include "server.h"
+#include "mapgen.h"
+#include "emerge.h"
+#include "map.h"
+#include "mapblock.h"
+#include "log.h"
+#include "util/numeric.h"
+#include "util/serialize.h"
+#include "serialization.h"
+#include "filesys.h"
+#include "voxelalgorithms.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+SchematicManager::SchematicManager(Server *server) :
+       ObjDefManager(server, OBJDEF_SCHEMATIC),
+       m_server(server)
+{
+}
+
+
+void SchematicManager::clear()
+{
+       EmergeManager *emerge = m_server->getEmergeManager();
+
+       // Remove all dangling references in Decorations
+       DecorationManager *decomgr = emerge->decomgr;
+       for (size_t i = 0; i != decomgr->getNumObjects(); i++) {
+               Decoration *deco = (Decoration *)decomgr->getRaw(i);
+
+               try {
+                       DecoSchematic *dschem = dynamic_cast<DecoSchematic *>(deco);
+                       if (dschem)
+                               dschem->schematic = NULL;
+               } catch (const std::bad_cast &) {
+               }
+       }
+
+       ObjDefManager::clear();
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+Schematic::Schematic()
+= default;
+
+
+Schematic::~Schematic()
+{
+       delete []schemdata;
+       delete []slice_probs;
+}
+
+
+void Schematic::resolveNodeNames()
+{
+       getIdsFromNrBacklog(&c_nodes, true, CONTENT_AIR);
+
+       size_t bufsize = size.X * size.Y * size.Z;
+       for (size_t i = 0; i != bufsize; i++) {
+               content_t c_original = schemdata[i].getContent();
+               content_t c_new = c_nodes[c_original];
+               schemdata[i].setContent(c_new);
+       }
+}
+
+
+void Schematic::blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place)
+{
+       sanity_check(m_ndef != NULL);
+
+       int xstride = 1;
+       int ystride = size.X;
+       int zstride = size.X * size.Y;
+
+       s16 sx = size.X;
+       s16 sy = size.Y;
+       s16 sz = size.Z;
+
+       int i_start, i_step_x, i_step_z;
+       switch (rot) {
+               case ROTATE_90:
+                       i_start  = sx - 1;
+                       i_step_x = zstride;
+                       i_step_z = -xstride;
+                       SWAP(s16, sx, sz);
+                       break;
+               case ROTATE_180:
+                       i_start  = zstride * (sz - 1) + sx - 1;
+                       i_step_x = -xstride;
+                       i_step_z = -zstride;
+                       break;
+               case ROTATE_270:
+                       i_start  = zstride * (sz - 1);
+                       i_step_x = -zstride;
+                       i_step_z = xstride;
+                       SWAP(s16, sx, sz);
+                       break;
+               default:
+                       i_start  = 0;
+                       i_step_x = xstride;
+                       i_step_z = zstride;
+       }
+
+       s16 y_map = p.Y;
+       for (s16 y = 0; y != sy; y++) {
+               if ((slice_probs[y] != MTSCHEM_PROB_ALWAYS) &&
+                       (slice_probs[y] <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
+                       continue;
+
+               for (s16 z = 0; z != sz; z++) {
+                       u32 i = z * i_step_z + y * ystride + i_start;
+                       for (s16 x = 0; x != sx; x++, i += i_step_x) {
+                               u32 vi = vm->m_area.index(p.X + x, y_map, p.Z + z);
+                               if (!vm->m_area.contains(vi))
+                                       continue;
+
+                               if (schemdata[i].getContent() == CONTENT_IGNORE)
+                                       continue;
+
+                               u8 placement_prob     = schemdata[i].param1 & MTSCHEM_PROB_MASK;
+                               bool force_place_node = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
+
+                               if (placement_prob == MTSCHEM_PROB_NEVER)
+                                       continue;
+
+                               if (!force_place && !force_place_node) {
+                                       content_t c = vm->m_data[vi].getContent();
+                                       if (c != CONTENT_AIR && c != CONTENT_IGNORE)
+                                               continue;
+                               }
+
+                               if ((placement_prob != MTSCHEM_PROB_ALWAYS) &&
+                                       (placement_prob <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
+                                       continue;
+
+                               vm->m_data[vi] = schemdata[i];
+                               vm->m_data[vi].param1 = 0;
+
+                               if (rot)
+                                       vm->m_data[vi].rotateAlongYAxis(m_ndef, rot);
+                       }
+               }
+               y_map++;
+       }
+}
+
+
+bool Schematic::placeOnVManip(MMVManip *vm, v3s16 p, u32 flags,
+       Rotation rot, bool force_place)
+{
+       assert(vm != NULL);
+       assert(schemdata != NULL);
+       sanity_check(m_ndef != NULL);
+
+       //// Determine effective rotation and effective schematic dimensions
+       if (rot == ROTATE_RAND)
+               rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270);
+
+       v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ?
+               v3s16(size.Z, size.Y, size.X) : size;
+
+       //// Adjust placement position if necessary
+       if (flags & DECO_PLACE_CENTER_X)
+               p.X -= (s.X + 1) / 2;
+       if (flags & DECO_PLACE_CENTER_Y)
+               p.Y -= (s.Y + 1) / 2;
+       if (flags & DECO_PLACE_CENTER_Z)
+               p.Z -= (s.Z + 1) / 2;
+
+       blitToVManip(vm, p, rot, force_place);
+
+       return vm->m_area.contains(VoxelArea(p, p + s - v3s16(1,1,1)));
+}
+
+void Schematic::placeOnMap(ServerMap *map, v3s16 p, u32 flags,
+       Rotation rot, bool force_place)
+{
+       std::map<v3s16, MapBlock *> lighting_modified_blocks;
+       std::map<v3s16, MapBlock *> modified_blocks;
+       std::map<v3s16, MapBlock *>::iterator it;
+
+       assert(map != NULL);
+       assert(schemdata != NULL);
+       sanity_check(m_ndef != NULL);
+
+       //// Determine effective rotation and effective schematic dimensions
+       if (rot == ROTATE_RAND)
+               rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270);
+
+       v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ?
+                       v3s16(size.Z, size.Y, size.X) : size;
+
+       //// Adjust placement position if necessary
+       if (flags & DECO_PLACE_CENTER_X)
+               p.X -= (s.X + 1) / 2;
+       if (flags & DECO_PLACE_CENTER_Y)
+               p.Y -= (s.Y + 1) / 2;
+       if (flags & DECO_PLACE_CENTER_Z)
+               p.Z -= (s.Z + 1) / 2;
+
+       //// Create VManip for effected area, emerge our area, modify area
+       //// inside VManip, then blit back.
+       v3s16 bp1 = getNodeBlockPos(p);
+       v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1));
+
+       MMVManip vm(map);
+       vm.initialEmerge(bp1, bp2);
+
+       blitToVManip(&vm, p, rot, force_place);
+
+       voxalgo::blit_back_with_light(map, &vm, &modified_blocks);
+
+       //// Carry out post-map-modification actions
+
+       //// Create & dispatch map modification events to observers
+       MapEditEvent event;
+       event.type = MEET_OTHER;
+       for (it = modified_blocks.begin(); it != modified_blocks.end(); ++it)
+               event.modified_blocks.insert(it->first);
+
+       map->dispatchEvent(&event);
+}
+
+
+bool Schematic::deserializeFromMts(std::istream *is,
+       std::vector<std::string> *names)
+{
+       std::istream &ss = *is;
+       content_t cignore = CONTENT_IGNORE;
+       bool have_cignore = false;
+
+       //// Read signature
+       u32 signature = readU32(ss);
+       if (signature != MTSCHEM_FILE_SIGNATURE) {
+               errorstream << __FUNCTION__ << ": invalid schematic "
+                       "file" << std::endl;
+               return false;
+       }
+
+       //// Read version
+       u16 version = readU16(ss);
+       if (version > MTSCHEM_FILE_VER_HIGHEST_READ) {
+               errorstream << __FUNCTION__ << ": unsupported schematic "
+                       "file version" << std::endl;
+               return false;
+       }
+
+       //// Read size
+       size = readV3S16(ss);
+
+       //// Read Y-slice probability values
+       delete []slice_probs;
+       slice_probs = new u8[size.Y];
+       for (int y = 0; y != size.Y; y++)
+               slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS_OLD;
+
+       //// Read node names
+       u16 nidmapcount = readU16(ss);
+       for (int i = 0; i != nidmapcount; i++) {
+               std::string name = deSerializeString(ss);
+
+               // Instances of "ignore" from v1 are converted to air (and instances
+               // are fixed to have MTSCHEM_PROB_NEVER later on).
+               if (name == "ignore") {
+                       name = "air";
+                       cignore = i;
+                       have_cignore = true;
+               }
+
+               names->push_back(name);
+       }
+
+       //// Read node data
+       size_t nodecount = size.X * size.Y * size.Z;
+
+       delete []schemdata;
+       schemdata = new MapNode[nodecount];
+
+       MapNode::deSerializeBulk(ss, SER_FMT_VER_HIGHEST_READ, schemdata,
+               nodecount, 2, 2, true);
+
+       // Fix probability values for nodes that were ignore; removed in v2
+       if (version < 2) {
+               for (size_t i = 0; i != nodecount; i++) {
+                       if (schemdata[i].param1 == 0)
+                               schemdata[i].param1 = MTSCHEM_PROB_ALWAYS_OLD;
+                       if (have_cignore && schemdata[i].getContent() == cignore)
+                               schemdata[i].param1 = MTSCHEM_PROB_NEVER;
+               }
+       }
+
+       // Fix probability values for probability range truncation introduced in v4
+       if (version < 4) {
+               for (s16 y = 0; y != size.Y; y++)
+                       slice_probs[y] >>= 1;
+               for (size_t i = 0; i != nodecount; i++)
+                       schemdata[i].param1 >>= 1;
+       }
+
+       return true;
+}
+
+
+bool Schematic::serializeToMts(std::ostream *os,
+       const std::vector<std::string> &names)
+{
+       std::ostream &ss = *os;
+
+       writeU32(ss, MTSCHEM_FILE_SIGNATURE);         // signature
+       writeU16(ss, MTSCHEM_FILE_VER_HIGHEST_WRITE); // version
+       writeV3S16(ss, size);                         // schematic size
+
+       for (int y = 0; y != size.Y; y++)             // Y slice probabilities
+               writeU8(ss, slice_probs[y]);
+
+       writeU16(ss, names.size()); // name count
+       for (size_t i = 0; i != names.size(); i++)
+               ss << serializeString(names[i]); // node names
+
+       // compressed bulk node data
+       MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE,
+               schemdata, size.X * size.Y * size.Z, 2, 2, true);
+
+       return true;
+}
+
+
+bool Schematic::serializeToLua(std::ostream *os,
+       const std::vector<std::string> &names, bool use_comments, u32 indent_spaces)
+{
+       std::ostream &ss = *os;
+
+       std::string indent("\t");
+       if (indent_spaces > 0)
+               indent.assign(indent_spaces, ' ');
+
+       //// Write header
+       {
+               ss << "schematic = {" << std::endl;
+               ss << indent << "size = "
+                       << "{x=" << size.X
+                       << ", y=" << size.Y
+                       << ", z=" << size.Z
+                       << "}," << std::endl;
+       }
+
+       //// Write y-slice probabilities
+       {
+               ss << indent << "yslice_prob = {" << std::endl;
+
+               for (u16 y = 0; y != size.Y; y++) {
+                       u8 probability = slice_probs[y] & MTSCHEM_PROB_MASK;
+
+                       ss << indent << indent << "{"
+                               << "ypos=" << y
+                               << ", prob=" << (u16)probability * 2
+                               << "}," << std::endl;
+               }
+
+               ss << indent << "}," << std::endl;
+       }
+
+       //// Write node data
+       {
+               ss << indent << "data = {" << std::endl;
+
+               u32 i = 0;
+               for (u16 z = 0; z != size.Z; z++)
+               for (u16 y = 0; y != size.Y; y++) {
+                       if (use_comments) {
+                               ss << std::endl
+                                       << indent << indent
+                                       << "-- z=" << z
+                                       << ", y=" << y << std::endl;
+                       }
+
+                       for (u16 x = 0; x != size.X; x++, i++) {
+                               u8 probability   = schemdata[i].param1 & MTSCHEM_PROB_MASK;
+                               bool force_place = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
+
+                               ss << indent << indent << "{"
+                                       << "name=\"" << names[schemdata[i].getContent()]
+                                       << "\", prob=" << (u16)probability * 2
+                                       << ", param2=" << (u16)schemdata[i].param2;
+
+                               if (force_place)
+                                       ss << ", force_place=true";
+
+                               ss << "}," << std::endl;
+                       }
+               }
+
+               ss << indent << "}," << std::endl;
+       }
+
+       ss << "}" << std::endl;
+
+       return true;
+}
+
+
+bool Schematic::loadSchematicFromFile(const std::string &filename,
+       INodeDefManager *ndef, StringMap *replace_names)
+{
+       std::ifstream is(filename.c_str(), std::ios_base::binary);
+       if (!is.good()) {
+               errorstream << __FUNCTION__ << ": unable to open file '"
+                       << filename << "'" << std::endl;
+               return false;
+       }
+
+       size_t origsize = m_nodenames.size();
+       if (!deserializeFromMts(&is, &m_nodenames))
+               return false;
+
+       m_nnlistsizes.push_back(m_nodenames.size() - origsize);
+
+       name = filename;
+
+       if (replace_names) {
+               for (size_t i = origsize; i < m_nodenames.size(); i++) {
+                       std::string &node_name = m_nodenames[i];
+                       StringMap::iterator it = replace_names->find(node_name);
+                       if (it != replace_names->end())
+                               node_name = it->second;
+               }
+       }
+
+       if (ndef)
+               ndef->pendNodeResolve(this);
+
+       return true;
+}
+
+
+bool Schematic::saveSchematicToFile(const std::string &filename,
+       INodeDefManager *ndef)
+{
+       MapNode *orig_schemdata = schemdata;
+       std::vector<std::string> ndef_nodenames;
+       std::vector<std::string> *names;
+
+       if (m_resolve_done && ndef == NULL)
+               ndef = m_ndef;
+
+       if (ndef) {
+               names = &ndef_nodenames;
+
+               u32 volume = size.X * size.Y * size.Z;
+               schemdata = new MapNode[volume];
+               for (u32 i = 0; i != volume; i++)
+                       schemdata[i] = orig_schemdata[i];
+
+               generate_nodelist_and_update_ids(schemdata, volume, names, ndef);
+       } else { // otherwise, use the names we have on hand in the list
+               names = &m_nodenames;
+       }
+
+       std::ostringstream os(std::ios_base::binary);
+       bool status = serializeToMts(&os, *names);
+
+       if (ndef) {
+               delete []schemdata;
+               schemdata = orig_schemdata;
+       }
+
+       if (!status)
+               return false;
+
+       return fs::safeWriteToFile(filename, os.str());
+}
+
+
+bool Schematic::getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2)
+{
+       MMVManip *vm = new MMVManip(map);
+
+       v3s16 bp1 = getNodeBlockPos(p1);
+       v3s16 bp2 = getNodeBlockPos(p2);
+       vm->initialEmerge(bp1, bp2);
+
+       size = p2 - p1 + 1;
+
+       slice_probs = new u8[size.Y];
+       for (s16 y = 0; y != size.Y; y++)
+               slice_probs[y] = MTSCHEM_PROB_ALWAYS;
+
+       schemdata = new MapNode[size.X * size.Y * size.Z];
+
+       u32 i = 0;
+       for (s16 z = p1.Z; z <= p2.Z; z++)
+       for (s16 y = p1.Y; y <= p2.Y; y++) {
+               u32 vi = vm->m_area.index(p1.X, y, z);
+               for (s16 x = p1.X; x <= p2.X; x++, i++, vi++) {
+                       schemdata[i] = vm->m_data[vi];
+                       schemdata[i].param1 = MTSCHEM_PROB_ALWAYS;
+               }
+       }
+
+       delete vm;
+       return true;
+}
+
+
+void Schematic::applyProbabilities(v3s16 p0,
+       std::vector<std::pair<v3s16, u8> > *plist,
+       std::vector<std::pair<s16, u8> > *splist)
+{
+       for (size_t i = 0; i != plist->size(); i++) {
+               v3s16 p = (*plist)[i].first - p0;
+               int index = p.Z * (size.Y * size.X) + p.Y * size.X + p.X;
+               if (index < size.Z * size.Y * size.X) {
+                       u8 prob = (*plist)[i].second;
+                       schemdata[index].param1 = prob;
+
+                       // trim unnecessary node names from schematic
+                       if (prob == MTSCHEM_PROB_NEVER)
+                               schemdata[index].setContent(CONTENT_AIR);
+               }
+       }
+
+       for (size_t i = 0; i != splist->size(); i++) {
+               s16 y = (*splist)[i].first - p0.Y;
+               slice_probs[y] = (*splist)[i].second;
+       }
+}
+
+
+void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount,
+       std::vector<std::string> *usednodes, INodeDefManager *ndef)
+{
+       std::unordered_map<content_t, content_t> nodeidmap;
+       content_t numids = 0;
+
+       for (size_t i = 0; i != nodecount; i++) {
+               content_t id;
+               content_t c = nodes[i].getContent();
+
+               std::unordered_map<content_t, content_t>::const_iterator it = nodeidmap.find(c);
+               if (it == nodeidmap.end()) {
+                       id = numids;
+                       numids++;
+
+                       usednodes->push_back(ndef->get(c).name);
+                       nodeidmap.insert(std::make_pair(c, id));
+               } else {
+                       id = it->second;
+               }
+               nodes[i].setContent(id);
+       }
+}
diff --git a/src/mapgen/mg_schematic.h b/src/mapgen/mg_schematic.h
new file mode 100644 (file)
index 0000000..069b594
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+Minetest
+Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
+Copyright (C) 2015-2017 paramat
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include <map>
+#include "mg_decoration.h"
+#include "util/string.h"
+
+class Map;
+class ServerMap;
+class Mapgen;
+class MMVManip;
+class PseudoRandom;
+class NodeResolver;
+class Server;
+
+/*
+       Minetest Schematic File Format
+
+       All values are stored in big-endian byte order.
+       [u32] signature: 'MTSM'
+       [u16] version: 4
+       [u16] size X
+       [u16] size Y
+       [u16] size Z
+       For each Y:
+               [u8] slice probability value
+       [Name-ID table] Name ID Mapping Table
+               [u16] name-id count
+               For each name-id mapping:
+                       [u16] name length
+                       [u8[]] name
+       ZLib deflated {
+       For each node in schematic:  (for z, y, x)
+               [u16] content
+       For each node in schematic:
+               [u8] param1
+                 bit 0-6: probability
+                 bit 7:   specific node force placement
+       For each node in schematic:
+               [u8] param2
+       }
+
+       Version changes:
+       1 - Initial version
+       2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always
+       3 - Added y-slice probabilities; this allows for variable height structures
+       4 - Compressed range of node occurence prob., added per-node force placement bit
+*/
+
+//// Schematic constants
+#define MTSCHEM_FILE_SIGNATURE 0x4d54534d // 'MTSM'
+#define MTSCHEM_FILE_VER_HIGHEST_READ  4
+#define MTSCHEM_FILE_VER_HIGHEST_WRITE 4
+
+#define MTSCHEM_PROB_MASK       0x7F
+
+#define MTSCHEM_PROB_NEVER      0x00
+#define MTSCHEM_PROB_ALWAYS     0x7F
+#define MTSCHEM_PROB_ALWAYS_OLD 0xFF
+
+#define MTSCHEM_FORCE_PLACE     0x80
+
+enum SchematicType
+{
+       SCHEMATIC_NORMAL,
+};
+
+enum SchematicFormatType {
+       SCHEM_FMT_HANDLE,
+       SCHEM_FMT_MTS,
+       SCHEM_FMT_LUA,
+};
+
+class Schematic : public ObjDef, public NodeResolver {
+public:
+       Schematic();
+       virtual ~Schematic();
+
+       virtual void resolveNodeNames();
+
+       bool loadSchematicFromFile(const std::string &filename, INodeDefManager *ndef,
+               StringMap *replace_names=NULL);
+       bool saveSchematicToFile(const std::string &filename, INodeDefManager *ndef);
+       bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2);
+
+       bool deserializeFromMts(std::istream *is, std::vector<std::string> *names);
+       bool serializeToMts(std::ostream *os, const std::vector<std::string> &names);
+       bool serializeToLua(std::ostream *os, const std::vector<std::string> &names,
+               bool use_comments, u32 indent_spaces);
+
+       void blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place);
+       bool placeOnVManip(MMVManip *vm, v3s16 p, u32 flags, Rotation rot, bool force_place);
+       void placeOnMap(ServerMap *map, v3s16 p, u32 flags, Rotation rot, bool force_place);
+
+       void applyProbabilities(v3s16 p0,
+               std::vector<std::pair<v3s16, u8> > *plist,
+               std::vector<std::pair<s16, u8> > *splist);
+
+       std::vector<content_t> c_nodes;
+       u32 flags = 0;
+       v3s16 size;
+       MapNode *schemdata = nullptr;
+       u8 *slice_probs = nullptr;
+};
+
+class SchematicManager : public ObjDefManager {
+public:
+       SchematicManager(Server *server);
+       virtual ~SchematicManager() = default;
+
+       virtual void clear();
+
+       const char *getObjectTitle() const
+       {
+               return "schematic";
+       }
+
+       static Schematic *create(SchematicType type)
+       {
+               return new Schematic;
+       }
+
+private:
+       Server *m_server;
+};
+
+void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount,
+       std::vector<std::string> *usednodes, INodeDefManager *ndef);
diff --git a/src/mapgen/treegen.cpp b/src/mapgen/treegen.cpp
new file mode 100644 (file)
index 0000000..9e11b1a
--- /dev/null
@@ -0,0 +1,872 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>,
+                         2012-2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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 "irr_v3d.h"
+#include <stack>
+#include "util/pointer.h"
+#include "util/numeric.h"
+#include "map.h"
+#include "mapblock.h"
+#include "serverenvironment.h"
+#include "nodedef.h"
+#include "treegen.h"
+#include "voxelalgorithms.h"
+
+namespace treegen
+{
+
+void make_tree(MMVManip &vmanip, v3s16 p0,
+               bool is_apple_tree, INodeDefManager *ndef, s32 seed)
+{
+       /*
+               NOTE: Tree-placing code is currently duplicated in the engine
+               and in games that have saplings; both are deprecated but not
+               replaced yet
+       */
+       MapNode treenode(ndef->getId("mapgen_tree"));
+       MapNode leavesnode(ndef->getId("mapgen_leaves"));
+       MapNode applenode(ndef->getId("mapgen_apple"));
+
+       PseudoRandom pr(seed);
+       s16 trunk_h = pr.range(4, 5);
+       v3s16 p1 = p0;
+       for (s16 ii = 0; ii < trunk_h; ii++) {
+               if (vmanip.m_area.contains(p1)) {
+                       u32 vi = vmanip.m_area.index(p1);
+                       vmanip.m_data[vi] = treenode;
+               }
+               p1.Y++;
+       }
+
+       // p1 is now the last piece of the trunk
+       p1.Y -= 1;
+
+       VoxelArea leaves_a(v3s16(-2, -1, -2), v3s16(2, 2, 2));
+       Buffer<u8> leaves_d(leaves_a.getVolume());
+       for (s32 i = 0; i < leaves_a.getVolume(); i++)
+               leaves_d[i] = 0;
+
+       // Force leaves at near the end of the trunk
+       s16 d = 1;
+       for (s16 z = -d; z <= d; z++)
+       for (s16 y = -d; y <= d; y++)
+       for (s16 x = -d; x <= d; x++) {
+               leaves_d[leaves_a.index(v3s16(x, y, z))] = 1;
+       }
+
+       // Add leaves randomly
+       for (u32 iii = 0; iii < 7; iii++) {
+               v3s16 p(
+                       pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X - d),
+                       pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y - d),
+                       pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z - d)
+               );
+
+               for (s16 z = 0; z <= d; z++)
+               for (s16 y = 0; y <= d; y++)
+               for (s16 x = 0; x <= d; x++) {
+                       leaves_d[leaves_a.index(p + v3s16(x, y, z))] = 1;
+               }
+       }
+
+       // Blit leaves to vmanip
+       for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
+       for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
+               v3s16 pmin(leaves_a.MinEdge.X, y, z);
+               u32 i = leaves_a.index(pmin);
+               u32 vi = vmanip.m_area.index(pmin + p1);
+               for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
+                       v3s16 p(x, y, z);
+                       if (vmanip.m_area.contains(p + p1) &&
+                                       (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
+                                       vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
+                               if (leaves_d[i] == 1) {
+                                       bool is_apple = pr.range(0, 99) < 10;
+                                       if (is_apple_tree && is_apple)
+                                               vmanip.m_data[vi] = applenode;
+                                       else
+                                               vmanip.m_data[vi] = leavesnode;
+                               }
+                       }
+                       vi++;
+                       i++;
+               }
+       }
+}
+
+
+// L-System tree LUA spawner
+treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0,
+               INodeDefManager *ndef, const TreeDef &tree_definition)
+{
+       ServerMap *map = &env->getServerMap();
+       std::map<v3s16, MapBlock*> modified_blocks;
+       MMVManip vmanip(map);
+       v3s16 tree_blockp = getNodeBlockPos(p0);
+       treegen::error e;
+
+       vmanip.initialEmerge(tree_blockp - v3s16(1, 1, 1), tree_blockp + v3s16(1, 3, 1));
+       e = make_ltree(vmanip, p0, ndef, tree_definition);
+       if (e != SUCCESS)
+               return e;
+
+       voxalgo::blit_back_with_light(map, &vmanip, &modified_blocks);
+
+       // Send a MEET_OTHER event
+       MapEditEvent event;
+       event.type = MEET_OTHER;
+       for (auto &modified_block : modified_blocks)
+               event.modified_blocks.insert(modified_block.first);
+       map->dispatchEvent(&event);
+       return SUCCESS;
+}
+
+
+//L-System tree generator
+treegen::error make_ltree(MMVManip &vmanip, v3s16 p0,
+               INodeDefManager *ndef, TreeDef tree_definition)
+{
+       MapNode dirtnode(ndef->getId("mapgen_dirt"));
+       s32 seed;
+       if (tree_definition.explicit_seed)
+               seed = tree_definition.seed + 14002;
+       else
+               seed = p0.X * 2 + p0.Y * 4 + p0.Z;  // use the tree position to seed PRNG
+       PseudoRandom ps(seed);
+
+       // chance of inserting abcd rules
+       double prop_a = 9;
+       double prop_b = 8;
+       double prop_c = 7;
+       double prop_d = 6;
+
+       //randomize tree growth level, minimum=2
+       s16 iterations = tree_definition.iterations;
+       if (tree_definition.iterations_random_level > 0)
+               iterations -= ps.range(0, tree_definition.iterations_random_level);
+       if (iterations < 2)
+               iterations = 2;
+
+       s16 MAX_ANGLE_OFFSET = 5;
+       double angle_in_radians = (double)tree_definition.angle * M_PI / 180;
+       double angleOffset_in_radians = (s16)(ps.range(0, 1) % MAX_ANGLE_OFFSET) * M_PI / 180;
+
+       //initialize rotation matrix, position and stacks for branches
+       core::matrix4 rotation;
+       rotation = setRotationAxisRadians(rotation, M_PI / 2, v3f(0, 0, 1));
+       v3f position;
+       position.X = p0.X;
+       position.Y = p0.Y;
+       position.Z = p0.Z;
+       std::stack <core::matrix4> stack_orientation;
+       std::stack <v3f> stack_position;
+
+       //generate axiom
+       std::string axiom = tree_definition.initial_axiom;
+       for (s16 i = 0; i < iterations; i++) {
+               std::string temp;
+               for (s16 j = 0; j < (s16)axiom.size(); j++) {
+                       char axiom_char = axiom.at(j);
+                       switch (axiom_char) {
+                       case 'A':
+                               temp += tree_definition.rules_a;
+                               break;
+                       case 'B':
+                               temp += tree_definition.rules_b;
+                               break;
+                       case 'C':
+                               temp += tree_definition.rules_c;
+                               break;
+                       case 'D':
+                               temp += tree_definition.rules_d;
+                               break;
+                       case 'a':
+                               if (prop_a >= ps.range(1, 10))
+                                       temp += tree_definition.rules_a;
+                               break;
+                       case 'b':
+                               if (prop_b >= ps.range(1, 10))
+                                       temp += tree_definition.rules_b;
+                               break;
+                       case 'c':
+                               if (prop_c >= ps.range(1, 10))
+                                       temp += tree_definition.rules_c;
+                               break;
+                       case 'd':
+                               if (prop_d >= ps.range(1, 10))
+                                       temp += tree_definition.rules_d;
+                               break;
+                       default:
+                               temp += axiom_char;
+                               break;
+                       }
+               }
+               axiom = temp;
+       }
+
+       //make sure tree is not floating in the air
+       if (tree_definition.trunk_type == "double") {
+               tree_node_placement(
+                       vmanip,
+                       v3f(position.X + 1, position.Y - 1, position.Z),
+                       dirtnode
+               );
+               tree_node_placement(
+                       vmanip,
+                       v3f(position.X, position.Y - 1, position.Z + 1),
+                       dirtnode
+               );
+               tree_node_placement(
+                       vmanip,
+                       v3f(position.X + 1, position.Y - 1, position.Z + 1),
+                       dirtnode
+               );
+       } else if (tree_definition.trunk_type == "crossed") {
+               tree_node_placement(
+                       vmanip,
+                       v3f(position.X + 1, position.Y - 1, position.Z),
+                       dirtnode
+               );
+               tree_node_placement(
+                       vmanip,
+                       v3f(position.X - 1, position.Y - 1, position.Z),
+                       dirtnode
+               );
+               tree_node_placement(
+                       vmanip,
+                       v3f(position.X, position.Y - 1, position.Z + 1),
+                       dirtnode
+               );
+               tree_node_placement(
+                       vmanip,
+                       v3f(position.X, position.Y - 1, position.Z - 1),
+                       dirtnode
+               );
+       }
+
+       /* build tree out of generated axiom
+
+       Key for Special L-System Symbols used in Axioms
+
+    G  - move forward one unit with the pen up
+    F  - move forward one unit with the pen down drawing trunks and branches
+    f  - move forward one unit with the pen down drawing leaves (100% chance)
+    T  - move forward one unit with the pen down drawing trunks only
+    R  - move forward one unit with the pen down placing fruit
+    A  - replace with rules set A
+    B  - replace with rules set B
+    C  - replace with rules set C
+    D  - replace with rules set D
+    a  - replace with rules set A, chance 90%
+    b  - replace with rules set B, chance 80%
+    c  - replace with rules set C, chance 70%
+    d  - replace with rules set D, chance 60%
+    +  - yaw the turtle right by angle degrees
+    -  - yaw the turtle left by angle degrees
+    &  - pitch the turtle down by angle degrees
+    ^  - pitch the turtle up by angle degrees
+    /  - roll the turtle to the right by angle degrees
+    *  - roll the turtle to the left by angle degrees
+    [  - save in stack current state info
+    ]  - recover from stack state info
+
+    */
+
+       s16 x,y,z;
+       for (s16 i = 0; i < (s16)axiom.size(); i++) {
+               char axiom_char = axiom.at(i);
+               core::matrix4 temp_rotation;
+               temp_rotation.makeIdentity();
+               v3f dir;
+               switch (axiom_char) {
+               case 'G':
+                       dir = v3f(1, 0, 0);
+                       dir = transposeMatrix(rotation, dir);
+                       position += dir;
+                       break;
+               case 'T':
+                       tree_trunk_placement(
+                               vmanip,
+                               v3f(position.X, position.Y, position.Z),
+                               tree_definition
+                       );
+                       if (tree_definition.trunk_type == "double" &&
+                                       !tree_definition.thin_branches) {
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X + 1, position.Y, position.Z),
+                                       tree_definition
+                               );
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X, position.Y, position.Z + 1),
+                                       tree_definition
+                               );
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X + 1, position.Y, position.Z + 1),
+                                       tree_definition
+                               );
+                       } else if (tree_definition.trunk_type == "crossed" &&
+                                       !tree_definition.thin_branches) {
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X + 1, position.Y, position.Z),
+                                       tree_definition
+                               );
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X - 1, position.Y, position.Z),
+                                       tree_definition
+                               );
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X, position.Y, position.Z + 1),
+                                       tree_definition
+                               );
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X, position.Y, position.Z - 1),
+                                       tree_definition
+                               );
+                       }
+                       dir = v3f(1, 0, 0);
+                       dir = transposeMatrix(rotation, dir);
+                       position += dir;
+                       break;
+               case 'F':
+                       tree_trunk_placement(
+                               vmanip,
+                               v3f(position.X, position.Y, position.Z),
+                               tree_definition
+                       );
+                       if ((stack_orientation.empty() &&
+                                       tree_definition.trunk_type == "double") ||
+                                       (!stack_orientation.empty() &&
+                                       tree_definition.trunk_type == "double" &&
+                                       !tree_definition.thin_branches)) {
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X +1 , position.Y, position.Z),
+                                       tree_definition
+                               );
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X, position.Y, position.Z + 1),
+                                       tree_definition
+                               );
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X + 1, position.Y, position.Z + 1),
+                                       tree_definition
+                               );
+                       } else if ((stack_orientation.empty() &&
+                                       tree_definition.trunk_type == "crossed") ||
+                                       (!stack_orientation.empty() &&
+                                       tree_definition.trunk_type == "crossed" &&
+                                       !tree_definition.thin_branches)) {
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X + 1, position.Y, position.Z),
+                                       tree_definition
+                               );
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X - 1, position.Y, position.Z),
+                                       tree_definition
+                               );
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X, position.Y, position.Z + 1),
+                                       tree_definition
+                               );
+                               tree_trunk_placement(
+                                       vmanip,
+                                       v3f(position.X, position.Y, position.Z - 1),
+                                       tree_definition
+                               );
+                       } if (!stack_orientation.empty()) {
+                               s16 size = 1;
+                               for (x = -size; x <= size; x++)
+                               for (y = -size; y <= size; y++)
+                               for (z = -size; z <= size; z++) {
+                                       if (abs(x) == size &&
+                                                       abs(y) == size &&
+                                                       abs(z) == size) {
+                                               tree_leaves_placement(
+                                                       vmanip,
+                                                       v3f(position.X + x + 1, position.Y + y,
+                                                                       position.Z + z),
+                                                       ps.next(),
+                                                       tree_definition
+                                               );
+                                               tree_leaves_placement(
+                                                       vmanip,
+                                                       v3f(position.X + x - 1, position.Y + y,
+                                                                       position.Z + z),
+                                                       ps.next(),
+                                                       tree_definition
+                                               );
+                                               tree_leaves_placement(
+                                                       vmanip,v3f(position.X + x, position.Y + y,
+                                                                       position.Z + z + 1),
+                                                       ps.next(),
+                                                       tree_definition
+                                               );
+                                               tree_leaves_placement(
+                                                       vmanip,v3f(position.X + x, position.Y + y,
+                                                                       position.Z + z - 1),
+                                                       ps.next(),
+                                                       tree_definition
+                                               );
+                                       }
+                               }
+                       }
+                       dir = v3f(1, 0, 0);
+                       dir = transposeMatrix(rotation, dir);
+                       position += dir;
+                       break;
+               case 'f':
+                       tree_single_leaves_placement(
+                               vmanip,
+                               v3f(position.X, position.Y, position.Z),
+                               ps.next(),
+                               tree_definition
+                       );
+                       dir = v3f(1, 0, 0);
+                       dir = transposeMatrix(rotation, dir);
+                       position += dir;
+                       break;
+               case 'R':
+                       tree_fruit_placement(
+                               vmanip,
+                               v3f(position.X, position.Y, position.Z),
+                               tree_definition
+                       );
+                       dir = v3f(1, 0, 0);
+                       dir = transposeMatrix(rotation, dir);
+                       position += dir;
+                       break;
+
+               // turtle orientation commands
+               case '[':
+                       stack_orientation.push(rotation);
+                       stack_position.push(position);
+                       break;
+               case ']':
+                       if (stack_orientation.empty())
+                               return UNBALANCED_BRACKETS;
+                       rotation = stack_orientation.top();
+                       stack_orientation.pop();
+                       position = stack_position.top();
+                       stack_position.pop();
+                       break;
+               case '+':
+                       temp_rotation.makeIdentity();
+                       temp_rotation = setRotationAxisRadians(temp_rotation,
+                                       angle_in_radians + angleOffset_in_radians, v3f(0, 0, 1));
+                       rotation *= temp_rotation;
+                       break;
+               case '-':
+                       temp_rotation.makeIdentity();
+                       temp_rotation = setRotationAxisRadians(temp_rotation,
+                                       angle_in_radians + angleOffset_in_radians, v3f(0, 0, -1));
+                       rotation *= temp_rotation;
+                       break;
+               case '&':
+                       temp_rotation.makeIdentity();
+                       temp_rotation = setRotationAxisRadians(temp_rotation,
+                                       angle_in_radians + angleOffset_in_radians, v3f(0, 1, 0));
+                       rotation *= temp_rotation;
+                       break;
+               case '^':
+                       temp_rotation.makeIdentity();
+                       temp_rotation = setRotationAxisRadians(temp_rotation,
+                                       angle_in_radians + angleOffset_in_radians, v3f(0, -1, 0));
+                       rotation *= temp_rotation;
+                       break;
+               case '*':
+                       temp_rotation.makeIdentity();
+                       temp_rotation = setRotationAxisRadians(temp_rotation,
+                                       angle_in_radians, v3f(1, 0, 0));
+                       rotation *= temp_rotation;
+                       break;
+               case '/':
+                       temp_rotation.makeIdentity();
+                       temp_rotation = setRotationAxisRadians(temp_rotation,
+                                       angle_in_radians, v3f(-1, 0, 0));
+                       rotation *= temp_rotation;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return SUCCESS;
+}
+
+
+void tree_node_placement(MMVManip &vmanip, v3f p0, MapNode node)
+{
+       v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+       if (!vmanip.m_area.contains(p1))
+               return;
+       u32 vi = vmanip.m_area.index(p1);
+       if (vmanip.m_data[vi].getContent() != CONTENT_AIR
+                       && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
+               return;
+       vmanip.m_data[vmanip.m_area.index(p1)] = node;
+}
+
+
+void tree_trunk_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
+{
+       v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+       if (!vmanip.m_area.contains(p1))
+               return;
+       u32 vi = vmanip.m_area.index(p1);
+       content_t current_node = vmanip.m_data[vi].getContent();
+       if (current_node != CONTENT_AIR && current_node != CONTENT_IGNORE
+                       && current_node != tree_definition.leavesnode.getContent()
+                       && current_node != tree_definition.leaves2node.getContent()
+                       && current_node != tree_definition.fruitnode.getContent())
+               return;
+       vmanip.m_data[vi] = tree_definition.trunknode;
+}
+
+
+void tree_leaves_placement(MMVManip &vmanip, v3f p0,
+               PseudoRandom ps, TreeDef &tree_definition)
+{
+       MapNode leavesnode = tree_definition.leavesnode;
+       if (ps.range(1, 100) > 100 - tree_definition.leaves2_chance)
+               leavesnode = tree_definition.leaves2node;
+       v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+       if (!vmanip.m_area.contains(p1))
+               return;
+       u32 vi = vmanip.m_area.index(p1);
+       if (vmanip.m_data[vi].getContent() != CONTENT_AIR
+                       && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
+               return;
+       if (tree_definition.fruit_chance > 0) {
+               if (ps.range(1, 100) > 100 - tree_definition.fruit_chance)
+                       vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
+               else
+                       vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
+       } else if (ps.range(1, 100) > 20) {
+               vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
+       }
+}
+
+
+void tree_single_leaves_placement(MMVManip &vmanip, v3f p0,
+               PseudoRandom ps, TreeDef &tree_definition)
+{
+       MapNode leavesnode = tree_definition.leavesnode;
+       if (ps.range(1, 100) > 100 - tree_definition.leaves2_chance)
+               leavesnode = tree_definition.leaves2node;
+       v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+       if (!vmanip.m_area.contains(p1))
+               return;
+       u32 vi = vmanip.m_area.index(p1);
+       if (vmanip.m_data[vi].getContent() != CONTENT_AIR
+                       && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
+               return;
+       vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
+}
+
+
+void tree_fruit_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
+{
+       v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
+       if (!vmanip.m_area.contains(p1))
+               return;
+       u32 vi = vmanip.m_area.index(p1);
+       if (vmanip.m_data[vi].getContent() != CONTENT_AIR
+                       && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
+               return;
+       vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
+}
+
+
+irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis)
+{
+       double c = cos(angle);
+       double s = sin(angle);
+       double t = 1.0 - c;
+
+       double tx  = t * axis.X;
+       double ty  = t * axis.Y;
+       double tz  = t * axis.Z;
+       double sx  = s * axis.X;
+       double sy  = s * axis.Y;
+       double sz  = s * axis.Z;
+
+       M[0] = tx * axis.X + c;
+       M[1] = tx * axis.Y + sz;
+       M[2] = tx * axis.Z - sy;
+
+       M[4] = ty * axis.X - sz;
+       M[5] = ty * axis.Y + c;
+       M[6] = ty * axis.Z + sx;
+
+       M[8]  = tz * axis.X + sy;
+       M[9]  = tz * axis.Y - sx;
+       M[10] = tz * axis.Z + c;
+       return M;
+}
+
+
+v3f transposeMatrix(irr::core::matrix4 M, v3f v)
+{
+       v3f translated;
+       double x = M[0] * v.X + M[4] * v.Y + M[8]  * v.Z +M[12];
+       double y = M[1] * v.X + M[5] * v.Y + M[9]  * v.Z +M[13];
+       double z = M[2] * v.X + M[6] * v.Y + M[10] * v.Z +M[14];
+       translated.X = x;
+       translated.Y = y;
+       translated.Z = z;
+       return translated;
+}
+
+
+void make_jungletree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 seed)
+{
+       /*
+               NOTE: Tree-placing code is currently duplicated in the engine
+               and in games that have saplings; both are deprecated but not
+               replaced yet
+       */
+       content_t c_tree   = ndef->getId("mapgen_jungletree");
+       content_t c_leaves = ndef->getId("mapgen_jungleleaves");
+       if (c_tree == CONTENT_IGNORE)
+               c_tree = ndef->getId("mapgen_tree");
+       if (c_leaves == CONTENT_IGNORE)
+               c_leaves = ndef->getId("mapgen_leaves");
+
+       MapNode treenode(c_tree);
+       MapNode leavesnode(c_leaves);
+
+       PseudoRandom pr(seed);
+       for (s16 x= -1; x <= 1; x++)
+       for (s16 z= -1; z <= 1; z++) {
+               if (pr.range(0, 2) == 0)
+                       continue;
+               v3s16 p1 = p0 + v3s16(x, 0, z);
+               v3s16 p2 = p0 + v3s16(x, -1, z);
+               u32 vi1 = vmanip.m_area.index(p1);
+               u32 vi2 = vmanip.m_area.index(p2);
+
+               if (vmanip.m_area.contains(p2) &&
+                               vmanip.m_data[vi2].getContent() == CONTENT_AIR)
+                       vmanip.m_data[vi2] = treenode;
+               else if (vmanip.m_area.contains(p1) &&
+                               vmanip.m_data[vi1].getContent() == CONTENT_AIR)
+                       vmanip.m_data[vi1] = treenode;
+       }
+       vmanip.m_data[vmanip.m_area.index(p0)] = treenode;
+
+       s16 trunk_h = pr.range(8, 12);
+       v3s16 p1 = p0;
+       for (s16 ii = 0; ii < trunk_h; ii++) {
+               if (vmanip.m_area.contains(p1)) {
+                       u32 vi = vmanip.m_area.index(p1);
+                       vmanip.m_data[vi] = treenode;
+               }
+               p1.Y++;
+       }
+
+       // p1 is now the last piece of the trunk
+       p1.Y -= 1;
+
+       VoxelArea leaves_a(v3s16(-3, -2, -3), v3s16(3, 2, 3));
+       //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++)
+               leaves_d[i] = 0;
+
+       // Force leaves at near the end of the trunk
+       s16 d = 1;
+       for (s16 z = -d; z <= d; z++)
+       for (s16 y = -d; y <= d; y++)
+       for (s16 x = -d; x <= d; x++) {
+               leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
+       }
+
+       // Add leaves randomly
+       for (u32 iii = 0; iii < 30; iii++) {
+               v3s16 p(
+                       pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X - d),
+                       pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y - d),
+                       pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z - d)
+               );
+
+               for (s16 z = 0; z <= d; z++)
+               for (s16 y = 0; y <= d; y++)
+               for (s16 x = 0; x <= d; x++) {
+                       leaves_d[leaves_a.index(p + v3s16(x, y, z))] = 1;
+               }
+       }
+
+       // Blit leaves to vmanip
+       for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
+       for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
+               v3s16 pmin(leaves_a.MinEdge.X, y, z);
+               u32 i = leaves_a.index(pmin);
+               u32 vi = vmanip.m_area.index(pmin + p1);
+               for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
+                       v3s16 p(x, y, z);
+                       if (vmanip.m_area.contains(p + p1) &&
+                                       (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
+                                       vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
+                               if (leaves_d[i] == 1)
+                                       vmanip.m_data[vi] = leavesnode;
+                       }
+                       vi++;
+                       i++;
+               }
+       }
+}
+
+
+void make_pine_tree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 seed)
+{
+       /*
+               NOTE: Tree-placing code is currently duplicated in the engine
+               and in games that have saplings; both are deprecated but not
+               replaced yet
+       */
+       content_t c_tree   = ndef->getId("mapgen_pine_tree");
+       content_t c_leaves = ndef->getId("mapgen_pine_needles");
+       content_t c_snow = ndef->getId("mapgen_snow");
+       if (c_tree == CONTENT_IGNORE)
+               c_tree = ndef->getId("mapgen_tree");
+       if (c_leaves == CONTENT_IGNORE)
+               c_leaves = ndef->getId("mapgen_leaves");
+       if (c_snow == CONTENT_IGNORE)
+               c_snow = CONTENT_AIR;
+
+       MapNode treenode(c_tree);
+       MapNode leavesnode(c_leaves);
+       MapNode snownode(c_snow);
+
+       PseudoRandom pr(seed);
+       u16 trunk_h = pr.range(9, 13);
+       v3s16 p1 = p0;
+       for (u16 ii = 0; ii < trunk_h; ii++) {
+               if (vmanip.m_area.contains(p1)) {
+                       u32 vi = vmanip.m_area.index(p1);
+                       vmanip.m_data[vi] = treenode;
+               }
+               p1.Y++;
+       }
+
+       // Make p1 the top node of the trunk
+       p1.Y -= 1;
+
+       VoxelArea leaves_a(v3s16(-3, -6, -3), v3s16(3, 3, 3));
+       Buffer<u8> leaves_d(leaves_a.getVolume());
+       for (s32 i = 0; i < leaves_a.getVolume(); i++)
+               leaves_d[i] = 0;
+
+       // Upper branches
+       u16 dev = 3;
+       for (s16 yy = -1; yy <= 1; yy++) {
+               for (s16 zz = -dev; zz <= dev; zz++) {
+                       u32 i = leaves_a.index(v3s16(-dev, yy, zz));
+                       u32 ia = leaves_a.index(v3s16(-dev, yy+1, zz));
+                       for (s16 xx = -dev; xx <= dev; xx++) {
+                               if (pr.range(0, 20) <= 19 - dev) {
+                                       leaves_d[i] = 1;
+                                       leaves_d[ia] = 2;
+                               }
+                               i++;
+                               ia++;
+                       }
+               }
+               dev--;
+       }
+
+       // Centre top nodes
+       leaves_d[leaves_a.index(v3s16(0, 1, 0))] = 1;
+       leaves_d[leaves_a.index(v3s16(0, 2, 0))] = 1;
+       leaves_d[leaves_a.index(v3s16(0, 3, 0))] = 2;
+
+       // Lower branches
+       s16 my = -6;
+       for (u32 iii = 0; iii < 20; iii++) {
+               s16 xi = pr.range(-3, 2);
+               s16 yy = pr.range(-6, -5);
+               s16 zi = pr.range(-3, 2);
+               if (yy > my)
+                       my = yy;
+               for (s16 zz = zi; zz <= zi + 1; zz++) {
+                       u32 i = leaves_a.index(v3s16(xi, yy, zz));
+                       u32 ia = leaves_a.index(v3s16(xi, yy + 1, zz));
+                       for (s32 xx = xi; xx <= xi + 1; xx++) {
+                               leaves_d[i] = 1;
+                               if (leaves_d[ia] == 0)
+                                       leaves_d[ia] = 2;
+                               i++;
+                               ia++;
+                       }
+               }
+       }
+
+       dev = 2;
+       for (s16 yy = my + 1; yy <= my + 2; yy++) {
+               for (s16 zz = -dev; zz <= dev; zz++) {
+                       u32 i = leaves_a.index(v3s16(-dev, yy, zz));
+                       u32 ia = leaves_a.index(v3s16(-dev, yy + 1, zz));
+                       for (s16 xx = -dev; xx <= dev; xx++) {
+                               if (pr.range(0, 20) <= 19 - dev) {
+                                       leaves_d[i] = 1;
+                                       leaves_d[ia] = 2;
+                               }
+                               i++;
+                               ia++;
+                       }
+               }
+               dev--;
+       }
+
+       // Blit leaves to vmanip
+       for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
+       for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
+               v3s16 pmin(leaves_a.MinEdge.X, y, z);
+               u32 i = leaves_a.index(pmin);
+               u32 vi = vmanip.m_area.index(pmin + p1);
+               for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
+                       v3s16 p(x, y, z);
+                       if (vmanip.m_area.contains(p + p1) &&
+                                       (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
+                                       vmanip.m_data[vi].getContent() == CONTENT_IGNORE ||
+                                       vmanip.m_data[vi] == snownode)) {
+                               if (leaves_d[i] == 1)
+                                       vmanip.m_data[vi] = leavesnode;
+                               else if (leaves_d[i] == 2)
+                                       vmanip.m_data[vi] = snownode;
+                       }
+                       vi++;
+                       i++;
+               }
+       }
+}
+
+}; // namespace treegen
diff --git a/src/mapgen/treegen.h b/src/mapgen/treegen.h
new file mode 100644 (file)
index 0000000..8e53065
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+Minetest
+Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>,
+                         2012-2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser 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.
+*/
+
+#pragma once
+
+#include <matrix4.h>
+#include "noise.h"
+
+class MMVManip;
+class INodeDefManager;
+class ServerEnvironment;
+
+
+namespace treegen {
+
+       enum error {
+               SUCCESS,
+               UNBALANCED_BRACKETS
+       };
+
+       struct TreeDef {
+               std::string initial_axiom;
+               std::string rules_a;
+               std::string rules_b;
+               std::string rules_c;
+               std::string rules_d;
+
+               MapNode trunknode;
+               MapNode leavesnode;
+               MapNode leaves2node;
+
+               int leaves2_chance;
+               int angle;
+               int iterations;
+               int iterations_random_level;
+               std::string trunk_type;
+               bool thin_branches;
+               MapNode fruitnode;
+               int fruit_chance;
+               s32 seed;
+               bool explicit_seed;
+       };
+
+       // Add default tree
+       void make_tree(MMVManip &vmanip, v3s16 p0,
+               bool is_apple_tree, INodeDefManager *ndef, s32 seed);
+       // Add jungle tree
+       void make_jungletree(MMVManip &vmanip, v3s16 p0,
+               INodeDefManager *ndef, s32 seed);
+       // Add pine tree
+       void make_pine_tree(MMVManip &vmanip, v3s16 p0,
+               INodeDefManager *ndef, s32 seed);
+
+       // Add L-Systems tree (used by engine)
+       treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef,
+               TreeDef tree_definition);
+       // Spawn L-systems tree from LUA
+       treegen::error spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef,
+               const TreeDef &tree_definition);
+
+       // L-System tree gen helper functions
+       void tree_node_placement(MMVManip &vmanip, v3f p0,
+               MapNode node);
+       void tree_trunk_placement(MMVManip &vmanip, v3f p0,
+               TreeDef &tree_definition);
+       void tree_leaves_placement(MMVManip &vmanip, v3f p0,
+               PseudoRandom ps, TreeDef &tree_definition);
+       void tree_single_leaves_placement(MMVManip &vmanip, v3f p0,
+               PseudoRandom ps, TreeDef &tree_definition);
+       void tree_fruit_placement(MMVManip &vmanip, v3f p0,
+               TreeDef &tree_definition);
+       irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis);
+
+       v3f transposeMatrix(irr::core::matrix4 M ,v3f v);
+
+}; // namespace treegen
diff --git a/src/mapgen_carpathian.cpp b/src/mapgen_carpathian.cpp
deleted file mode 100644 (file)
index 9884ce4..0000000
+++ /dev/null
@@ -1,454 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2016 paramat, Matt Gregory
-Copyright (C) 2010-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2017 vlapsley, Vaughan Lapsley <vlapsley@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 <cmath>
-#include "mapgen.h"
-#include "voxel.h"
-#include "noise.h"
-#include "mapblock.h"
-#include "mapnode.h"
-#include "map.h"
-#include "content_sao.h"
-#include "nodedef.h"
-#include "voxelalgorithms.h"
-//#include "profiler.h" // For TimeTaker
-#include "settings.h" // For g_settings
-#include "emerge.h"
-#include "dungeongen.h"
-#include "cavegen.h"
-#include "mg_biome.h"
-#include "mg_ore.h"
-#include "mg_decoration.h"
-#include "mapgen_carpathian.h"
-
-
-FlagDesc flagdesc_mapgen_carpathian[] = {
-       {"caverns", MGCARPATHIAN_CAVERNS},
-       {NULL,      0}
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-MapgenCarpathian::MapgenCarpathian(
-               int mapgenid, MapgenCarpathianParams *params, EmergeManager *emerge)
-       : MapgenBasic(mapgenid, params, emerge)
-{
-       spflags          = params->spflags;
-       cave_width       = params->cave_width;
-       large_cave_depth = params->large_cave_depth;
-       lava_depth       = params->lava_depth;
-       cavern_limit     = params->cavern_limit;
-       cavern_taper     = params->cavern_taper;
-       cavern_threshold = params->cavern_threshold;
-       grad_wl          = 1 - water_level;
-
-       //// 2D Terrain noise
-       noise_base          = new Noise(&params->np_base,          seed, csize.X, csize.Z);
-       noise_filler_depth  = new Noise(&params->np_filler_depth,  seed, csize.X, csize.Z);
-       noise_height1       = new Noise(&params->np_height1,       seed, csize.X, csize.Z);
-       noise_height2       = new Noise(&params->np_height2,       seed, csize.X, csize.Z);
-       noise_height3       = new Noise(&params->np_height3,       seed, csize.X, csize.Z);
-       noise_height4       = new Noise(&params->np_height4,       seed, csize.X, csize.Z);
-       noise_hills_terrain = new Noise(&params->np_hills_terrain, seed, csize.X, csize.Z);
-       noise_ridge_terrain = new Noise(&params->np_ridge_terrain, seed, csize.X, csize.Z);
-       noise_step_terrain  = new Noise(&params->np_step_terrain,  seed, csize.X, csize.Z);
-       noise_hills         = new Noise(&params->np_hills,         seed, csize.X, csize.Z);
-       noise_ridge_mnt     = new Noise(&params->np_ridge_mnt,     seed, csize.X, csize.Z);
-       noise_step_mnt      = new Noise(&params->np_step_mnt,      seed, csize.X, csize.Z);
-
-       //// 3D terrain noise
-       // 1 up 1 down overgeneration
-       noise_mnt_var = new Noise(&params->np_mnt_var, seed, csize.X, csize.Y + 2, csize.Z);
-
-       //// Cave noise
-       MapgenBasic::np_cave1  = params->np_cave1;
-       MapgenBasic::np_cave2  = params->np_cave2;
-       MapgenBasic::np_cavern = params->np_cavern;
-}
-
-
-MapgenCarpathian::~MapgenCarpathian()
-{
-       delete noise_base;
-       delete noise_filler_depth;
-       delete noise_height1;
-       delete noise_height2;
-       delete noise_height3;
-       delete noise_height4;
-       delete noise_hills_terrain;
-       delete noise_ridge_terrain;
-       delete noise_step_terrain;
-       delete noise_hills;
-       delete noise_ridge_mnt;
-       delete noise_step_mnt;
-       delete noise_mnt_var;
-}
-
-
-MapgenCarpathianParams::MapgenCarpathianParams():
-       np_base          (12, 1,  v3f(2557, 2557, 2557), 6538,  4, 0.8,  0.5),
-       np_filler_depth  (0,  1,  v3f(128,  128,  128),  261,   3, 0.7,  2.0),
-       np_height1       (0,  5,  v3f(251,  251,  251),  9613,  5, 0.5,  2.0),
-       np_height2       (0,  5,  v3f(383,  383,  383),  1949,  5, 0.5,  2.0),
-       np_height3       (0,  5,  v3f(509,  509,  509),  3211,  5, 0.5,  2.0),
-       np_height4       (0,  5,  v3f(631,  631,  631),  1583,  5, 0.5,  2.0),
-       np_hills_terrain (1,  1,  v3f(1301, 1301, 1301), 1692,  5, 0.5,  2.0),
-       np_ridge_terrain (1,  1,  v3f(1889, 1889, 1889), 3568,  5, 0.5,  2.0),
-       np_step_terrain  (1,  1,  v3f(1889, 1889, 1889), 4157,  5, 0.5,  2.0),
-       np_hills         (0,  3,  v3f(257,  257,  257),  6604,  6, 0.5,  2.0),
-       np_ridge_mnt     (0,  12, v3f(743,  743,  743),  5520,  6, 0.7,  2.0),
-       np_step_mnt      (0,  8,  v3f(509,  509,  509),  2590,  6, 0.6,  2.0),
-       np_mnt_var       (0,  1,  v3f(499,  499,  499),  2490,  5, 0.55, 2.0),
-       np_cave1         (0,  12, v3f(61,   61,   61),   52534, 3, 0.5,  2.0),
-       np_cave2         (0,  12, v3f(67,   67,   67),   10325, 3, 0.5,  2.0),
-       np_cavern        (0,  1,  v3f(384,  128,  384),  723,   5, 0.63, 2.0)
-{
-}
-
-
-void MapgenCarpathianParams::readParams(const Settings *settings)
-{
-       settings->getFlagStrNoEx("mgcarpathian_spflags", spflags, flagdesc_mapgen_carpathian);
-       settings->getFloatNoEx("mgcarpathian_cave_width",       cave_width);
-       settings->getS16NoEx("mgcarpathian_large_cave_depth",   large_cave_depth);
-       settings->getS16NoEx("mgcarpathian_lava_depth",         lava_depth);
-       settings->getS16NoEx("mgcarpathian_cavern_limit",       cavern_limit);
-       settings->getS16NoEx("mgcarpathian_cavern_taper",       cavern_taper);
-       settings->getFloatNoEx("mgcarpathian_cavern_threshold", cavern_threshold);
-
-       settings->getNoiseParams("mgcarpathian_np_base",          np_base);
-       settings->getNoiseParams("mgcarpathian_np_filler_depth",  np_filler_depth);
-       settings->getNoiseParams("mgcarpathian_np_height1",       np_height1);
-       settings->getNoiseParams("mgcarpathian_np_height2",       np_height2);
-       settings->getNoiseParams("mgcarpathian_np_height3",       np_height3);
-       settings->getNoiseParams("mgcarpathian_np_height4",       np_height4);
-       settings->getNoiseParams("mgcarpathian_np_hills_terrain", np_hills_terrain);
-       settings->getNoiseParams("mgcarpathian_np_ridge_terrain", np_ridge_terrain);
-       settings->getNoiseParams("mgcarpathian_np_step_terrain",  np_step_terrain);
-       settings->getNoiseParams("mgcarpathian_np_hills",         np_hills);
-       settings->getNoiseParams("mgcarpathian_np_ridge_mnt",     np_ridge_mnt);
-       settings->getNoiseParams("mgcarpathian_np_step_mnt",      np_step_mnt);
-       settings->getNoiseParams("mgcarpathian_np_mnt_var",       np_mnt_var);
-       settings->getNoiseParams("mgcarpathian_np_cave1",         np_cave1);
-       settings->getNoiseParams("mgcarpathian_np_cave2",         np_cave2);
-       settings->getNoiseParams("mgcarpathian_np_cavern",        np_cavern);
-}
-
-
-void MapgenCarpathianParams::writeParams(Settings *settings) const
-{
-       settings->setFlagStr("mgcarpathian_spflags", spflags, flagdesc_mapgen_carpathian, U32_MAX);
-       settings->setFloat("mgcarpathian_cave_width",       cave_width);
-       settings->setS16("mgcarpathian_large_cave_depth",   large_cave_depth);
-       settings->setS16("mgcarpathian_lava_depth",         lava_depth);
-       settings->setS16("mgcarpathian_cavern_limit",       cavern_limit);
-       settings->setS16("mgcarpathian_cavern_taper",       cavern_taper);
-       settings->setFloat("mgcarpathian_cavern_threshold", cavern_threshold);
-
-       settings->setNoiseParams("mgcarpathian_np_base",          np_base);
-       settings->setNoiseParams("mgcarpathian_np_filler_depth",  np_filler_depth);
-       settings->setNoiseParams("mgcarpathian_np_height1",       np_height1);
-       settings->setNoiseParams("mgcarpathian_np_height2",       np_height2);
-       settings->setNoiseParams("mgcarpathian_np_height3",       np_height3);
-       settings->setNoiseParams("mgcarpathian_np_height4",       np_height4);
-       settings->setNoiseParams("mgcarpathian_np_hills_terrain", np_hills_terrain);
-       settings->setNoiseParams("mgcarpathian_np_ridge_terrain", np_ridge_terrain);
-       settings->setNoiseParams("mgcarpathian_np_step_terrain",  np_step_terrain);
-       settings->setNoiseParams("mgcarpathian_np_hills",         np_hills);
-       settings->setNoiseParams("mgcarpathian_np_ridge_mnt",     np_ridge_mnt);
-       settings->setNoiseParams("mgcarpathian_np_step_mnt",      np_step_mnt);
-       settings->setNoiseParams("mgcarpathian_np_mnt_var",       np_mnt_var);
-       settings->setNoiseParams("mgcarpathian_np_cave1",         np_cave1);
-       settings->setNoiseParams("mgcarpathian_np_cave2",         np_cave2);
-       settings->setNoiseParams("mgcarpathian_np_cavern",        np_cavern);
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-// Lerp function
-inline float MapgenCarpathian::getLerp(float noise1, float noise2, float mod)
-{
-       return noise1 + mod * (noise2 - noise1);
-}
-
-// Steps function
-float MapgenCarpathian::getSteps(float noise)
-{
-       float w = 0.5f;
-       float k = floor(noise / w);
-       float f = (noise - k * w) / w;
-       float s = std::fmin(2.f * f, 1.f);
-       return (k + s) * w;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-void MapgenCarpathian::makeChunk(BlockMakeData *data)
-{
-       // Pre-conditions
-       assert(data->vmanip);
-       assert(data->nodedef);
-       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
-                       data->blockpos_requested.Y >= data->blockpos_min.Y &&
-                       data->blockpos_requested.Z >= data->blockpos_min.Z);
-       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
-                       data->blockpos_requested.Y <= data->blockpos_max.Y &&
-                       data->blockpos_requested.Z <= data->blockpos_max.Z);
-
-       this->generating = true;
-       this->vm = data->vmanip;
-       this->ndef = data->nodedef;
-
-       v3s16 blockpos_min = data->blockpos_min;
-       v3s16 blockpos_max = data->blockpos_max;
-       node_min = blockpos_min * MAP_BLOCKSIZE;
-       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
-       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-
-       // Create a block-specific seed
-       blockseed = getBlockSeed2(full_node_min, seed);
-
-       // Generate terrain
-       s16 stone_surface_max_y = generateTerrain();
-
-       // Create heightmap
-       updateHeightmap(node_min, node_max);
-
-       // Init biome generator, place biome-specific nodes, and build biomemap
-       biomegen->calcBiomeNoise(node_min);
-
-       MgStoneType mgstone_type;
-       content_t biome_stone;
-       generateBiomes(&mgstone_type, &biome_stone);
-
-       // Generate caverns, tunnels and classic caves
-       if (flags & MG_CAVES) {
-               bool has_cavern = false;
-               // Generate caverns
-               if (spflags & MGCARPATHIAN_CAVERNS)
-                       has_cavern = generateCaverns(stone_surface_max_y);
-               // Generate tunnels and classic caves
-               if (has_cavern)
-                       // Disable classic caves in this mapchunk by setting
-                       // 'large cave depth' to world base. Avoids excessive liquid in
-                       // large caverns and floating blobs of overgenerated liquid.
-                       generateCaves(stone_surface_max_y, -MAX_MAP_GENERATION_LIMIT);
-               else
-                       generateCaves(stone_surface_max_y, large_cave_depth);
-       }
-
-       // Generate dungeons
-       if (flags & MG_DUNGEONS)
-               generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
-
-       // Generate the registered decorations
-       if (flags & MG_DECORATIONS)
-               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
-
-       // Generate the registered ores
-       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
-
-       // Sprinkle some dust on top after everything else was generated
-       dustTopNodes();
-
-       // Update liquids
-       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
-
-       // Calculate lighting
-       if (flags & MG_LIGHT) {
-               calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
-                               full_node_min, full_node_max);
-       }
-
-       this->generating = false;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-int MapgenCarpathian::getSpawnLevelAtPoint(v2s16 p)
-{
-       s16 level_at_point = terrainLevelAtPoint(p.X, p.Y);
-       if (level_at_point <= water_level || level_at_point > water_level + 32)
-               return MAX_MAP_GENERATION_LIMIT; // Unsuitable spawn point
-
-       return level_at_point;
-}
-
-
-float MapgenCarpathian::terrainLevelAtPoint(s16 x, s16 z)
-{
-       float ground = NoisePerlin2D(&noise_base->np, x, z, seed);
-       float height1 = NoisePerlin2D(&noise_height1->np, x, z, seed);
-       float height2 = NoisePerlin2D(&noise_height2->np, x, z, seed);
-       float height3 = NoisePerlin2D(&noise_height3->np, x, z, seed);
-       float height4 = NoisePerlin2D(&noise_height4->np, x, z, seed);
-       float hter = NoisePerlin2D(&noise_hills_terrain->np, x, z, seed);
-       float rter = NoisePerlin2D(&noise_ridge_terrain->np, x, z, seed);
-       float ster = NoisePerlin2D(&noise_step_terrain->np, x, z, seed);
-       float n_hills = NoisePerlin2D(&noise_hills->np, x, z, seed);
-       float n_ridge_mnt = NoisePerlin2D(&noise_ridge_mnt->np, x, z, seed);
-       float n_step_mnt = NoisePerlin2D(&noise_step_mnt->np, x, z, seed);
-
-       int height = -MAX_MAP_GENERATION_LIMIT;
-
-       for (s16 y = 1; y <= 30; y++) {
-               float mnt_var = NoisePerlin3D(&noise_mnt_var->np, x, y, z, seed);
-
-               // Gradient & shallow seabed
-               s32 grad = (y < water_level) ? grad_wl + (water_level - y) * 3 : 1 - y;
-
-               // Hill/Mountain height (hilliness)
-               float hill1 = getLerp(height1, height2, mnt_var);
-               float hill2 = getLerp(height3, height4, mnt_var);
-               float hill3 = getLerp(height3, height2, mnt_var);
-               float hill4 = getLerp(height1, height4, mnt_var);
-               float hilliness = std::fmax(std::fmin(hill1, hill2), std::fmin(hill3, hill4));
-
-               // Rolling hills
-               float hill_mnt = hilliness * pow(n_hills, 2.f);
-               float hills = pow(hter, 3.f) * hill_mnt;
-
-               // Ridged mountains
-               float ridge_mnt = hilliness * (1.f - fabs(n_ridge_mnt));
-               float ridged_mountains = pow(rter, 3.f) * ridge_mnt;
-
-               // Step (terraced) mountains
-               float step_mnt = hilliness * getSteps(n_step_mnt);
-               float step_mountains = pow(ster, 3.f) * step_mnt;
-
-               // Final terrain level
-               float mountains = hills + ridged_mountains + step_mountains;
-               float surface_level = ground + mountains + grad;
-
-               if (y > surface_level && height < 0)
-                       height = y;
-       }
-
-       return height;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-int MapgenCarpathian::generateTerrain()
-{
-       MapNode mn_air(CONTENT_AIR);
-       MapNode mn_stone(c_stone);
-       MapNode mn_water(c_water_source);
-
-       s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
-       u32 index2d = 0;
-       u32 index3d = 0;
-
-       // Calculate noise for terrain generation
-       noise_base->perlinMap2D(node_min.X, node_min.Z);
-       noise_height1->perlinMap2D(node_min.X, node_min.Z);
-       noise_height2->perlinMap2D(node_min.X, node_min.Z);
-       noise_height3->perlinMap2D(node_min.X, node_min.Z);
-       noise_height4->perlinMap2D(node_min.X, node_min.Z);
-       noise_hills_terrain->perlinMap2D(node_min.X, node_min.Z);
-       noise_ridge_terrain->perlinMap2D(node_min.X, node_min.Z);
-       noise_step_terrain->perlinMap2D(node_min.X, node_min.Z);
-       noise_hills->perlinMap2D(node_min.X, node_min.Z);
-       noise_ridge_mnt->perlinMap2D(node_min.X, node_min.Z);
-       noise_step_mnt->perlinMap2D(node_min.X, node_min.Z);
-       noise_mnt_var->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
-
-       //// Place nodes
-       for (s16 z = node_min.Z; z <= node_max.Z; z++) {
-               for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
-                       u32 vi = vm->m_area.index(node_min.X, y, z);
-                       for (s16 x = node_min.X; x <= node_max.X;
-                                       x++, vi++, index2d++, index3d++) {
-                               if (vm->m_data[vi].getContent() != CONTENT_IGNORE)
-                                       continue;
-
-                               // Base terrain
-                               float ground = noise_base->result[index2d];
-
-                               // Gradient & shallow seabed
-                               s32 grad = (y < water_level) ? grad_wl + (water_level - y) * 3 : 1 - y;
-
-                               // Hill/Mountain height (hilliness)
-                               float height1 = noise_height1->result[index2d];
-                               float height2 = noise_height2->result[index2d];
-                               float height3 = noise_height3->result[index2d];
-                               float height4 = noise_height4->result[index2d];
-                               float mnt_var = noise_mnt_var->result[index3d];
-                               // Combine height noises and apply 3D variation
-                               float hill1 = getLerp(height1, height2, mnt_var);
-                               float hill2 = getLerp(height3, height4, mnt_var);
-                               float hill3 = getLerp(height3, height2, mnt_var);
-                               float hill4 = getLerp(height1, height4, mnt_var);
-                               // 'hilliness' determines whether hills/mountains are
-                               // small or large
-                               float hilliness = std::fmax(std::fmin(hill1, hill2), std::fmin(hill3, hill4));
-
-                               // Rolling hills
-                               float hter = noise_hills_terrain->result[index2d];
-                               float n_hills = noise_hills->result[index2d];
-                               float hill_mnt = hilliness * pow(n_hills, 2.f);
-                               float hills = pow(fabs(hter), 3.f) * hill_mnt;
-
-                               // Ridged mountains
-                               float rter = noise_ridge_terrain->result[index2d];
-                               float n_ridge_mnt = noise_ridge_mnt->result[index2d];
-                               float ridge_mnt = hilliness * (1.f - fabs(n_ridge_mnt));
-                               float ridged_mountains = pow(fabs(rter), 3.f) * ridge_mnt;
-
-                               // Step (terraced) mountains
-                               float ster = noise_step_terrain->result[index2d];
-                               float n_step_mnt = noise_step_mnt->result[index2d];
-                               float step_mnt = hilliness * getSteps(n_step_mnt);
-                               float step_mountains = pow(fabs(ster), 3.f) * step_mnt;
-
-                               // Final terrain level
-                               float mountains = hills + ridged_mountains + step_mountains;
-                               float surface_level = ground + mountains + grad;
-
-                               if (y < surface_level) {
-                                       vm->m_data[vi] = mn_stone; // Stone
-                                       if (y > stone_surface_max_y)
-                                               stone_surface_max_y = y;
-                               } else if (y <= water_level) {
-                                       vm->m_data[vi] = mn_water; // Sea water
-                               } else {
-                                       vm->m_data[vi] = mn_air; // Air
-                               }
-                       }
-                       index2d -= ystride;
-               }
-               index2d += ystride;
-       }
-
-       return stone_surface_max_y;
-}
diff --git a/src/mapgen_carpathian.h b/src/mapgen_carpathian.h
deleted file mode 100644 (file)
index 7dbccde..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2016 paramat, Matt Gregory
-Copyright (C) 2010-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2017 vlapsley, Vaughan Lapsley <vlapsley@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "mapgen.h"
-
-///////// Mapgen Carpathian flags
-#define MGCARPATHIAN_CAVERNS 0x01
-
-class BiomeManager;
-
-extern FlagDesc flagdesc_mapgen_carpathian[];
-
-
-struct MapgenCarpathianParams : public MapgenParams
-{
-       u32 spflags            = MGCARPATHIAN_CAVERNS;
-       float cave_width       = 0.09f;
-       s16 large_cave_depth   = -33;
-       s16 lava_depth         = -256;
-       s16 cavern_limit       = -256;
-       s16 cavern_taper       = 256;
-       float cavern_threshold = 0.7f;
-
-       NoiseParams np_base;
-       NoiseParams np_filler_depth;
-       NoiseParams np_height1;
-       NoiseParams np_height2;
-       NoiseParams np_height3;
-       NoiseParams np_height4;
-       NoiseParams np_hills_terrain;
-       NoiseParams np_ridge_terrain;
-       NoiseParams np_step_terrain;
-       NoiseParams np_hills;
-       NoiseParams np_ridge_mnt;
-       NoiseParams np_step_mnt;
-       NoiseParams np_mnt_var;
-       NoiseParams np_cave1;
-       NoiseParams np_cave2;
-       NoiseParams np_cavern;
-
-       MapgenCarpathianParams();
-       ~MapgenCarpathianParams() = default;
-
-       void readParams(const Settings *settings);
-       void writeParams(Settings *settings) const;
-};
-
-class MapgenCarpathian : public MapgenBasic
-{
-public:
-       MapgenCarpathian(int mapgenid, MapgenCarpathianParams *params,
-                       EmergeManager *emerge);
-       ~MapgenCarpathian();
-
-       virtual MapgenType getType() const { return MAPGEN_CARPATHIAN; }
-
-       float getSteps(float noise);
-       inline float getLerp(float noise1, float noise2, float mod);
-
-       virtual void makeChunk(BlockMakeData *data);
-       int getSpawnLevelAtPoint(v2s16 p);
-
-private:
-       s16 large_cave_depth;
-       s32 grad_wl;
-
-       Noise *noise_base;
-       Noise *noise_height1;
-       Noise *noise_height2;
-       Noise *noise_height3;
-       Noise *noise_height4;
-       Noise *noise_hills_terrain;
-       Noise *noise_ridge_terrain;
-       Noise *noise_step_terrain;
-       Noise *noise_hills;
-       Noise *noise_ridge_mnt;
-       Noise *noise_step_mnt;
-       Noise *noise_mnt_var;
-
-       float terrainLevelAtPoint(s16 x, s16 z);
-       int generateTerrain();
-};
diff --git a/src/mapgen_flat.cpp b/src/mapgen_flat.cpp
deleted file mode 100644 (file)
index 57c20d4..0000000
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
-Minetest
-Copyright (C) 2015-2017 paramat
-Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "mapgen.h"
-#include "voxel.h"
-#include "noise.h"
-#include "mapblock.h"
-#include "mapnode.h"
-#include "map.h"
-#include "content_sao.h"
-#include "nodedef.h"
-#include "voxelalgorithms.h"
-//#include "profiler.h" // For TimeTaker
-#include "settings.h" // For g_settings
-#include "emerge.h"
-#include "dungeongen.h"
-#include "cavegen.h"
-#include "mg_biome.h"
-#include "mg_ore.h"
-#include "mg_decoration.h"
-#include "mapgen_flat.h"
-
-
-FlagDesc flagdesc_mapgen_flat[] = {
-       {"lakes", MGFLAT_LAKES},
-       {"hills", MGFLAT_HILLS},
-       {NULL,    0}
-};
-
-///////////////////////////////////////////////////////////////////////////////////////
-
-
-MapgenFlat::MapgenFlat(int mapgenid, MapgenFlatParams *params, EmergeManager *emerge)
-       : MapgenBasic(mapgenid, params, emerge)
-{
-       spflags          = params->spflags;
-       ground_level     = params->ground_level;
-       large_cave_depth = params->large_cave_depth;
-       lava_depth       = params->lava_depth;
-       cave_width       = params->cave_width;
-       lake_threshold   = params->lake_threshold;
-       lake_steepness   = params->lake_steepness;
-       hill_threshold   = params->hill_threshold;
-       hill_steepness   = params->hill_steepness;
-
-       // 2D noise
-       noise_filler_depth = new Noise(&params->np_filler_depth, seed, csize.X, csize.Z);
-
-       if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
-               noise_terrain = new Noise(&params->np_terrain, seed, csize.X, csize.Z);
-       // 3D noise
-       MapgenBasic::np_cave1 = params->np_cave1;
-       MapgenBasic::np_cave2 = params->np_cave2;
-}
-
-
-MapgenFlat::~MapgenFlat()
-{
-       delete noise_filler_depth;
-
-       if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
-               delete noise_terrain;
-}
-
-
-MapgenFlatParams::MapgenFlatParams():
-       np_terrain      (0, 1,   v3f(600, 600, 600), 7244,  5, 0.6, 2.0),
-       np_filler_depth (0, 1.2, v3f(150, 150, 150), 261,   3, 0.7, 2.0),
-       np_cave1        (0, 12,  v3f(61,  61,  61),  52534, 3, 0.5, 2.0),
-       np_cave2        (0, 12,  v3f(67,  67,  67),  10325, 3, 0.5, 2.0)
-{
-}
-
-
-void MapgenFlatParams::readParams(const Settings *settings)
-{
-       settings->getFlagStrNoEx("mgflat_spflags",      spflags, flagdesc_mapgen_flat);
-       settings->getS16NoEx("mgflat_ground_level",     ground_level);
-       settings->getS16NoEx("mgflat_large_cave_depth", large_cave_depth);
-       settings->getS16NoEx("mgflat_lava_depth",       lava_depth);
-       settings->getFloatNoEx("mgflat_cave_width",     cave_width);
-       settings->getFloatNoEx("mgflat_lake_threshold", lake_threshold);
-       settings->getFloatNoEx("mgflat_lake_steepness", lake_steepness);
-       settings->getFloatNoEx("mgflat_hill_threshold", hill_threshold);
-       settings->getFloatNoEx("mgflat_hill_steepness", hill_steepness);
-
-       settings->getNoiseParams("mgflat_np_terrain",      np_terrain);
-       settings->getNoiseParams("mgflat_np_filler_depth", np_filler_depth);
-       settings->getNoiseParams("mgflat_np_cave1",        np_cave1);
-       settings->getNoiseParams("mgflat_np_cave2",        np_cave2);
-}
-
-
-void MapgenFlatParams::writeParams(Settings *settings) const
-{
-       settings->setFlagStr("mgflat_spflags",      spflags, flagdesc_mapgen_flat, U32_MAX);
-       settings->setS16("mgflat_ground_level",     ground_level);
-       settings->setS16("mgflat_large_cave_depth", large_cave_depth);
-       settings->setS16("mgflat_lava_depth",       lava_depth);
-       settings->setFloat("mgflat_cave_width",     cave_width);
-       settings->setFloat("mgflat_lake_threshold", lake_threshold);
-       settings->setFloat("mgflat_lake_steepness", lake_steepness);
-       settings->setFloat("mgflat_hill_threshold", hill_threshold);
-       settings->setFloat("mgflat_hill_steepness", hill_steepness);
-
-       settings->setNoiseParams("mgflat_np_terrain",      np_terrain);
-       settings->setNoiseParams("mgflat_np_filler_depth", np_filler_depth);
-       settings->setNoiseParams("mgflat_np_cave1",        np_cave1);
-       settings->setNoiseParams("mgflat_np_cave2",        np_cave2);
-}
-
-
-/////////////////////////////////////////////////////////////////
-
-
-int MapgenFlat::getSpawnLevelAtPoint(v2s16 p)
-{
-       s16 level_at_point = ground_level;
-       float n_terrain = 0.0f;
-       if ((spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS))
-               n_terrain = NoisePerlin2D(&noise_terrain->np, p.X, p.Y, seed);
-
-       if ((spflags & MGFLAT_LAKES) && n_terrain < lake_threshold) {
-               level_at_point = ground_level -
-                       (lake_threshold - n_terrain) * lake_steepness;
-       } else if ((spflags & MGFLAT_HILLS) && n_terrain > hill_threshold) {
-               level_at_point = ground_level +
-                       (n_terrain - hill_threshold) * hill_steepness;
-       }
-
-       if (ground_level < water_level)  // Ocean world, allow spawn in water
-               return MYMAX(level_at_point, water_level);
-
-       if (level_at_point > water_level)
-               return level_at_point;  // Spawn on land
-
-       return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
-}
-
-
-void MapgenFlat::makeChunk(BlockMakeData *data)
-{
-       // Pre-conditions
-       assert(data->vmanip);
-       assert(data->nodedef);
-       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
-               data->blockpos_requested.Y >= data->blockpos_min.Y &&
-               data->blockpos_requested.Z >= data->blockpos_min.Z);
-       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
-               data->blockpos_requested.Y <= data->blockpos_max.Y &&
-               data->blockpos_requested.Z <= data->blockpos_max.Z);
-
-       this->generating = true;
-       this->vm   = data->vmanip;
-       this->ndef = data->nodedef;
-       //TimeTaker t("makeChunk");
-
-       v3s16 blockpos_min = data->blockpos_min;
-       v3s16 blockpos_max = data->blockpos_max;
-       node_min = blockpos_min * MAP_BLOCKSIZE;
-       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
-       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-
-       blockseed = getBlockSeed2(full_node_min, seed);
-
-       // Generate base terrain, mountains, and ridges with initial heightmaps
-       s16 stone_surface_max_y = generateTerrain();
-
-       // Create heightmap
-       updateHeightmap(node_min, node_max);
-
-       // Init biome generator, place biome-specific nodes, and build biomemap
-       biomegen->calcBiomeNoise(node_min);
-
-       MgStoneType mgstone_type;
-       content_t biome_stone;
-       generateBiomes(&mgstone_type, &biome_stone);
-
-       if (flags & MG_CAVES)
-               generateCaves(stone_surface_max_y, large_cave_depth);
-
-       if (flags & MG_DUNGEONS)
-               generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
-
-       // Generate the registered decorations
-       if (flags & MG_DECORATIONS)
-               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
-
-       // Generate the registered ores
-       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
-
-       // Sprinkle some dust on top after everything else was generated
-       dustTopNodes();
-
-       //printf("makeChunk: %dms\n", t.stop());
-
-       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
-
-       if (flags & MG_LIGHT)
-               calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
-                       full_node_min, full_node_max);
-
-       //setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE,
-       //                      node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF);
-
-       this->generating = false;
-}
-
-
-s16 MapgenFlat::generateTerrain()
-{
-       MapNode n_air(CONTENT_AIR);
-       MapNode n_stone(c_stone);
-       MapNode n_water(c_water_source);
-
-       const v3s16 &em = vm->m_area.getExtent();
-       s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
-       u32 ni2d = 0;
-
-       bool use_noise = (spflags & MGFLAT_LAKES) || (spflags & MGFLAT_HILLS);
-       if (use_noise)
-               noise_terrain->perlinMap2D(node_min.X, node_min.Z);
-
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 x = node_min.X; x <= node_max.X; x++, ni2d++) {
-               s16 stone_level = ground_level;
-               float n_terrain = use_noise ? noise_terrain->result[ni2d] : 0.0f;
-
-               if ((spflags & MGFLAT_LAKES) && n_terrain < lake_threshold) {
-                       s16 depress = (lake_threshold - n_terrain) * lake_steepness;
-                       stone_level = ground_level - depress;
-               } else if ((spflags & MGFLAT_HILLS) && n_terrain > hill_threshold) {
-                       s16 rise = (n_terrain - hill_threshold) * hill_steepness;
-                       stone_level = ground_level + rise;
-               }
-
-               u32 vi = vm->m_area.index(x, node_min.Y - 1, z);
-               for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
-                       if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
-                               if (y <= stone_level) {
-                                       vm->m_data[vi] = n_stone;
-                                       if (y > stone_surface_max_y)
-                                               stone_surface_max_y = y;
-                               } else if (y <= water_level) {
-                                       vm->m_data[vi] = n_water;
-                               } else {
-                                       vm->m_data[vi] = n_air;
-                               }
-                       }
-                       vm->m_area.add_y(em, vi, 1);
-               }
-       }
-
-       return stone_surface_max_y;
-}
diff --git a/src/mapgen_flat.h b/src/mapgen_flat.h
deleted file mode 100644 (file)
index 635d406..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
-Minetest
-Copyright (C) 2015-2017 paramat
-Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "mapgen.h"
-
-/////// Mapgen Flat flags
-#define MGFLAT_LAKES 0x01
-#define MGFLAT_HILLS 0x02
-
-class BiomeManager;
-
-extern FlagDesc flagdesc_mapgen_flat[];
-
-struct MapgenFlatParams : public MapgenParams
-{
-       u32 spflags = 0;
-       s16 ground_level = 8;
-       s16 large_cave_depth = -33;
-       s16 lava_depth = -256;
-       float cave_width = 0.09f;
-       float lake_threshold = -0.45f;
-       float lake_steepness = 48.0f;
-       float hill_threshold = 0.45f;
-       float hill_steepness = 64.0f;
-       NoiseParams np_terrain;
-       NoiseParams np_filler_depth;
-       NoiseParams np_cave1;
-       NoiseParams np_cave2;
-
-       MapgenFlatParams();
-       ~MapgenFlatParams() = default;
-
-       void readParams(const Settings *settings);
-       void writeParams(Settings *settings) const;
-};
-
-class MapgenFlat : public MapgenBasic
-{
-public:
-       MapgenFlat(int mapgenid, MapgenFlatParams *params, EmergeManager *emerge);
-       ~MapgenFlat();
-
-       virtual MapgenType getType() const { return MAPGEN_FLAT; }
-
-       virtual void makeChunk(BlockMakeData *data);
-       int getSpawnLevelAtPoint(v2s16 p);
-       s16 generateTerrain();
-
-private:
-       s16 ground_level;
-       s16 large_cave_depth;
-       float lake_threshold;
-       float lake_steepness;
-       float hill_threshold;
-       float hill_steepness;
-       Noise *noise_terrain;
-};
diff --git a/src/mapgen_fractal.cpp b/src/mapgen_fractal.cpp
deleted file mode 100644 (file)
index b068858..0000000
+++ /dev/null
@@ -1,404 +0,0 @@
-/*
-Minetest
-Copyright (C) 2015-2017 paramat
-Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "mapgen.h"
-#include "voxel.h"
-#include "noise.h"
-#include "mapblock.h"
-#include "mapnode.h"
-#include "map.h"
-#include "content_sao.h"
-#include "nodedef.h"
-#include "voxelalgorithms.h"
-//#include "profiler.h" // For TimeTaker
-#include "settings.h" // For g_settings
-#include "emerge.h"
-#include "dungeongen.h"
-#include "cavegen.h"
-#include "mg_biome.h"
-#include "mg_ore.h"
-#include "mg_decoration.h"
-#include "mapgen_fractal.h"
-
-
-FlagDesc flagdesc_mapgen_fractal[] = {
-       {NULL,    0}
-};
-
-///////////////////////////////////////////////////////////////////////////////////////
-
-
-MapgenFractal::MapgenFractal(int mapgenid, MapgenFractalParams *params, EmergeManager *emerge)
-       : MapgenBasic(mapgenid, params, emerge)
-{
-       spflags          = params->spflags;
-       cave_width       = params->cave_width;
-       large_cave_depth = params->large_cave_depth;
-       lava_depth       = params->lava_depth;
-       fractal          = params->fractal;
-       iterations       = params->iterations;
-       scale            = params->scale;
-       offset           = params->offset;
-       slice_w          = params->slice_w;
-       julia_x          = params->julia_x;
-       julia_y          = params->julia_y;
-       julia_z          = params->julia_z;
-       julia_w          = params->julia_w;
-
-       //// 2D terrain noise
-       noise_seabed       = new Noise(&params->np_seabed, seed, csize.X, csize.Z);
-       noise_filler_depth = new Noise(&params->np_filler_depth, seed, csize.X, csize.Z);
-
-       MapgenBasic::np_cave1 = params->np_cave1;
-       MapgenBasic::np_cave2 = params->np_cave2;
-
-       formula = fractal / 2 + fractal % 2;
-       julia   = fractal % 2 == 0;
-}
-
-
-MapgenFractal::~MapgenFractal()
-{
-       delete noise_seabed;
-       delete noise_filler_depth;
-}
-
-
-MapgenFractalParams::MapgenFractalParams():
-       np_seabed       (-14, 9,   v3f(600, 600, 600), 41900, 5, 0.6, 2.0),
-       np_filler_depth (0,   1.2, v3f(150, 150, 150), 261,   3, 0.7, 2.0),
-       np_cave1        (0,   12,  v3f(61,  61,  61),  52534, 3, 0.5, 2.0),
-       np_cave2        (0,   12,  v3f(67,  67,  67),  10325, 3, 0.5, 2.0)
-{
-}
-
-
-void MapgenFractalParams::readParams(const Settings *settings)
-{
-       settings->getFlagStrNoEx("mgfractal_spflags",      spflags, flagdesc_mapgen_fractal);
-       settings->getFloatNoEx("mgfractal_cave_width",     cave_width);
-       settings->getS16NoEx("mgfractal_large_cave_depth", large_cave_depth);
-       settings->getS16NoEx("mgfractal_lava_depth",       lava_depth);
-       settings->getU16NoEx("mgfractal_fractal",          fractal);
-       settings->getU16NoEx("mgfractal_iterations",       iterations);
-       settings->getV3FNoEx("mgfractal_scale",            scale);
-       settings->getV3FNoEx("mgfractal_offset",           offset);
-       settings->getFloatNoEx("mgfractal_slice_w",        slice_w);
-       settings->getFloatNoEx("mgfractal_julia_x",        julia_x);
-       settings->getFloatNoEx("mgfractal_julia_y",        julia_y);
-       settings->getFloatNoEx("mgfractal_julia_z",        julia_z);
-       settings->getFloatNoEx("mgfractal_julia_w",        julia_w);
-
-       settings->getNoiseParams("mgfractal_np_seabed",       np_seabed);
-       settings->getNoiseParams("mgfractal_np_filler_depth", np_filler_depth);
-       settings->getNoiseParams("mgfractal_np_cave1",        np_cave1);
-       settings->getNoiseParams("mgfractal_np_cave2",        np_cave2);
-}
-
-
-void MapgenFractalParams::writeParams(Settings *settings) const
-{
-       settings->setFlagStr("mgfractal_spflags",      spflags, flagdesc_mapgen_fractal, U32_MAX);
-       settings->setFloat("mgfractal_cave_width",     cave_width);
-       settings->setS16("mgfractal_large_cave_depth", large_cave_depth);
-       settings->setS16("mgfractal_lava_depth",       lava_depth);
-       settings->setU16("mgfractal_fractal",          fractal);
-       settings->setU16("mgfractal_iterations",       iterations);
-       settings->setV3F("mgfractal_scale",            scale);
-       settings->setV3F("mgfractal_offset",           offset);
-       settings->setFloat("mgfractal_slice_w",        slice_w);
-       settings->setFloat("mgfractal_julia_x",        julia_x);
-       settings->setFloat("mgfractal_julia_y",        julia_y);
-       settings->setFloat("mgfractal_julia_z",        julia_z);
-       settings->setFloat("mgfractal_julia_w",        julia_w);
-
-       settings->setNoiseParams("mgfractal_np_seabed",       np_seabed);
-       settings->setNoiseParams("mgfractal_np_filler_depth", np_filler_depth);
-       settings->setNoiseParams("mgfractal_np_cave1",        np_cave1);
-       settings->setNoiseParams("mgfractal_np_cave2",        np_cave2);
-}
-
-
-/////////////////////////////////////////////////////////////////
-
-
-int MapgenFractal::getSpawnLevelAtPoint(v2s16 p)
-{
-       bool solid_below = false;  // Dry solid node is present below to spawn on
-       u8 air_count = 0;  // Consecutive air nodes above the dry solid node
-       s16 seabed_level = NoisePerlin2D(&noise_seabed->np, p.X, p.Y, seed);
-       // Seabed can rise above water_level or might be raised to create dry land
-       s16 search_start = MYMAX(seabed_level, water_level + 1);
-       if (seabed_level > water_level)
-               solid_below = true;
-
-       for (s16 y = search_start; y <= search_start + 128; y++) {
-               if (getFractalAtPoint(p.X, y, p.Y)) {  // Fractal node
-                       solid_below = true;
-                       air_count = 0;
-               } else if (solid_below) {  // Air above solid node
-                       air_count++;
-                       // 3 to account for snowblock dust
-                       if (air_count == 3)
-                               return y - 2;
-               }
-       }
-
-       return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
-}
-
-
-void MapgenFractal::makeChunk(BlockMakeData *data)
-{
-       // Pre-conditions
-       assert(data->vmanip);
-       assert(data->nodedef);
-       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
-               data->blockpos_requested.Y >= data->blockpos_min.Y &&
-               data->blockpos_requested.Z >= data->blockpos_min.Z);
-       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
-               data->blockpos_requested.Y <= data->blockpos_max.Y &&
-               data->blockpos_requested.Z <= data->blockpos_max.Z);
-
-       this->generating = true;
-       this->vm   = data->vmanip;
-       this->ndef = data->nodedef;
-       //TimeTaker t("makeChunk");
-
-       v3s16 blockpos_min = data->blockpos_min;
-       v3s16 blockpos_max = data->blockpos_max;
-       node_min = blockpos_min * MAP_BLOCKSIZE;
-       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
-       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-
-       blockseed = getBlockSeed2(full_node_min, seed);
-
-       // Generate base terrain, mountains, and ridges with initial heightmaps
-       s16 stone_surface_max_y = generateTerrain();
-
-       // Create heightmap
-       updateHeightmap(node_min, node_max);
-
-       // Init biome generator, place biome-specific nodes, and build biomemap
-       biomegen->calcBiomeNoise(node_min);
-
-       MgStoneType mgstone_type;
-       content_t biome_stone;
-       generateBiomes(&mgstone_type, &biome_stone);
-
-       if (flags & MG_CAVES)
-               generateCaves(stone_surface_max_y, large_cave_depth);
-
-       if (flags & MG_DUNGEONS)
-               generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
-
-       // Generate the registered decorations
-       if (flags & MG_DECORATIONS)
-               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
-
-       // Generate the registered ores
-       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
-
-       // Sprinkle some dust on top after everything else was generated
-       dustTopNodes();
-
-       //printf("makeChunk: %dms\n", t.stop());
-
-       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
-
-       if (flags & MG_LIGHT)
-               calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
-                       full_node_min, full_node_max);
-
-       //setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE,
-       //                      node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF);
-
-       this->generating = false;
-}
-
-
-bool MapgenFractal::getFractalAtPoint(s16 x, s16 y, s16 z)
-{
-       float cx, cy, cz, cw, ox, oy, oz, ow;
-
-       if (julia) {  // Julia set
-               cx = julia_x;
-               cy = julia_y;
-               cz = julia_z;
-               cw = julia_w;
-               ox = (float)x / scale.X - offset.X;
-               oy = (float)y / scale.Y - offset.Y;
-               oz = (float)z / scale.Z - offset.Z;
-               ow = slice_w;
-       } else {  // Mandelbrot set
-               cx = (float)x / scale.X - offset.X;
-               cy = (float)y / scale.Y - offset.Y;
-               cz = (float)z / scale.Z - offset.Z;
-               cw = slice_w;
-               ox = 0.0f;
-               oy = 0.0f;
-               oz = 0.0f;
-               ow = 0.0f;
-       }
-
-       float nx = 0.0f;
-       float ny = 0.0f;
-       float nz = 0.0f;
-       float nw = 0.0f;
-
-       for (u16 iter = 0; iter < iterations; iter++) {
-               switch (formula) {
-               default:
-               case 1: // 4D "Roundy"
-                       nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
-                       ny = 2.0f * (ox * oy + oz * ow) + cy;
-                       nz = 2.0f * (ox * oz + oy * ow) + cz;
-                       nw = 2.0f * (ox * ow + oy * oz) + cw;
-                       break;
-               case 2: // 4D "Squarry"
-                       nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
-                       ny = 2.0f * (ox * oy + oz * ow) + cy;
-                       nz = 2.0f * (ox * oz + oy * ow) + cz;
-                       nw = 2.0f * (ox * ow - oy * oz) + cw;
-                       break;
-               case 3: // 4D "Mandy Cousin"
-                       nx = ox * ox - oy * oy - oz * oz + ow * ow + cx;
-                       ny = 2.0f * (ox * oy + oz * ow) + cy;
-                       nz = 2.0f * (ox * oz + oy * ow) + cz;
-                       nw = 2.0f * (ox * ow + oy * oz) + cw;
-                       break;
-               case 4: // 4D "Variation"
-                       nx = ox * ox - oy * oy - oz * oz - ow * ow + cx;
-                       ny = 2.0f * (ox * oy + oz * ow) + cy;
-                       nz = 2.0f * (ox * oz - oy * ow) + cz;
-                       nw = 2.0f * (ox * ow + oy * oz) + cw;
-                       break;
-               case 5: // 3D "Mandelbrot/Mandelbar"
-                       nx = ox * ox - oy * oy - oz * oz + cx;
-                       ny = 2.0f * ox * oy + cy;
-                       nz = -2.0f * ox * oz + cz;
-                       break;
-               case 6: // 3D "Christmas Tree"
-                       // Altering the formula here is necessary to avoid division by zero
-                       if (fabs(oz) < 0.000000001f) {
-                               nx = ox * ox - oy * oy - oz * oz + cx;
-                               ny = 2.0f * oy * ox + cy;
-                               nz = 4.0f * oz * ox + cz;
-                       } else {
-                               float a = (2.0f * ox) / (sqrt(oy * oy + oz * oz));
-                               nx = ox * ox - oy * oy - oz * oz + cx;
-                               ny = a * (oy * oy - oz * oz) + cy;
-                               nz = a * 2.0f * oy * oz + cz;
-                       }
-                       break;
-               case 7: // 3D "Mandelbulb"
-                       if (fabs(oy) < 0.000000001f) {
-                               nx = ox * ox - oz * oz + cx;
-                               ny = cy;
-                               nz = -2.0f * oz * sqrt(ox * ox) + cz;
-                       } else {
-                               float a = 1.0f - (oz * oz) / (ox * ox + oy * oy);
-                               nx = (ox * ox - oy * oy) * a + cx;
-                               ny = 2.0f * ox * oy * a + cy;
-                               nz = -2.0f * oz * sqrt(ox * ox + oy * oy) + cz;
-                       }
-                       break;
-               case 8: // 3D "Cosine Mandelbulb"
-                       if (fabs(oy) < 0.000000001f) {
-                               nx = 2.0f * ox * oz + cx;
-                               ny = 4.0f * oy * oz + cy;
-                               nz = oz * oz - ox * ox - oy * oy + cz;
-                       } else {
-                               float a = (2.0f * oz) / sqrt(ox * ox + oy * oy);
-                               nx = (ox * ox - oy * oy) * a + cx;
-                               ny = 2.0f * ox * oy * a + cy;
-                               nz = oz * oz - ox * ox - oy * oy + cz;
-                       }
-                       break;
-               case 9: // 4D "Mandelbulb"
-                       float rxy = sqrt(ox * ox + oy * oy);
-                       float rxyz = sqrt(ox * ox + oy * oy + oz * oz);
-                       if (fabs(ow) < 0.000000001f && fabs(oz) < 0.000000001f) {
-                               nx = (ox * ox - oy * oy) + cx;
-                               ny = 2.0f * ox * oy + cy;
-                               nz = -2.0f * rxy * oz + cz;
-                               nw = 2.0f * rxyz * ow + cw;
-                       } else {
-                               float a = 1.0f - (ow * ow) / (rxyz * rxyz);
-                               float b = a * (1.0f - (oz * oz) / (rxy * rxy));
-                               nx = (ox * ox - oy * oy) * b + cx;
-                               ny = 2.0f * ox * oy * b + cy;
-                               nz = -2.0f * rxy * oz * a + cz;
-                               nw = 2.0f * rxyz * ow + cw;
-                       }
-                       break;
-               }
-
-               if (nx * nx + ny * ny + nz * nz + nw * nw > 4.0f)
-                       return false;
-
-               ox = nx;
-               oy = ny;
-               oz = nz;
-               ow = nw;
-       }
-
-       return true;
-}
-
-
-s16 MapgenFractal::generateTerrain()
-{
-       MapNode n_air(CONTENT_AIR);
-       MapNode n_stone(c_stone);
-       MapNode n_water(c_water_source);
-
-       s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
-       u32 index2d = 0;
-
-       noise_seabed->perlinMap2D(node_min.X, node_min.Z);
-
-       for (s16 z = node_min.Z; z <= node_max.Z; z++) {
-               for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
-                       u32 vi = vm->m_area.index(node_min.X, y, z);
-                       for (s16 x = node_min.X; x <= node_max.X; x++, vi++, index2d++) {
-                               if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
-                                       s16 seabed_height = noise_seabed->result[index2d];
-
-                                       if (y <= seabed_height || getFractalAtPoint(x, y, z)) {
-                                               vm->m_data[vi] = n_stone;
-                                               if (y > stone_surface_max_y)
-                                                       stone_surface_max_y = y;
-                                       } else if (y <= water_level) {
-                                               vm->m_data[vi] = n_water;
-                                       } else {
-                                               vm->m_data[vi] = n_air;
-                                       }
-                               }
-                       }
-                       index2d -= ystride;
-               }
-               index2d += ystride;
-       }
-
-       return stone_surface_max_y;
-}
diff --git a/src/mapgen_fractal.h b/src/mapgen_fractal.h
deleted file mode 100644 (file)
index be49415..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
-Minetest
-Copyright (C) 2015-2017 paramat
-Copyright (C) 2015-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-
-Fractal formulas from http://www.bugman123.com/Hypercomplex/index.html
-by Paul Nylander, and from http://www.fractalforums.com, thank you.
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "mapgen.h"
-
-class BiomeManager;
-
-extern FlagDesc flagdesc_mapgen_fractal[];
-
-struct MapgenFractalParams : public MapgenParams
-{
-       u32 spflags = 0;
-       float cave_width = 0.09f;
-       s16 large_cave_depth = -33;
-       s16 lava_depth = -256;
-       u16 fractal = 1;
-       u16 iterations = 11;
-       v3f scale = v3f(4096.0, 1024.0, 4096.0);
-       v3f offset = v3f(1.52, 0.0, 0.0);
-       float slice_w = 0.0f;
-       float julia_x = 0.267f;
-       float julia_y = 0.2f;
-       float julia_z = 0.133f;
-       float julia_w = 0.067f;
-       NoiseParams np_seabed;
-       NoiseParams np_filler_depth;
-       NoiseParams np_cave1;
-       NoiseParams np_cave2;
-
-       MapgenFractalParams();
-       ~MapgenFractalParams() = default;
-
-       void readParams(const Settings *settings);
-       void writeParams(Settings *settings) const;
-};
-
-class MapgenFractal : public MapgenBasic
-{
-public:
-       MapgenFractal(int mapgenid, MapgenFractalParams *params, EmergeManager *emerge);
-       ~MapgenFractal();
-
-       virtual MapgenType getType() const { return MAPGEN_FRACTAL; }
-
-       virtual void makeChunk(BlockMakeData *data);
-       int getSpawnLevelAtPoint(v2s16 p);
-       bool getFractalAtPoint(s16 x, s16 y, s16 z);
-       s16 generateTerrain();
-
-private:
-       u16 formula;
-       bool julia;
-
-       s16 large_cave_depth;
-       u16 fractal;
-       u16 iterations;
-       v3f scale;
-       v3f offset;
-       float slice_w;
-       float julia_x;
-       float julia_y;
-       float julia_z;
-       float julia_w;
-       Noise *noise_seabed;
-};
diff --git a/src/mapgen_singlenode.cpp b/src/mapgen_singlenode.cpp
deleted file mode 100644 (file)
index ee8746b..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2015-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "mapgen_singlenode.h"
-#include "voxel.h"
-#include "mapblock.h"
-#include "mapnode.h"
-#include "map.h"
-#include "nodedef.h"
-#include "voxelalgorithms.h"
-#include "emerge.h"
-
-
-MapgenSinglenode::MapgenSinglenode(int mapgenid,
-       MapgenParams *params, EmergeManager *emerge)
-       : Mapgen(mapgenid, params, emerge)
-{
-       flags = params->flags;
-
-       INodeDefManager *ndef = emerge->ndef;
-
-       c_node = ndef->getId("mapgen_singlenode");
-       if (c_node == CONTENT_IGNORE)
-               c_node = CONTENT_AIR;
-
-       MapNode n_node(c_node);
-       set_light = (ndef->get(n_node).sunlight_propagates) ? LIGHT_SUN : 0x00;
-}
-
-
-//////////////////////// Map generator
-
-void MapgenSinglenode::makeChunk(BlockMakeData *data)
-{
-       // Pre-conditions
-       assert(data->vmanip);
-       assert(data->nodedef);
-       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
-               data->blockpos_requested.Y >= data->blockpos_min.Y &&
-               data->blockpos_requested.Z >= data->blockpos_min.Z);
-       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
-               data->blockpos_requested.Y <= data->blockpos_max.Y &&
-               data->blockpos_requested.Z <= data->blockpos_max.Z);
-
-       this->generating = true;
-       this->vm   = data->vmanip;
-       this->ndef = data->nodedef;
-
-       v3s16 blockpos_min = data->blockpos_min;
-       v3s16 blockpos_max = data->blockpos_max;
-
-       // Area of central chunk
-       v3s16 node_min = blockpos_min * MAP_BLOCKSIZE;
-       v3s16 node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-
-       blockseed = getBlockSeed2(node_min, data->seed);
-
-       MapNode n_node(c_node);
-
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 y = node_min.Y; y <= node_max.Y; y++) {
-               u32 i = vm->m_area.index(node_min.X, y, z);
-               for (s16 x = node_min.X; x <= node_max.X; x++) {
-                       if (vm->m_data[i].getContent() == CONTENT_IGNORE)
-                               vm->m_data[i] = n_node;
-                       i++;
-               }
-       }
-
-       // Add top and bottom side of water to transforming_liquid queue
-       updateLiquid(&data->transforming_liquid, node_min, node_max);
-
-       // Set lighting
-       if ((flags & MG_LIGHT) && set_light == LIGHT_SUN)
-               setLighting(LIGHT_SUN, node_min, node_max);
-
-       this->generating = false;
-}
-
-
-int MapgenSinglenode::getSpawnLevelAtPoint(v2s16 p)
-{
-       return 0;
-}
diff --git a/src/mapgen_singlenode.h b/src/mapgen_singlenode.h
deleted file mode 100644 (file)
index 7678613..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2015-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "mapgen.h"
-
-struct MapgenSinglenodeParams : public MapgenParams
-{
-       MapgenSinglenodeParams() = default;
-       ~MapgenSinglenodeParams() = default;
-
-       void readParams(const Settings *settings) {}
-       void writeParams(Settings *settings) const {}
-};
-
-class MapgenSinglenode : public Mapgen
-{
-public:
-       u32 flags;
-       content_t c_node;
-       u8 set_light;
-
-       MapgenSinglenode(int mapgenid, MapgenParams *params, EmergeManager *emerge);
-       ~MapgenSinglenode() = default;
-
-       virtual MapgenType getType() const { return MAPGEN_SINGLENODE; }
-
-       void makeChunk(BlockMakeData *data);
-       int getSpawnLevelAtPoint(v2s16 p);
-};
diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp
deleted file mode 100644 (file)
index 5f54872..0000000
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014-2017 paramat
-Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "mapgen.h"
-#include "voxel.h"
-#include "noise.h"
-#include "mapblock.h"
-#include "mapnode.h"
-#include "map.h"
-#include "content_sao.h"
-#include "nodedef.h"
-#include "voxelalgorithms.h"
-//#include "profiler.h" // For TimeTaker
-#include "settings.h" // For g_settings
-#include "emerge.h"
-#include "dungeongen.h"
-#include "cavegen.h"
-#include "mg_biome.h"
-#include "mg_ore.h"
-#include "mg_decoration.h"
-#include "mapgen_v5.h"
-
-
-FlagDesc flagdesc_mapgen_v5[] = {
-       {"caverns", MGV5_CAVERNS},
-       {NULL,      0}
-};
-
-
-MapgenV5::MapgenV5(int mapgenid, MapgenV5Params *params, EmergeManager *emerge)
-       : MapgenBasic(mapgenid, params, emerge)
-{
-       spflags          = params->spflags;
-       cave_width       = params->cave_width;
-       large_cave_depth = params->large_cave_depth;
-       lava_depth       = params->lava_depth;
-       cavern_limit     = params->cavern_limit;
-       cavern_taper     = params->cavern_taper;
-       cavern_threshold = params->cavern_threshold;
-
-       // Terrain noise
-       noise_filler_depth = new Noise(&params->np_filler_depth, seed, csize.X, csize.Z);
-       noise_factor       = new Noise(&params->np_factor,       seed, csize.X, csize.Z);
-       noise_height       = new Noise(&params->np_height,       seed, csize.X, csize.Z);
-
-       // 3D terrain noise
-       // 1-up 1-down overgeneration
-       noise_ground = new Noise(&params->np_ground, seed, csize.X, csize.Y + 2, csize.Z);
-       // 1 down overgeneration
-       MapgenBasic::np_cave1  = params->np_cave1;
-       MapgenBasic::np_cave2  = params->np_cave2;
-       MapgenBasic::np_cavern = params->np_cavern;
-}
-
-
-MapgenV5::~MapgenV5()
-{
-       delete noise_filler_depth;
-       delete noise_factor;
-       delete noise_height;
-       delete noise_ground;
-}
-
-
-MapgenV5Params::MapgenV5Params():
-       np_filler_depth (0, 1,  v3f(150, 150, 150), 261,    4, 0.7,  2.0),
-       np_factor       (0, 1,  v3f(250, 250, 250), 920381, 3, 0.45, 2.0),
-       np_height       (0, 10, v3f(250, 250, 250), 84174,  4, 0.5,  2.0),
-       np_ground       (0, 40, v3f(80,  80,  80),  983240, 4, 0.55, 2.0, NOISE_FLAG_EASED),
-       np_cave1        (0, 12, v3f(50,  50,  50),  52534,  4, 0.5,  2.0),
-       np_cave2        (0, 12, v3f(50,  50,  50),  10325,  4, 0.5,  2.0),
-       np_cavern       (0, 1,  v3f(384, 128, 384), 723,    5, 0.63, 2.0)
-{
-}
-
-
-void MapgenV5Params::readParams(const Settings *settings)
-{
-       settings->getFlagStrNoEx("mgv5_spflags",        spflags, flagdesc_mapgen_v5);
-       settings->getFloatNoEx("mgv5_cave_width",       cave_width);
-       settings->getS16NoEx("mgv5_large_cave_depth",   large_cave_depth);
-       settings->getS16NoEx("mgv5_lava_depth",         lava_depth);
-       settings->getS16NoEx("mgv5_cavern_limit",       cavern_limit);
-       settings->getS16NoEx("mgv5_cavern_taper",       cavern_taper);
-       settings->getFloatNoEx("mgv5_cavern_threshold", cavern_threshold);
-
-       settings->getNoiseParams("mgv5_np_filler_depth", np_filler_depth);
-       settings->getNoiseParams("mgv5_np_factor",       np_factor);
-       settings->getNoiseParams("mgv5_np_height",       np_height);
-       settings->getNoiseParams("mgv5_np_ground",       np_ground);
-       settings->getNoiseParams("mgv5_np_cave1",        np_cave1);
-       settings->getNoiseParams("mgv5_np_cave2",        np_cave2);
-       settings->getNoiseParams("mgv5_np_cavern",       np_cavern);
-}
-
-
-void MapgenV5Params::writeParams(Settings *settings) const
-{
-       settings->setFlagStr("mgv5_spflags",        spflags, flagdesc_mapgen_v5, U32_MAX);
-       settings->setFloat("mgv5_cave_width",       cave_width);
-       settings->setS16("mgv5_large_cave_depth",   large_cave_depth);
-       settings->setS16("mgv5_lava_depth",         lava_depth);
-       settings->setS16("mgv5_cavern_limit",       cavern_limit);
-       settings->setS16("mgv5_cavern_taper",       cavern_taper);
-       settings->setFloat("mgv5_cavern_threshold", cavern_threshold);
-
-       settings->setNoiseParams("mgv5_np_filler_depth", np_filler_depth);
-       settings->setNoiseParams("mgv5_np_factor",       np_factor);
-       settings->setNoiseParams("mgv5_np_height",       np_height);
-       settings->setNoiseParams("mgv5_np_ground",       np_ground);
-       settings->setNoiseParams("mgv5_np_cave1",        np_cave1);
-       settings->setNoiseParams("mgv5_np_cave2",        np_cave2);
-       settings->setNoiseParams("mgv5_np_cavern",       np_cavern);
-}
-
-
-int MapgenV5::getSpawnLevelAtPoint(v2s16 p)
-{
-
-       float f = 0.55 + NoisePerlin2D(&noise_factor->np, p.X, p.Y, seed);
-       if (f < 0.01)
-               f = 0.01;
-       else if (f >= 1.0)
-               f *= 1.6;
-       float h = NoisePerlin2D(&noise_height->np, p.X, p.Y, seed);
-
-       // noise_height 'offset' is the average level of terrain. At least 50% of
-       // terrain will be below this.
-       // Raising the maximum spawn level above 'water_level + 16' is necessary
-       // for when noise_height 'offset' is set much higher than water_level.
-       s16 max_spawn_y = MYMAX(noise_height->np.offset, water_level + 16);
-
-       // Starting spawn search at max_spawn_y + 128 ensures 128 nodes of open
-       // space above spawn position. Avoids spawning in possibly sealed voids.
-       for (s16 y = max_spawn_y + 128; y >= water_level; y--) {
-               float n_ground = NoisePerlin3D(&noise_ground->np, p.X, y, p.Y, seed);
-
-               if (n_ground * f > y - h) {  // If solid
-                       if (y < water_level || y > max_spawn_y)
-                               return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
-
-                       // y + 2 because y is surface and due to biome 'dust' nodes.
-                       return y + 2;
-               }
-       }
-       // Unsuitable spawn position, no ground found
-       return MAX_MAP_GENERATION_LIMIT;
-}
-
-
-void MapgenV5::makeChunk(BlockMakeData *data)
-{
-       // Pre-conditions
-       assert(data->vmanip);
-       assert(data->nodedef);
-       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
-               data->blockpos_requested.Y >= data->blockpos_min.Y &&
-               data->blockpos_requested.Z >= data->blockpos_min.Z);
-       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
-               data->blockpos_requested.Y <= data->blockpos_max.Y &&
-               data->blockpos_requested.Z <= data->blockpos_max.Z);
-
-       this->generating = true;
-       this->vm   = data->vmanip;
-       this->ndef = data->nodedef;
-       //TimeTaker t("makeChunk");
-
-       v3s16 blockpos_min = data->blockpos_min;
-       v3s16 blockpos_max = data->blockpos_max;
-       node_min = blockpos_min * MAP_BLOCKSIZE;
-       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
-       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-
-       // Create a block-specific seed
-       blockseed = getBlockSeed2(full_node_min, seed);
-
-       // Generate base terrain
-       s16 stone_surface_max_y = generateBaseTerrain();
-
-       // Create heightmap
-       updateHeightmap(node_min, node_max);
-
-       // Init biome generator, place biome-specific nodes, and build biomemap
-       biomegen->calcBiomeNoise(node_min);
-
-       MgStoneType mgstone_type;
-       content_t biome_stone;
-       generateBiomes(&mgstone_type, &biome_stone);
-
-       // Generate caverns, tunnels and classic caves
-       if (flags & MG_CAVES) {
-               bool near_cavern = false;
-               // Generate caverns
-               if (spflags & MGV5_CAVERNS)
-                       near_cavern = generateCaverns(stone_surface_max_y);
-               // Generate tunnels and classic caves
-               if (near_cavern)
-                       // Disable classic caves in this mapchunk by setting
-                       // 'large cave depth' to world base. Avoids excessive liquid in
-                       // large caverns and floating blobs of overgenerated liquid.
-                       generateCaves(stone_surface_max_y, -MAX_MAP_GENERATION_LIMIT);
-               else
-                       generateCaves(stone_surface_max_y, large_cave_depth);
-       }
-
-       // Generate dungeons and desert temples
-       if (flags & MG_DUNGEONS)
-               generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
-
-       // Generate the registered decorations
-       if (flags & MG_DECORATIONS)
-               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
-
-       // Generate the registered ores
-       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
-
-       // Sprinkle some dust on top after everything else was generated
-       dustTopNodes();
-
-       //printf("makeChunk: %dms\n", t.stop());
-
-       // Add top and bottom side of water to transforming_liquid queue
-       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
-
-       // Calculate lighting
-       if (flags & MG_LIGHT) {
-               calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
-                       full_node_min, full_node_max);
-       }
-
-       this->generating = false;
-}
-
-
-int MapgenV5::generateBaseTerrain()
-{
-       u32 index = 0;
-       u32 index2d = 0;
-       int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
-
-       noise_factor->perlinMap2D(node_min.X, node_min.Z);
-       noise_height->perlinMap2D(node_min.X, node_min.Z);
-       noise_ground->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
-
-       for (s16 z=node_min.Z; z<=node_max.Z; z++) {
-               for (s16 y=node_min.Y - 1; y<=node_max.Y + 1; y++) {
-                       u32 vi = vm->m_area.index(node_min.X, y, z);
-                       for (s16 x=node_min.X; x<=node_max.X; x++, vi++, index++, index2d++) {
-                               if (vm->m_data[vi].getContent() != CONTENT_IGNORE)
-                                       continue;
-
-                               float f = 0.55 + noise_factor->result[index2d];
-                               if (f < 0.01)
-                                       f = 0.01;
-                               else if (f >= 1.0)
-                                       f *= 1.6;
-                               float h = noise_height->result[index2d];
-
-                               if (noise_ground->result[index] * f < y - h) {
-                                       if (y <= water_level)
-                                               vm->m_data[vi] = MapNode(c_water_source);
-                                       else
-                                               vm->m_data[vi] = MapNode(CONTENT_AIR);
-                               } else {
-                                       vm->m_data[vi] = MapNode(c_stone);
-                                       if (y > stone_surface_max_y)
-                                               stone_surface_max_y = y;
-                               }
-                       }
-                       index2d -= ystride;
-               }
-               index2d += ystride;
-       }
-
-       return stone_surface_max_y;
-}
diff --git a/src/mapgen_v5.h b/src/mapgen_v5.h
deleted file mode 100644 (file)
index 44b0a09..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014-2017 paramat
-Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "mapgen.h"
-
-///////// Mapgen V5 flags
-#define MGV5_CAVERNS 0x01
-
-class BiomeManager;
-
-extern FlagDesc flagdesc_mapgen_v5[];
-
-struct MapgenV5Params : public MapgenParams
-{
-       u32 spflags = MGV5_CAVERNS;
-       float cave_width = 0.125f;
-       s16 large_cave_depth = -256;
-       s16 lava_depth = -256;
-       s16 cavern_limit = -256;
-       s16 cavern_taper = 256;
-       float cavern_threshold = 0.7f;
-
-       NoiseParams np_filler_depth;
-       NoiseParams np_factor;
-       NoiseParams np_height;
-       NoiseParams np_ground;
-       NoiseParams np_cave1;
-       NoiseParams np_cave2;
-       NoiseParams np_cavern;
-
-       MapgenV5Params();
-       ~MapgenV5Params() = default;
-
-       void readParams(const Settings *settings);
-       void writeParams(Settings *settings) const;
-};
-
-class MapgenV5 : public MapgenBasic
-{
-public:
-       MapgenV5(int mapgenid, MapgenV5Params *params, EmergeManager *emerge);
-       ~MapgenV5();
-
-       virtual MapgenType getType() const { return MAPGEN_V5; }
-
-       virtual void makeChunk(BlockMakeData *data);
-       int getSpawnLevelAtPoint(v2s16 p);
-       int generateBaseTerrain();
-
-private:
-       s16 large_cave_depth;
-       Noise *noise_factor;
-       Noise *noise_height;
-       Noise *noise_ground;
-};
diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp
deleted file mode 100644 (file)
index 6dcf834..0000000
+++ /dev/null
@@ -1,1123 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2014-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "mapgen.h"
-#include "voxel.h"
-#include "noise.h"
-#include "mapblock.h"
-#include "mapnode.h"
-#include "map.h"
-//#include "serverobject.h"
-#include "content_sao.h"
-#include "nodedef.h"
-#include "voxelalgorithms.h"
-//#include "profiler.h" // For TimeTaker
-#include "settings.h" // For g_settings
-#include "emerge.h"
-#include "dungeongen.h"
-#include "cavegen.h"
-#include "treegen.h"
-#include "mg_ore.h"
-#include "mg_decoration.h"
-#include "mapgen_v6.h"
-
-
-FlagDesc flagdesc_mapgen_v6[] = {
-       {"jungles",    MGV6_JUNGLES},
-       {"biomeblend", MGV6_BIOMEBLEND},
-       {"mudflow",    MGV6_MUDFLOW},
-       {"snowbiomes", MGV6_SNOWBIOMES},
-       {"flat",       MGV6_FLAT},
-       {"trees",      MGV6_TREES},
-       {NULL,         0}
-};
-
-
-/////////////////////////////////////////////////////////////////////////////
-
-
-MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge)
-       : Mapgen(mapgenid, params, emerge)
-{
-       m_emerge = emerge;
-       ystride = csize.X; //////fix this
-
-       heightmap = new s16[csize.X * csize.Z];
-
-       spflags     = params->spflags;
-       freq_desert = params->freq_desert;
-       freq_beach  = params->freq_beach;
-
-       np_cave        = &params->np_cave;
-       np_humidity    = &params->np_humidity;
-       np_trees       = &params->np_trees;
-       np_apple_trees = &params->np_apple_trees;
-
-       //// Create noise objects
-       noise_terrain_base   = new Noise(&params->np_terrain_base,   seed, csize.X, csize.Y);
-       noise_terrain_higher = new Noise(&params->np_terrain_higher, seed, csize.X, csize.Y);
-       noise_steepness      = new Noise(&params->np_steepness,      seed, csize.X, csize.Y);
-       noise_height_select  = new Noise(&params->np_height_select,  seed, csize.X, csize.Y);
-       noise_mud            = new Noise(&params->np_mud,            seed, csize.X, csize.Y);
-       noise_beach          = new Noise(&params->np_beach,          seed, csize.X, csize.Y);
-       noise_biome          = new Noise(&params->np_biome,          seed,
-                       csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
-       noise_humidity       = new Noise(&params->np_humidity,       seed,
-                       csize.X + 2 * MAP_BLOCKSIZE, csize.Y + 2 * MAP_BLOCKSIZE);
-
-       //// Resolve nodes to be used
-       INodeDefManager *ndef = emerge->ndef;
-
-       c_stone           = ndef->getId("mapgen_stone");
-       c_dirt            = ndef->getId("mapgen_dirt");
-       c_dirt_with_grass = ndef->getId("mapgen_dirt_with_grass");
-       c_sand            = ndef->getId("mapgen_sand");
-       c_water_source    = ndef->getId("mapgen_water_source");
-       c_lava_source     = ndef->getId("mapgen_lava_source");
-       c_gravel          = ndef->getId("mapgen_gravel");
-       c_desert_stone    = ndef->getId("mapgen_desert_stone");
-       c_desert_sand     = ndef->getId("mapgen_desert_sand");
-       c_dirt_with_snow  = ndef->getId("mapgen_dirt_with_snow");
-       c_snow            = ndef->getId("mapgen_snow");
-       c_snowblock       = ndef->getId("mapgen_snowblock");
-       c_ice             = ndef->getId("mapgen_ice");
-
-       if (c_gravel == CONTENT_IGNORE)
-               c_gravel = c_stone;
-       if (c_desert_stone == CONTENT_IGNORE)
-               c_desert_stone = c_stone;
-       if (c_desert_sand == CONTENT_IGNORE)
-               c_desert_sand = c_sand;
-       if (c_dirt_with_snow == CONTENT_IGNORE)
-               c_dirt_with_snow = c_dirt_with_grass;
-       if (c_snow == CONTENT_IGNORE)
-               c_snow = CONTENT_AIR;
-       if (c_snowblock == CONTENT_IGNORE)
-               c_snowblock = c_dirt_with_grass;
-       if (c_ice == CONTENT_IGNORE)
-               c_ice = c_water_source;
-
-       c_cobble             = ndef->getId("mapgen_cobble");
-       c_mossycobble        = ndef->getId("mapgen_mossycobble");
-       c_stair_cobble       = ndef->getId("mapgen_stair_cobble");
-       c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone");
-
-       if (c_mossycobble == CONTENT_IGNORE)
-               c_mossycobble = c_cobble;
-       if (c_stair_cobble == CONTENT_IGNORE)
-               c_stair_cobble = c_cobble;
-       if (c_stair_desert_stone == CONTENT_IGNORE)
-               c_stair_desert_stone = c_desert_stone;
-}
-
-
-MapgenV6::~MapgenV6()
-{
-       delete noise_terrain_base;
-       delete noise_terrain_higher;
-       delete noise_steepness;
-       delete noise_height_select;
-       delete noise_mud;
-       delete noise_beach;
-       delete noise_biome;
-       delete noise_humidity;
-
-       delete[] heightmap;
-}
-
-
-MapgenV6Params::MapgenV6Params():
-       np_terrain_base   (-4,   20.0, v3f(250.0, 250.0, 250.0), 82341,  5, 0.6,  2.0),
-       np_terrain_higher (20,   16.0, v3f(500.0, 500.0, 500.0), 85039,  5, 0.6,  2.0),
-       np_steepness      (0.85, 0.5,  v3f(125.0, 125.0, 125.0), -932,   5, 0.7,  2.0),
-       np_height_select  (0,    1.0,  v3f(250.0, 250.0, 250.0), 4213,   5, 0.69, 2.0),
-       np_mud            (4,    2.0,  v3f(200.0, 200.0, 200.0), 91013,  3, 0.55, 2.0),
-       np_beach          (0,    1.0,  v3f(250.0, 250.0, 250.0), 59420,  3, 0.50, 2.0),
-       np_biome          (0,    1.0,  v3f(500.0, 500.0, 500.0), 9130,   3, 0.50, 2.0),
-       np_cave           (6,    6.0,  v3f(250.0, 250.0, 250.0), 34329,  3, 0.50, 2.0),
-       np_humidity       (0.5,  0.5,  v3f(500.0, 500.0, 500.0), 72384,  3, 0.50, 2.0),
-       np_trees          (0,    1.0,  v3f(125.0, 125.0, 125.0), 2,      4, 0.66, 2.0),
-       np_apple_trees    (0,    1.0,  v3f(100.0, 100.0, 100.0), 342902, 3, 0.45, 2.0)
-{
-}
-
-
-void MapgenV6Params::readParams(const Settings *settings)
-{
-       settings->getFlagStrNoEx("mgv6_spflags", spflags, flagdesc_mapgen_v6);
-       settings->getFloatNoEx("mgv6_freq_desert", freq_desert);
-       settings->getFloatNoEx("mgv6_freq_beach",  freq_beach);
-
-       settings->getNoiseParams("mgv6_np_terrain_base",   np_terrain_base);
-       settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
-       settings->getNoiseParams("mgv6_np_steepness",      np_steepness);
-       settings->getNoiseParams("mgv6_np_height_select",  np_height_select);
-       settings->getNoiseParams("mgv6_np_mud",            np_mud);
-       settings->getNoiseParams("mgv6_np_beach",          np_beach);
-       settings->getNoiseParams("mgv6_np_biome",          np_biome);
-       settings->getNoiseParams("mgv6_np_cave",           np_cave);
-       settings->getNoiseParams("mgv6_np_humidity",       np_humidity);
-       settings->getNoiseParams("mgv6_np_trees",          np_trees);
-       settings->getNoiseParams("mgv6_np_apple_trees",    np_apple_trees);
-}
-
-
-void MapgenV6Params::writeParams(Settings *settings) const
-{
-       settings->setFlagStr("mgv6_spflags", spflags, flagdesc_mapgen_v6, U32_MAX);
-       settings->setFloat("mgv6_freq_desert", freq_desert);
-       settings->setFloat("mgv6_freq_beach",  freq_beach);
-
-       settings->setNoiseParams("mgv6_np_terrain_base",   np_terrain_base);
-       settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher);
-       settings->setNoiseParams("mgv6_np_steepness",      np_steepness);
-       settings->setNoiseParams("mgv6_np_height_select",  np_height_select);
-       settings->setNoiseParams("mgv6_np_mud",            np_mud);
-       settings->setNoiseParams("mgv6_np_beach",          np_beach);
-       settings->setNoiseParams("mgv6_np_biome",          np_biome);
-       settings->setNoiseParams("mgv6_np_cave",           np_cave);
-       settings->setNoiseParams("mgv6_np_humidity",       np_humidity);
-       settings->setNoiseParams("mgv6_np_trees",          np_trees);
-       settings->setNoiseParams("mgv6_np_apple_trees",    np_apple_trees);
-}
-
-
-//////////////////////// Some helper functions for the map generator
-
-// Returns Y one under area minimum if not found
-s16 MapgenV6::find_stone_level(v2s16 p2d)
-{
-       const v3s16 &em = vm->m_area.getExtent();
-       s16 y_nodes_max = vm->m_area.MaxEdge.Y;
-       s16 y_nodes_min = vm->m_area.MinEdge.Y;
-       u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
-       s16 y;
-
-       for (y = y_nodes_max; y >= y_nodes_min; y--) {
-               content_t c = vm->m_data[i].getContent();
-               if (c != CONTENT_IGNORE && (c == c_stone || c == c_desert_stone))
-                       break;
-
-               vm->m_area.add_y(em, i, -1);
-       }
-       return (y >= y_nodes_min) ? y : y_nodes_min - 1;
-}
-
-
-// Required by mapgen.h
-bool MapgenV6::block_is_underground(u64 seed, v3s16 blockpos)
-{
-       /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level(
-                       seed, v2s16(blockpos.X, blockpos.Z));*/
-       // Nah, this is just a heuristic, just return something
-       s16 minimum_groundlevel = water_level;
-
-       if(blockpos.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel)
-               return true;
-
-       return false;
-}
-
-
-//////////////////////// Base terrain height functions
-
-float MapgenV6::baseTerrainLevel(float terrain_base, float terrain_higher,
-       float steepness, float height_select)
-{
-       float base   = 1 + terrain_base;
-       float higher = 1 + terrain_higher;
-
-       // Limit higher ground level to at least base
-       if(higher < base)
-               higher = base;
-
-       // Steepness factor of cliffs
-       float b = steepness;
-       b = rangelim(b, 0.0, 1000.0);
-       b = 5 * b * b * b * b * b * b * b;
-       b = rangelim(b, 0.5, 1000.0);
-
-       // Values 1.5...100 give quite horrible looking slopes
-       if (b > 1.5 && b < 100.0)
-               b = (b < 10.0) ? 1.5 : 100.0;
-
-       float a_off = -0.20; // Offset to more low
-       float a = 0.5 + b * (a_off + height_select);
-       a = rangelim(a, 0.0, 1.0); // Limit
-
-       return base * (1.0 - a) + higher * a;
-}
-
-
-float MapgenV6::baseTerrainLevelFromNoise(v2s16 p)
-{
-       if (spflags & MGV6_FLAT)
-               return water_level;
-
-       float terrain_base   = NoisePerlin2D_PO(&noise_terrain_base->np,
-                                                       p.X, 0.5, p.Y, 0.5, seed);
-       float terrain_higher = NoisePerlin2D_PO(&noise_terrain_higher->np,
-                                                       p.X, 0.5, p.Y, 0.5, seed);
-       float steepness      = NoisePerlin2D_PO(&noise_steepness->np,
-                                                       p.X, 0.5, p.Y, 0.5, seed);
-       float height_select  = NoisePerlin2D_PO(&noise_height_select->np,
-                                                       p.X, 0.5, p.Y, 0.5, seed);
-
-       return baseTerrainLevel(terrain_base, terrain_higher,
-                                                       steepness, height_select);
-}
-
-
-float MapgenV6::baseTerrainLevelFromMap(v2s16 p)
-{
-       int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
-       return baseTerrainLevelFromMap(index);
-}
-
-
-float MapgenV6::baseTerrainLevelFromMap(int index)
-{
-       if (spflags & MGV6_FLAT)
-               return water_level;
-
-       float terrain_base   = noise_terrain_base->result[index];
-       float terrain_higher = noise_terrain_higher->result[index];
-       float steepness      = noise_steepness->result[index];
-       float height_select  = noise_height_select->result[index];
-
-       return baseTerrainLevel(terrain_base, terrain_higher,
-                                                       steepness, height_select);
-}
-
-
-s16 MapgenV6::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision)
-{
-       return baseTerrainLevelFromNoise(p2d) + MGV6_AVERAGE_MUD_AMOUNT;
-}
-
-
-int MapgenV6::getGroundLevelAtPoint(v2s16 p)
-{
-       return baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
-}
-
-
-int MapgenV6::getSpawnLevelAtPoint(v2s16 p)
-{
-       s16 level_at_point = baseTerrainLevelFromNoise(p) + MGV6_AVERAGE_MUD_AMOUNT;
-       if (level_at_point <= water_level ||
-                       level_at_point > water_level + 16)
-               return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
-
-       return level_at_point;
-}
-
-
-//////////////////////// Noise functions
-
-float MapgenV6::getMudAmount(v2s16 p)
-{
-       int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
-       return getMudAmount(index);
-}
-
-
-bool MapgenV6::getHaveBeach(v2s16 p)
-{
-       int index = (p.Y - node_min.Z) * ystride + (p.X - node_min.X);
-       return getHaveBeach(index);
-}
-
-
-BiomeV6Type MapgenV6::getBiome(v2s16 p)
-{
-       int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
-                       + (p.X - full_node_min.X);
-       return getBiome(index, p);
-}
-
-
-float MapgenV6::getHumidity(v2s16 p)
-{
-       /*double noise = noise2d_perlin(
-               0.5+(float)p.X/500, 0.5+(float)p.Y/500,
-               seed+72384, 4, 0.66);
-       noise = (noise + 1.0)/2.0;*/
-
-       int index = (p.Y - full_node_min.Z) * (ystride + 2 * MAP_BLOCKSIZE)
-                       + (p.X - full_node_min.X);
-       float noise = noise_humidity->result[index];
-
-       if (noise < 0.0)
-               noise = 0.0;
-       if (noise > 1.0)
-               noise = 1.0;
-       return noise;
-}
-
-
-float MapgenV6::getTreeAmount(v2s16 p)
-{
-       /*double noise = noise2d_perlin(
-                       0.5+(float)p.X/125, 0.5+(float)p.Y/125,
-                       seed+2, 4, 0.66);*/
-
-       float noise = NoisePerlin2D(np_trees, p.X, p.Y, seed);
-       float zeroval = -0.39;
-       if (noise < zeroval)
-               return 0;
-
-       return 0.04 * (noise - zeroval) / (1.0 - zeroval);
-}
-
-
-bool MapgenV6::getHaveAppleTree(v2s16 p)
-{
-       /*is_apple_tree = noise2d_perlin(
-               0.5+(float)p.X/100, 0.5+(float)p.Z/100,
-               data->seed+342902, 3, 0.45) > 0.2;*/
-
-       float noise = NoisePerlin2D(np_apple_trees, p.X, p.Y, seed);
-
-       return noise > 0.2;
-}
-
-
-float MapgenV6::getMudAmount(int index)
-{
-       if (spflags & MGV6_FLAT)
-               return MGV6_AVERAGE_MUD_AMOUNT;
-
-       /*return ((float)AVERAGE_MUD_AMOUNT + 2.0 * noise2d_perlin(
-                       0.5+(float)p.X/200, 0.5+(float)p.Y/200,
-                       seed+91013, 3, 0.55));*/
-
-       return noise_mud->result[index];
-}
-
-
-bool MapgenV6::getHaveBeach(int index)
-{
-       // Determine whether to have sand here
-       /*double sandnoise = noise2d_perlin(
-                       0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250,
-                       seed+59420, 3, 0.50);*/
-
-       float sandnoise = noise_beach->result[index];
-       return (sandnoise > freq_beach);
-}
-
-
-BiomeV6Type MapgenV6::getBiome(int index, v2s16 p)
-{
-       // Just do something very simple as for now
-       /*double d = noise2d_perlin(
-                       0.6+(float)p2d.X/250, 0.2+(float)p2d.Y/250,
-                       seed+9130, 3, 0.50);*/
-
-       float d = noise_biome->result[index];
-       float h = noise_humidity->result[index];
-
-       if (spflags & MGV6_SNOWBIOMES) {
-               float blend = (spflags & MGV6_BIOMEBLEND) ? noise2d(p.X, p.Y, seed) / 40 : 0;
-
-               if (d > MGV6_FREQ_HOT + blend) {
-                       if (h > MGV6_FREQ_JUNGLE + blend)
-                               return BT_JUNGLE;
-
-                       return BT_DESERT;
-               }
-
-               if (d < MGV6_FREQ_SNOW + blend) {
-                       if (h > MGV6_FREQ_TAIGA + blend)
-                               return BT_TAIGA;
-
-                       return BT_TUNDRA;
-               }
-
-               return BT_NORMAL;
-       }
-
-       if (d > freq_desert)
-               return BT_DESERT;
-
-       if ((spflags & MGV6_BIOMEBLEND) && (d > freq_desert - 0.10) &&
-                       ((noise2d(p.X, p.Y, seed) + 1.0) > (freq_desert - d) * 20.0))
-               return BT_DESERT;
-
-       if ((spflags & MGV6_JUNGLES) && h > 0.75)
-               return BT_JUNGLE;
-
-       return BT_NORMAL;
-
-}
-
-
-u32 MapgenV6::get_blockseed(u64 seed, v3s16 p)
-{
-       s32 x = p.X, y = p.Y, z = p.Z;
-       return (u32)(seed % 0x100000000ULL) + z * 38134234 + y * 42123 + x * 23;
-}
-
-
-//////////////////////// Map generator
-
-void MapgenV6::makeChunk(BlockMakeData *data)
-{
-       // Pre-conditions
-       assert(data->vmanip);
-       assert(data->nodedef);
-       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
-               data->blockpos_requested.Y >= data->blockpos_min.Y &&
-               data->blockpos_requested.Z >= data->blockpos_min.Z);
-       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
-               data->blockpos_requested.Y <= data->blockpos_max.Y &&
-               data->blockpos_requested.Z <= data->blockpos_max.Z);
-
-       this->generating = true;
-       this->vm   = data->vmanip;
-       this->ndef = data->nodedef;
-
-       // Hack: use minimum block coords for old code that assumes a single block
-       v3s16 blockpos_min = data->blockpos_min;
-       v3s16 blockpos_max = data->blockpos_max;
-
-       // Area of central chunk
-       node_min = blockpos_min * MAP_BLOCKSIZE;
-       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-
-       // Full allocated area
-       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
-       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-
-       central_area_size = node_max - node_min + v3s16(1, 1, 1);
-       assert(central_area_size.X == central_area_size.Z);
-
-       // Create a block-specific seed
-       blockseed = get_blockseed(data->seed, full_node_min);
-
-       // Make some noise
-       calculateNoise();
-
-       // Maximum height of the stone surface and obstacles.
-       // This is used to guide the cave generation
-       s16 stone_surface_max_y;
-
-       // Generate general ground level to full area
-       stone_surface_max_y = generateGround();
-
-       // Create initial heightmap to limit caves
-       updateHeightmap(node_min, node_max);
-
-       const s16 max_spread_amount = MAP_BLOCKSIZE;
-       // Limit dirt flow area by 1 because mud is flown into neighbors.
-       s16 mudflow_minpos = -max_spread_amount + 1;
-       s16 mudflow_maxpos = central_area_size.X + max_spread_amount - 2;
-
-       // Loop this part, it will make stuff look older and newer nicely
-       const u32 age_loops = 2;
-       for (u32 i_age = 0; i_age < age_loops; i_age++) { // Aging loop
-               // Make caves (this code is relatively horrible)
-               if (flags & MG_CAVES)
-                       generateCaves(stone_surface_max_y);
-
-               // Add mud to the central chunk
-               addMud();
-
-               // Flow mud away from steep edges
-               if (spflags & MGV6_MUDFLOW)
-                       flowMud(mudflow_minpos, mudflow_maxpos);
-
-       }
-
-       // Update heightmap after mudflow
-       updateHeightmap(node_min, node_max);
-
-       // Add dungeons
-       if ((flags & MG_DUNGEONS) && (stone_surface_max_y >= node_min.Y)) {
-               DungeonParams dp;
-
-               dp.seed             = seed;
-               dp.c_water          = c_water_source;
-               dp.c_river_water    = c_water_source;
-
-               dp.only_in_ground   = true;
-               dp.corridor_len_min = 1;
-               dp.corridor_len_max = 13;
-               dp.rooms_min        = 2;
-               dp.rooms_max        = 16;
-               dp.y_min            = -MAX_MAP_GENERATION_LIMIT;
-               dp.y_max            = MAX_MAP_GENERATION_LIMIT;
-
-               dp.np_density
-                       = NoiseParams(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0);
-               dp.np_alt_wall
-                       = NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
-
-               if (getBiome(0, v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
-                       dp.c_wall              = c_desert_stone;
-                       dp.c_alt_wall          = CONTENT_IGNORE;
-                       dp.c_stair             = c_stair_desert_stone;
-
-                       dp.diagonal_dirs       = true;
-                       dp.holesize            = v3s16(2, 3, 2);
-                       dp.room_size_min       = v3s16(6, 9, 6);
-                       dp.room_size_max       = v3s16(10, 11, 10);
-                       dp.room_size_large_min = v3s16(10, 13, 10);
-                       dp.room_size_large_max = v3s16(18, 21, 18);
-                       dp.notifytype          = GENNOTIFY_TEMPLE;
-               } else {
-                       dp.c_wall              = c_cobble;
-                       dp.c_alt_wall          = c_mossycobble;
-                       dp.c_stair             = c_stair_cobble;
-
-                       dp.diagonal_dirs       = false;
-                       dp.holesize            = v3s16(1, 2, 1);
-                       dp.room_size_min       = v3s16(4, 4, 4);
-                       dp.room_size_max       = v3s16(8, 6, 8);
-                       dp.room_size_large_min = v3s16(8, 8, 8);
-                       dp.room_size_large_max = v3s16(16, 16, 16);
-                       dp.notifytype          = GENNOTIFY_DUNGEON;
-               }
-
-               DungeonGen dgen(ndef, &gennotify, &dp);
-               dgen.generate(vm, blockseed, full_node_min, full_node_max);
-       }
-
-       // Add top and bottom side of water to transforming_liquid queue
-       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
-
-       // Add surface nodes
-       growGrass();
-
-       // Generate some trees, and add grass, if a jungle
-       if (spflags & MGV6_TREES)
-               placeTreesAndJungleGrass();
-
-       // Generate the registered decorations
-       if (flags & MG_DECORATIONS)
-               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
-
-       // Generate the registered ores
-       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
-
-       // Calculate lighting
-       if (flags & MG_LIGHT)
-               calcLighting(node_min - v3s16(1, 1, 1) * MAP_BLOCKSIZE,
-                       node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE,
-                       full_node_min, full_node_max);
-
-       this->generating = false;
-}
-
-
-void MapgenV6::calculateNoise()
-{
-       int x = node_min.X;
-       int z = node_min.Z;
-       int fx = full_node_min.X;
-       int fz = full_node_min.Z;
-
-       if (!(spflags & MGV6_FLAT)) {
-               noise_terrain_base->perlinMap2D_PO(x, 0.5, z, 0.5);
-               noise_terrain_higher->perlinMap2D_PO(x, 0.5, z, 0.5);
-               noise_steepness->perlinMap2D_PO(x, 0.5, z, 0.5);
-               noise_height_select->perlinMap2D_PO(x, 0.5, z, 0.5);
-               noise_mud->perlinMap2D_PO(x, 0.5, z, 0.5);
-       }
-
-       noise_beach->perlinMap2D_PO(x, 0.2, z, 0.7);
-
-       noise_biome->perlinMap2D_PO(fx, 0.6, fz, 0.2);
-       noise_humidity->perlinMap2D_PO(fx, 0.0, fz, 0.0);
-       // Humidity map does not need range limiting 0 to 1,
-       // only humidity at point does
-}
-
-
-int MapgenV6::generateGround()
-{
-       //TimeTaker timer1("Generating ground level");
-       MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
-       MapNode n_stone(c_stone), n_desert_stone(c_desert_stone);
-       MapNode n_ice(c_ice);
-       int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
-
-       u32 index = 0;
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
-               // Surface height
-               s16 surface_y = (s16)baseTerrainLevelFromMap(index);
-
-               // Log it
-               if (surface_y > stone_surface_max_y)
-                       stone_surface_max_y = surface_y;
-
-               BiomeV6Type bt = getBiome(v2s16(x, z));
-
-               // Fill ground with stone
-               const v3s16 &em = vm->m_area.getExtent();
-               u32 i = vm->m_area.index(x, node_min.Y, z);
-               for (s16 y = node_min.Y; y <= node_max.Y; y++) {
-                       if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
-                               if (y <= surface_y) {
-                                       vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE
-                                                       && bt == BT_DESERT) ?
-                                               n_desert_stone : n_stone;
-                               } else if (y <= water_level) {
-                                       vm->m_data[i] = (y >= MGV6_ICE_BASE
-                                                       && bt == BT_TUNDRA) ?
-                                               n_ice : n_water_source;
-                               } else {
-                                       vm->m_data[i] = n_air;
-                               }
-                       }
-                       vm->m_area.add_y(em, i, 1);
-               }
-       }
-
-       return stone_surface_max_y;
-}
-
-
-void MapgenV6::addMud()
-{
-       // 15ms @cs=8
-       //TimeTaker timer1("add mud");
-       MapNode n_dirt(c_dirt), n_gravel(c_gravel);
-       MapNode n_sand(c_sand), n_desert_sand(c_desert_sand);
-       MapNode addnode;
-
-       u32 index = 0;
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
-               // Randomize mud amount
-               s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5;
-
-               // Find ground level
-               s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this!
-
-               // Handle area not found
-               if (surface_y == vm->m_area.MinEdge.Y - 1)
-                       continue;
-
-               BiomeV6Type bt = getBiome(v2s16(x, z));
-               addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt;
-
-               if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) {
-                       addnode = n_sand;
-               } else if (mud_add_amount <= 0) {
-                       mud_add_amount = 1 - mud_add_amount;
-                       addnode = n_gravel;
-               } else if (bt != BT_DESERT && getHaveBeach(index) &&
-                               surface_y + mud_add_amount <= water_level + 2) {
-                       addnode = n_sand;
-               }
-
-               if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20)
-                       mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5);
-
-               /* If topmost node is grass, change it to mud.  It might be if it was
-               // flown to there from a neighboring chunk and then converted.
-               u32 i = vm->m_area.index(x, surface_y, z);
-               if (vm->m_data[i].getContent() == c_dirt_with_grass)
-                       vm->m_data[i] = n_dirt;*/
-
-               // Add mud on ground
-               s16 mudcount = 0;
-               const v3s16 &em = vm->m_area.getExtent();
-               s16 y_start = surface_y + 1;
-               u32 i = vm->m_area.index(x, y_start, z);
-               for (s16 y = y_start; y <= node_max.Y; y++) {
-                       if (mudcount >= mud_add_amount)
-                               break;
-
-                       vm->m_data[i] = addnode;
-                       mudcount++;
-
-                       vm->m_area.add_y(em, i, 1);
-               }
-       }
-}
-
-
-void MapgenV6::flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos)
-{
-       // 340ms @cs=8
-       //TimeTaker timer1("flow mud");
-
-       // Iterate a few times
-       for (s16 k = 0; k < 3; k++) {
-               for (s16 z = mudflow_minpos; z <= mudflow_maxpos; z++)
-               for (s16 x = mudflow_minpos; x <= mudflow_maxpos; x++) {
-                       // Invert coordinates every 2nd iteration
-                       if (k % 2 == 0) {
-                               x = mudflow_maxpos - (x - mudflow_minpos);
-                               z = mudflow_maxpos - (z - mudflow_minpos);
-                       }
-
-                       // Node position in 2d
-                       v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x, z);
-
-                       const v3s16 &em = vm->m_area.getExtent();
-                       u32 i = vm->m_area.index(p2d.X, node_max.Y, p2d.Y);
-                       s16 y = node_max.Y;
-
-                       while (y >= node_min.Y) {
-
-                       for (;; y--) {
-                               MapNode *n = NULL;
-                               // Find mud
-                               for (; y >= node_min.Y; y--) {
-                                       n = &vm->m_data[i];
-                                       if (n->getContent() == c_dirt ||
-                                                       n->getContent() == c_dirt_with_grass ||
-                                                       n->getContent() == c_gravel)
-                                               break;
-
-                                       vm->m_area.add_y(em, i, -1);
-                               }
-
-                               // Stop if out of area
-                               //if(vmanip.m_area.contains(i) == false)
-                               if (y < node_min.Y)
-                                       break;
-
-                               if (n->getContent() == c_dirt ||
-                                               n->getContent() == c_dirt_with_grass) {
-                                       // Make it exactly mud
-                                       n->setContent(c_dirt);
-
-                                       // Don't flow it if the stuff under it is not mud
-                                       {
-                                               u32 i2 = i;
-                                               vm->m_area.add_y(em, i2, -1);
-                                               // Cancel if out of area
-                                               if (!vm->m_area.contains(i2))
-                                                       continue;
-                                               MapNode *n2 = &vm->m_data[i2];
-                                               if (n2->getContent() != c_dirt &&
-                                                               n2->getContent() != c_dirt_with_grass)
-                                                       continue;
-                                       }
-                               }
-
-                               v3s16 dirs4[4] = {
-                                       v3s16(0, 0, 1), // back
-                                       v3s16(1, 0, 0), // right
-                                       v3s16(0, 0, -1), // front
-                                       v3s16(-1, 0, 0), // left
-                               };
-
-                               // Check that upper is walkable. Cancel
-                               // dropping if upper keeps it in place.
-                               u32 i3 = i;
-                               vm->m_area.add_y(em, i3, 1);
-                               MapNode *n3 = NULL;
-
-                               if (vm->m_area.contains(i3)) {
-                                       n3 = &vm->m_data[i3];
-                                       if (ndef->get(*n3).walkable)
-                                               continue;
-                               }
-
-                               // Drop mud on side
-                               for (const v3s16 &dirp : dirs4) {
-                                       u32 i2 = i;
-                                       // Move to side
-                                       vm->m_area.add_p(em, i2, dirp);
-                                       // Fail if out of area
-                                       if (!vm->m_area.contains(i2))
-                                               continue;
-                                       // Check that side is air
-                                       MapNode *n2 = &vm->m_data[i2];
-                                       if (ndef->get(*n2).walkable)
-                                               continue;
-                                       // Check that under side is air
-                                       vm->m_area.add_y(em, i2, -1);
-                                       if (!vm->m_area.contains(i2))
-                                               continue;
-                                       n2 = &vm->m_data[i2];
-                                       if (ndef->get(*n2).walkable)
-                                               continue;
-                                       // Loop further down until not air
-                                       bool dropped_to_unknown = false;
-                                       do {
-                                               vm->m_area.add_y(em, i2, -1);
-                                               n2 = &vm->m_data[i2];
-                                               // if out of known area
-                                               if (!vm->m_area.contains(i2) ||
-                                                               n2->getContent() == CONTENT_IGNORE) {
-                                                       dropped_to_unknown = true;
-                                                       break;
-                                               }
-                                       } while (!ndef->get(*n2).walkable);
-                                       // Loop one up so that we're in air
-                                       vm->m_area.add_y(em, i2, 1);
-
-                                       // Move mud to new place. Outside mapchunk remove
-                                       // any decorations above removed or placed mud.
-                                       if (!dropped_to_unknown)
-                                               moveMud(i, i2, i3, p2d, em);
-
-                                       // Done
-                                       break;
-                               }
-                       }
-                       }
-               }
-       }
-}
-
-
-void MapgenV6::moveMud(u32 remove_index, u32 place_index,
-       u32 above_remove_index, v2s16 pos, v3s16 em)
-{
-       MapNode n_air(CONTENT_AIR);
-       // Copy mud from old place to new place
-       vm->m_data[place_index] = vm->m_data[remove_index];
-       // Set old place to be air
-       vm->m_data[remove_index] = n_air;
-       // Outside the mapchunk decorations may need to be removed if above removed
-       // mud or if half-buried in placed mud. Placed mud is to the side of pos so
-       // use 'pos.X >= node_max.X' etc.
-       if (pos.X >= node_max.X || pos.X <= node_min.X ||
-                       pos.Y >= node_max.Z || pos.Y <= node_min.Z) {
-               // 'above remove' node is above removed mud. If it is not air, water or
-               // 'ignore' it is a decoration that needs removing. Also search upwards
-               // to remove a possible stacked decoration.
-               // Check for 'ignore' because stacked decorations can penetrate into
-               // 'ignore' nodes above the mapchunk.
-               while (vm->m_area.contains(above_remove_index) &&
-                               vm->m_data[above_remove_index].getContent() != CONTENT_AIR &&
-                               vm->m_data[above_remove_index].getContent() != c_water_source &&
-                               vm->m_data[above_remove_index].getContent() != CONTENT_IGNORE) {
-                       vm->m_data[above_remove_index] = n_air;
-                       vm->m_area.add_y(em, above_remove_index, 1);
-               }
-               // Mud placed may have partially-buried a stacked decoration, search
-               // above and remove.
-               vm->m_area.add_y(em, place_index, 1);
-               while (vm->m_area.contains(place_index) &&
-                               vm->m_data[place_index].getContent() != CONTENT_AIR &&
-                               vm->m_data[place_index].getContent() != c_water_source &&
-                               vm->m_data[place_index].getContent() != CONTENT_IGNORE) {
-                       vm->m_data[place_index] = n_air;
-                       vm->m_area.add_y(em, place_index, 1);
-               }
-       }
-}
-
-
-void MapgenV6::placeTreesAndJungleGrass()
-{
-       //TimeTaker t("placeTrees");
-       if (node_max.Y < water_level)
-               return;
-
-       PseudoRandom grassrandom(blockseed + 53);
-       content_t c_junglegrass = ndef->getId("mapgen_junglegrass");
-       // if we don't have junglegrass, don't place cignore... that's bad
-       if (c_junglegrass == CONTENT_IGNORE)
-               c_junglegrass = CONTENT_AIR;
-       MapNode n_junglegrass(c_junglegrass);
-       const v3s16 &em = vm->m_area.getExtent();
-
-       // Divide area into parts
-       s16 div = 8;
-       s16 sidelen = central_area_size.X / div;
-       double area = sidelen * sidelen;
-
-       // N.B.  We must add jungle grass first, since tree leaves will
-       // obstruct the ground, giving us a false ground level
-       for (s16 z0 = 0; z0 < div; z0++)
-       for (s16 x0 = 0; x0 < div; x0++) {
-               // Center position of part of division
-               v2s16 p2d_center(
-                       node_min.X + sidelen / 2 + sidelen * x0,
-                       node_min.Z + sidelen / 2 + sidelen * z0
-               );
-               // Minimum edge of part of division
-               v2s16 p2d_min(
-                       node_min.X + sidelen * x0,
-                       node_min.Z + sidelen * z0
-               );
-               // Maximum edge of part of division
-               v2s16 p2d_max(
-                       node_min.X + sidelen + sidelen * x0 - 1,
-                       node_min.Z + sidelen + sidelen * z0 - 1
-               );
-
-               // Get biome at center position of part of division
-               BiomeV6Type bt = getBiome(p2d_center);
-
-               // Amount of trees
-               u32 tree_count;
-               if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) {
-                       tree_count = area * getTreeAmount(p2d_center);
-                       if (bt == BT_JUNGLE)
-                               tree_count *= 4;
-               } else {
-                       tree_count = 0;
-               }
-
-               // Add jungle grass
-               if (bt == BT_JUNGLE) {
-                       float humidity = getHumidity(p2d_center);
-                       u32 grass_count = 5 * humidity * tree_count;
-                       for (u32 i = 0; i < grass_count; i++) {
-                               s16 x = grassrandom.range(p2d_min.X, p2d_max.X);
-                               s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y);
-                               int mapindex = central_area_size.X * (z - node_min.Z)
-                                                               + (x - node_min.X);
-                               s16 y = heightmap[mapindex];
-                               if (y < water_level)
-                                       continue;
-
-                               u32 vi = vm->m_area.index(x, y, z);
-                               // place on dirt_with_grass, since we know it is exposed to sunlight
-                               if (vm->m_data[vi].getContent() == c_dirt_with_grass) {
-                                       vm->m_area.add_y(em, vi, 1);
-                                       vm->m_data[vi] = n_junglegrass;
-                               }
-                       }
-               }
-
-               // Put trees in random places on part of division
-               for (u32 i = 0; i < tree_count; i++) {
-                       s16 x = myrand_range(p2d_min.X, p2d_max.X);
-                       s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
-                       int mapindex = central_area_size.X * (z - node_min.Z)
-                                                       + (x - node_min.X);
-                       s16 y = heightmap[mapindex];
-                       // Don't make a tree under water level
-                       // Don't make a tree so high that it doesn't fit
-                       if (y < water_level || y > node_max.Y - 6)
-                               continue;
-
-                       v3s16 p(x, y, z);
-                       // Trees grow only on mud and grass
-                       {
-                               u32 i = vm->m_area.index(p);
-                               content_t c = vm->m_data[i].getContent();
-                               if (c != c_dirt &&
-                                               c != c_dirt_with_grass &&
-                                               c != c_dirt_with_snow)
-                                       continue;
-                       }
-                       p.Y++;
-
-                       // Make a tree
-                       if (bt == BT_JUNGLE) {
-                               treegen::make_jungletree(*vm, p, ndef, myrand());
-                       } else if (bt == BT_TAIGA) {
-                               treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand());
-                       } else if (bt == BT_NORMAL) {
-                               bool is_apple_tree = (myrand_range(0, 3) == 0) &&
-                                                       getHaveAppleTree(v2s16(x, z));
-                               treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand());
-                       }
-               }
-       }
-       //printf("placeTreesAndJungleGrass: %dms\n", t.stop());
-}
-
-
-void MapgenV6::growGrass() // Add surface nodes
-{
-       MapNode n_dirt_with_grass(c_dirt_with_grass);
-       MapNode n_dirt_with_snow(c_dirt_with_snow);
-       MapNode n_snowblock(c_snowblock);
-       MapNode n_snow(c_snow);
-       const v3s16 &em = vm->m_area.getExtent();
-
-       u32 index = 0;
-       for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++)
-       for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) {
-               // Find the lowest surface to which enough light ends up to make
-               // grass grow.  Basically just wait until not air and not leaves.
-               s16 surface_y = 0;
-               {
-                       u32 i = vm->m_area.index(x, node_max.Y, z);
-                       s16 y;
-                       // Go to ground level
-                       for (y = node_max.Y; y >= full_node_min.Y; y--) {
-                               MapNode &n = vm->m_data[i];
-                               if (ndef->get(n).param_type != CPT_LIGHT ||
-                                               ndef->get(n).liquid_type != LIQUID_NONE ||
-                                               n.getContent() == c_ice)
-                                       break;
-                               vm->m_area.add_y(em, i, -1);
-                       }
-                       surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y;
-               }
-
-               BiomeV6Type bt = getBiome(index, v2s16(x, z));
-               u32 i = vm->m_area.index(x, surface_y, z);
-               content_t c = vm->m_data[i].getContent();
-               if (surface_y >= water_level - 20) {
-                       if (bt == BT_TAIGA && c == c_dirt) {
-                               vm->m_data[i] = n_dirt_with_snow;
-                       } else if (bt == BT_TUNDRA) {
-                               if (c == c_dirt) {
-                                       vm->m_data[i] = n_snowblock;
-                                       vm->m_area.add_y(em, i, -1);
-                                       vm->m_data[i] = n_dirt_with_snow;
-                               } else if (c == c_stone && surface_y < node_max.Y) {
-                                       vm->m_area.add_y(em, i, 1);
-                                       vm->m_data[i] = n_snowblock;
-                               }
-                       } else if (c == c_dirt) {
-                               vm->m_data[i] = n_dirt_with_grass;
-                       }
-               }
-       }
-}
-
-
-void MapgenV6::generateCaves(int max_stone_y)
-{
-       float cave_amount = NoisePerlin2D(np_cave, node_min.X, node_min.Y, seed);
-       int volume_nodes = (node_max.X - node_min.X + 1) *
-                                          (node_max.Y - node_min.Y + 1) * MAP_BLOCKSIZE;
-       cave_amount = MYMAX(0.0, cave_amount);
-       u32 caves_count = cave_amount * volume_nodes / 50000;
-       u32 bruises_count = 1;
-       PseudoRandom ps(blockseed + 21343);
-       PseudoRandom ps2(blockseed + 1032);
-
-       if (ps.range(1, 6) == 1)
-               bruises_count = ps.range(0, ps.range(0, 2));
-
-       if (getBiome(v2s16(node_min.X, node_min.Z)) == BT_DESERT) {
-               caves_count   /= 3;
-               bruises_count /= 3;
-       }
-
-       for (u32 i = 0; i < caves_count + bruises_count; i++) {
-               CavesV6 cave(ndef, &gennotify, water_level, c_water_source, c_lava_source);
-
-               bool large_cave = (i >= caves_count);
-               cave.makeCave(vm, node_min, node_max, &ps, &ps2,
-                       large_cave, max_stone_y, heightmap);
-       }
-}
diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h
deleted file mode 100644 (file)
index 9c87942..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2014-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "mapgen.h"
-#include "noise.h"
-
-#define MGV6_AVERAGE_MUD_AMOUNT 4
-#define MGV6_DESERT_STONE_BASE -32
-#define MGV6_ICE_BASE 0
-#define MGV6_FREQ_HOT 0.4
-#define MGV6_FREQ_SNOW -0.4
-#define MGV6_FREQ_TAIGA 0.5
-#define MGV6_FREQ_JUNGLE 0.5
-
-//////////// Mapgen V6 flags
-#define MGV6_JUNGLES    0x01
-#define MGV6_BIOMEBLEND 0x02
-#define MGV6_MUDFLOW    0x04
-#define MGV6_SNOWBIOMES 0x08
-#define MGV6_FLAT       0x10
-#define MGV6_TREES      0x20
-
-
-extern FlagDesc flagdesc_mapgen_v6[];
-
-
-enum BiomeV6Type
-{
-       BT_NORMAL,
-       BT_DESERT,
-       BT_JUNGLE,
-       BT_TUNDRA,
-       BT_TAIGA,
-};
-
-
-struct MapgenV6Params : public MapgenParams {
-       u32 spflags = MGV6_JUNGLES | MGV6_SNOWBIOMES | MGV6_TREES |
-               MGV6_BIOMEBLEND | MGV6_MUDFLOW;
-       float freq_desert = 0.45f;
-       float freq_beach = 0.15f;
-       NoiseParams np_terrain_base;
-       NoiseParams np_terrain_higher;
-       NoiseParams np_steepness;
-       NoiseParams np_height_select;
-       NoiseParams np_mud;
-       NoiseParams np_beach;
-       NoiseParams np_biome;
-       NoiseParams np_cave;
-       NoiseParams np_humidity;
-       NoiseParams np_trees;
-       NoiseParams np_apple_trees;
-
-       MapgenV6Params();
-       ~MapgenV6Params() = default;
-
-       void readParams(const Settings *settings);
-       void writeParams(Settings *settings) const;
-};
-
-
-class MapgenV6 : public Mapgen {
-public:
-       EmergeManager *m_emerge;
-
-       int ystride;
-       u32 spflags;
-
-       v3s16 node_min;
-       v3s16 node_max;
-       v3s16 full_node_min;
-       v3s16 full_node_max;
-       v3s16 central_area_size;
-
-       Noise *noise_terrain_base;
-       Noise *noise_terrain_higher;
-       Noise *noise_steepness;
-       Noise *noise_height_select;
-       Noise *noise_mud;
-       Noise *noise_beach;
-       Noise *noise_biome;
-       Noise *noise_humidity;
-       NoiseParams *np_cave;
-       NoiseParams *np_humidity;
-       NoiseParams *np_trees;
-       NoiseParams *np_apple_trees;
-       float freq_desert;
-       float freq_beach;
-
-       content_t c_stone;
-       content_t c_dirt;
-       content_t c_dirt_with_grass;
-       content_t c_sand;
-       content_t c_water_source;
-       content_t c_lava_source;
-       content_t c_gravel;
-       content_t c_desert_stone;
-       content_t c_desert_sand;
-       content_t c_dirt_with_snow;
-       content_t c_snow;
-       content_t c_snowblock;
-       content_t c_ice;
-
-       content_t c_cobble;
-       content_t c_mossycobble;
-       content_t c_stair_cobble;
-       content_t c_stair_desert_stone;
-
-       MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge);
-       ~MapgenV6();
-
-       virtual MapgenType getType() const { return MAPGEN_V6; }
-
-       void makeChunk(BlockMakeData *data);
-       int getGroundLevelAtPoint(v2s16 p);
-       int getSpawnLevelAtPoint(v2s16 p);
-
-       float baseTerrainLevel(float terrain_base, float terrain_higher,
-               float steepness, float height_select);
-       virtual float baseTerrainLevelFromNoise(v2s16 p);
-       virtual float baseTerrainLevelFromMap(v2s16 p);
-       virtual float baseTerrainLevelFromMap(int index);
-
-       s16 find_stone_level(v2s16 p2d);
-       bool block_is_underground(u64 seed, v3s16 blockpos);
-       s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision);
-
-       float getHumidity(v2s16 p);
-       float getTreeAmount(v2s16 p);
-       bool getHaveAppleTree(v2s16 p);
-       float getMudAmount(v2s16 p);
-       virtual float getMudAmount(int index);
-       bool getHaveBeach(v2s16 p);
-       bool getHaveBeach(int index);
-       BiomeV6Type getBiome(v2s16 p);
-       BiomeV6Type getBiome(int index, v2s16 p);
-
-       u32 get_blockseed(u64 seed, v3s16 p);
-
-       virtual void calculateNoise();
-       int generateGround();
-       void addMud();
-       void flowMud(s16 &mudflow_minpos, s16 &mudflow_maxpos);
-       void moveMud(u32 remove_index, u32 place_index,
-               u32 above_remove_index, v2s16 pos, v3s16 em);
-       void growGrass();
-       void placeTreesAndJungleGrass();
-       virtual void generateCaves(int max_stone_y);
-};
diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp
deleted file mode 100644 (file)
index 4047801..0000000
+++ /dev/null
@@ -1,743 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2014-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "mapgen.h"
-#include "voxel.h"
-#include "noise.h"
-#include "mapblock.h"
-#include "mapnode.h"
-#include "map.h"
-#include "content_sao.h"
-#include "nodedef.h"
-#include "voxelalgorithms.h"
-//#include "profiler.h" // For TimeTaker
-#include "settings.h" // For g_settings
-#include "emerge.h"
-#include "dungeongen.h"
-#include "cavegen.h"
-#include "mg_biome.h"
-#include "mg_ore.h"
-#include "mg_decoration.h"
-#include "mapgen_v7.h"
-
-
-FlagDesc flagdesc_mapgen_v7[] = {
-       {"mountains",   MGV7_MOUNTAINS},
-       {"ridges",      MGV7_RIDGES},
-       {"floatlands",  MGV7_FLOATLANDS},
-       {"caverns",     MGV7_CAVERNS},
-       {NULL,          0}
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-MapgenV7::MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge)
-       : MapgenBasic(mapgenid, params, emerge)
-{
-       spflags             = params->spflags;
-       mount_zero_level    = params->mount_zero_level;
-       cave_width          = params->cave_width;
-       large_cave_depth    = params->large_cave_depth;
-       lava_depth          = params->lava_depth;
-       float_mount_density = params->float_mount_density;
-       floatland_level     = params->floatland_level;
-       shadow_limit        = params->shadow_limit;
-       cavern_limit        = params->cavern_limit;
-       cavern_taper        = params->cavern_taper;
-       cavern_threshold    = params->cavern_threshold;
-
-       // This is to avoid a divide-by-zero.
-       // Parameter will be saved to map_meta.txt in limited form.
-       params->float_mount_height = MYMAX(params->float_mount_height, 1.0f);
-       float_mount_height   = params->float_mount_height;
-
-       // 2D noise
-       noise_terrain_base    = new Noise(&params->np_terrain_base,    seed, csize.X, csize.Z);
-       noise_terrain_alt     = new Noise(&params->np_terrain_alt,     seed, csize.X, csize.Z);
-       noise_terrain_persist = new Noise(&params->np_terrain_persist, seed, csize.X, csize.Z);
-       noise_height_select   = new Noise(&params->np_height_select,   seed, csize.X, csize.Z);
-       noise_filler_depth    = new Noise(&params->np_filler_depth,    seed, csize.X, csize.Z);
-
-       if (spflags & MGV7_MOUNTAINS)
-               noise_mount_height = new Noise(&params->np_mount_height, seed, csize.X, csize.Z);
-
-       if (spflags & MGV7_FLOATLANDS) {
-               noise_floatland_base    = new Noise(&params->np_floatland_base,    seed, csize.X, csize.Z);
-               noise_float_base_height = new Noise(&params->np_float_base_height, seed, csize.X, csize.Z);
-       }
-
-       if (spflags & MGV7_RIDGES) {
-               noise_ridge_uwater = new Noise(&params->np_ridge_uwater, seed, csize.X, csize.Z);
-       // 3D noise, 1-up 1-down overgeneration
-               noise_ridge = new Noise(&params->np_ridge, seed, csize.X, csize.Y + 2, csize.Z);
-       }
-       // 3D noise, 1 up, 1 down overgeneration
-       if ((spflags & MGV7_MOUNTAINS) || (spflags & MGV7_FLOATLANDS))
-               noise_mountain = new Noise(&params->np_mountain, seed, csize.X, csize.Y + 2, csize.Z);
-       // 3D noise, 1 down overgeneration
-       MapgenBasic::np_cave1  = params->np_cave1;
-       MapgenBasic::np_cave2  = params->np_cave2;
-       MapgenBasic::np_cavern = params->np_cavern;
-}
-
-
-MapgenV7::~MapgenV7()
-{
-       delete noise_terrain_base;
-       delete noise_terrain_alt;
-       delete noise_terrain_persist;
-       delete noise_height_select;
-       delete noise_filler_depth;
-
-       if (spflags & MGV7_MOUNTAINS)
-               delete noise_mount_height;
-
-       if (spflags & MGV7_FLOATLANDS) {
-               delete noise_floatland_base;
-               delete noise_float_base_height;
-       }
-
-       if (spflags & MGV7_RIDGES) {
-               delete noise_ridge_uwater;
-               delete noise_ridge;
-       }
-
-       if ((spflags & MGV7_MOUNTAINS) || (spflags & MGV7_FLOATLANDS))
-               delete noise_mountain;
-}
-
-
-MapgenV7Params::MapgenV7Params():
-       np_terrain_base      (4,    70,   v3f(600,  600,  600),  82341, 5, 0.6,  2.0),
-       np_terrain_alt       (4,    25,   v3f(600,  600,  600),  5934,  5, 0.6,  2.0),
-       np_terrain_persist   (0.6,  0.1,  v3f(2000, 2000, 2000), 539,   3, 0.6,  2.0),
-       np_height_select     (-8,   16,   v3f(500,  500,  500),  4213,  6, 0.7,  2.0),
-       np_filler_depth      (0,    1.2,  v3f(150,  150,  150),  261,   3, 0.7,  2.0),
-       np_mount_height      (256,  112,  v3f(1000, 1000, 1000), 72449, 3, 0.6,  2.0),
-       np_ridge_uwater      (0,    1,    v3f(1000, 1000, 1000), 85039, 5, 0.6,  2.0),
-       np_floatland_base    (-0.6, 1.5,  v3f(600,  600,  600),  114,   5, 0.6,  2.0),
-       np_float_base_height (48,   24,   v3f(300,  300,  300),  907,   4, 0.7,  2.0),
-       np_mountain          (-0.6, 1,    v3f(250,  350,  250),  5333,  5, 0.63, 2.0),
-       np_ridge             (0,    1,    v3f(100,  100,  100),  6467,  4, 0.75, 2.0),
-       np_cavern            (0,    1,    v3f(384,  128,  384),  723,   5, 0.63, 2.0),
-       np_cave1             (0,    12,   v3f(61,   61,   61),   52534, 3, 0.5,  2.0),
-       np_cave2             (0,    12,   v3f(67,   67,   67),   10325, 3, 0.5,  2.0)
-{
-}
-
-
-void MapgenV7Params::readParams(const Settings *settings)
-{
-       settings->getFlagStrNoEx("mgv7_spflags",           spflags, flagdesc_mapgen_v7);
-       settings->getS16NoEx("mgv7_mount_zero_level",      mount_zero_level);
-       settings->getFloatNoEx("mgv7_cave_width",          cave_width);
-       settings->getS16NoEx("mgv7_large_cave_depth",      large_cave_depth);
-       settings->getS16NoEx("mgv7_lava_depth",            lava_depth);
-       settings->getFloatNoEx("mgv7_float_mount_density", float_mount_density);
-       settings->getFloatNoEx("mgv7_float_mount_height",  float_mount_height);
-       settings->getS16NoEx("mgv7_floatland_level",       floatland_level);
-       settings->getS16NoEx("mgv7_shadow_limit",          shadow_limit);
-       settings->getS16NoEx("mgv7_cavern_limit",          cavern_limit);
-       settings->getS16NoEx("mgv7_cavern_taper",          cavern_taper);
-       settings->getFloatNoEx("mgv7_cavern_threshold",    cavern_threshold);
-
-       settings->getNoiseParams("mgv7_np_terrain_base",      np_terrain_base);
-       settings->getNoiseParams("mgv7_np_terrain_alt",       np_terrain_alt);
-       settings->getNoiseParams("mgv7_np_terrain_persist",   np_terrain_persist);
-       settings->getNoiseParams("mgv7_np_height_select",     np_height_select);
-       settings->getNoiseParams("mgv7_np_filler_depth",      np_filler_depth);
-       settings->getNoiseParams("mgv7_np_mount_height",      np_mount_height);
-       settings->getNoiseParams("mgv7_np_ridge_uwater",      np_ridge_uwater);
-       settings->getNoiseParams("mgv7_np_floatland_base",    np_floatland_base);
-       settings->getNoiseParams("mgv7_np_float_base_height", np_float_base_height);
-       settings->getNoiseParams("mgv7_np_mountain",          np_mountain);
-       settings->getNoiseParams("mgv7_np_ridge",             np_ridge);
-       settings->getNoiseParams("mgv7_np_cavern",            np_cavern);
-       settings->getNoiseParams("mgv7_np_cave1",             np_cave1);
-       settings->getNoiseParams("mgv7_np_cave2",             np_cave2);
-}
-
-
-void MapgenV7Params::writeParams(Settings *settings) const
-{
-       settings->setFlagStr("mgv7_spflags",           spflags, flagdesc_mapgen_v7, U32_MAX);
-       settings->setS16("mgv7_mount_zero_level",      mount_zero_level);
-       settings->setFloat("mgv7_cave_width",          cave_width);
-       settings->setS16("mgv7_large_cave_depth",      large_cave_depth);
-       settings->setS16("mgv7_lava_depth",            lava_depth);
-       settings->setFloat("mgv7_float_mount_density", float_mount_density);
-       settings->setFloat("mgv7_float_mount_height",  float_mount_height);
-       settings->setS16("mgv7_floatland_level",       floatland_level);
-       settings->setS16("mgv7_shadow_limit",          shadow_limit);
-       settings->setS16("mgv7_cavern_limit",          cavern_limit);
-       settings->setS16("mgv7_cavern_taper",          cavern_taper);
-       settings->setFloat("mgv7_cavern_threshold",    cavern_threshold);
-
-       settings->setNoiseParams("mgv7_np_terrain_base",      np_terrain_base);
-       settings->setNoiseParams("mgv7_np_terrain_alt",       np_terrain_alt);
-       settings->setNoiseParams("mgv7_np_terrain_persist",   np_terrain_persist);
-       settings->setNoiseParams("mgv7_np_height_select",     np_height_select);
-       settings->setNoiseParams("mgv7_np_filler_depth",      np_filler_depth);
-       settings->setNoiseParams("mgv7_np_mount_height",      np_mount_height);
-       settings->setNoiseParams("mgv7_np_ridge_uwater",      np_ridge_uwater);
-       settings->setNoiseParams("mgv7_np_floatland_base",    np_floatland_base);
-       settings->setNoiseParams("mgv7_np_float_base_height", np_float_base_height);
-       settings->setNoiseParams("mgv7_np_mountain",          np_mountain);
-       settings->setNoiseParams("mgv7_np_ridge",             np_ridge);
-       settings->setNoiseParams("mgv7_np_cavern",            np_cavern);
-       settings->setNoiseParams("mgv7_np_cave1",             np_cave1);
-       settings->setNoiseParams("mgv7_np_cave2",             np_cave2);
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-int MapgenV7::getSpawnLevelAtPoint(v2s16 p)
-{
-       // If rivers are enabled, first check if in a river
-       if (spflags & MGV7_RIDGES) {
-               float width = 0.2;
-               float uwatern = NoisePerlin2D(&noise_ridge_uwater->np, p.X, p.Y, seed) * 2;
-               if (fabs(uwatern) <= width)
-                       return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
-       }
-
-       // Terrain noise 'offset' is the average level of that terrain.
-       // At least 50% of terrain will be below the higher of base and alt terrain
-       // 'offset's.
-       // Raising the maximum spawn level above 'water_level + 16' is necessary
-       // for when terrain 'offset's are set much higher than water_level.
-       s16 max_spawn_y = MYMAX(MYMAX(noise_terrain_alt->np.offset,
-                       noise_terrain_base->np.offset),
-                       water_level + 16);
-       // Base terrain calculation
-       s16 y = baseTerrainLevelAtPoint(p.X, p.Y);
-
-       // If mountains are disabled, terrain level is base terrain level.
-       // Avoids mid-air spawn where mountain terrain would have been.
-       if (!(spflags & MGV7_MOUNTAINS)) {
-               if (y < water_level || y > max_spawn_y)
-                       return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
-
-               // y + 2 because y is surface level and due to biome 'dust'
-               return y + 2;
-       }
-
-       // Search upwards for first node without mountain terrain
-       int iters = 256;
-       while (iters > 0 && y <= max_spawn_y) {
-               if (!getMountainTerrainAtPoint(p.X, y + 1, p.Y)) {
-                       if (y <= water_level || y > max_spawn_y)
-                               return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
-
-                       // y + 1 due to biome 'dust'
-                       return y + 1;
-               }
-               y++;
-               iters--;
-       }
-
-       // Unsuitable spawn point
-       return MAX_MAP_GENERATION_LIMIT;
-}
-
-
-void MapgenV7::makeChunk(BlockMakeData *data)
-{
-       // Pre-conditions
-       assert(data->vmanip);
-       assert(data->nodedef);
-       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
-               data->blockpos_requested.Y >= data->blockpos_min.Y &&
-               data->blockpos_requested.Z >= data->blockpos_min.Z);
-       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
-               data->blockpos_requested.Y <= data->blockpos_max.Y &&
-               data->blockpos_requested.Z <= data->blockpos_max.Z);
-
-       this->generating = true;
-       this->vm   = data->vmanip;
-       this->ndef = data->nodedef;
-       //TimeTaker t("makeChunk");
-
-       v3s16 blockpos_min = data->blockpos_min;
-       v3s16 blockpos_max = data->blockpos_max;
-       node_min = blockpos_min * MAP_BLOCKSIZE;
-       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
-       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-
-       blockseed = getBlockSeed2(full_node_min, seed);
-
-       // Generate base and mountain terrain
-       // An initial heightmap is no longer created here for use in generateRidgeTerrain()
-       s16 stone_surface_max_y = generateTerrain();
-
-       // Generate rivers
-       if (spflags & MGV7_RIDGES)
-               generateRidgeTerrain();
-
-       // Create heightmap
-       updateHeightmap(node_min, node_max);
-
-       // Init biome generator, place biome-specific nodes, and build biomemap
-       biomegen->calcBiomeNoise(node_min);
-
-       MgStoneType mgstone_type;
-       content_t biome_stone;
-       generateBiomes(&mgstone_type, &biome_stone);
-
-       // Generate caverns, tunnels and classic caves
-       if (flags & MG_CAVES) {
-               bool near_cavern = false;
-               // Generate caverns
-               if (spflags & MGV7_CAVERNS)
-                       near_cavern = generateCaverns(stone_surface_max_y);
-               // Generate tunnels and classic caves
-               if (near_cavern)
-                       // Disable classic caves in this mapchunk by setting
-                       // 'large cave depth' to world base. Avoids excessive liquid in
-                       // large caverns and floating blobs of overgenerated liquid.
-                       generateCaves(stone_surface_max_y, -MAX_MAP_GENERATION_LIMIT);
-               else
-                       generateCaves(stone_surface_max_y, large_cave_depth);
-       }
-
-       // Generate dungeons
-       if (flags & MG_DUNGEONS)
-               generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
-
-       // Generate the registered decorations
-       if (flags & MG_DECORATIONS)
-               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
-
-       // Generate the registered ores
-       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
-
-       // Sprinkle some dust on top after everything else was generated
-       dustTopNodes();
-
-       //printf("makeChunk: %dms\n", t.stop());
-
-       // Update liquids
-       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
-
-       // Calculate lighting
-       // Limit floatland shadow
-       bool propagate_shadow = !((spflags & MGV7_FLOATLANDS) &&
-               node_min.Y <= shadow_limit && node_max.Y >= shadow_limit);
-
-       if (flags & MG_LIGHT)
-               calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0),
-                       full_node_min, full_node_max, propagate_shadow);
-
-       //setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE,
-       //                      node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF);
-
-       this->generating = false;
-}
-
-
-float MapgenV7::baseTerrainLevelAtPoint(s16 x, s16 z)
-{
-       float hselect = NoisePerlin2D(&noise_height_select->np, x, z, seed);
-       hselect = rangelim(hselect, 0.0, 1.0);
-
-       float persist = NoisePerlin2D(&noise_terrain_persist->np, x, z, seed);
-
-       noise_terrain_base->np.persist = persist;
-       float height_base = NoisePerlin2D(&noise_terrain_base->np, x, z, seed);
-
-       noise_terrain_alt->np.persist = persist;
-       float height_alt = NoisePerlin2D(&noise_terrain_alt->np, x, z, seed);
-
-       if (height_alt > height_base)
-               return height_alt;
-
-       return (height_base * hselect) + (height_alt * (1.0 - hselect));
-}
-
-
-float MapgenV7::baseTerrainLevelFromMap(int index)
-{
-       float hselect     = rangelim(noise_height_select->result[index], 0.0, 1.0);
-       float height_base = noise_terrain_base->result[index];
-       float height_alt  = noise_terrain_alt->result[index];
-
-       if (height_alt > height_base)
-               return height_alt;
-
-       return (height_base * hselect) + (height_alt * (1.0 - hselect));
-}
-
-
-bool MapgenV7::getMountainTerrainAtPoint(s16 x, s16 y, s16 z)
-{
-       float mnt_h_n =
-                       MYMAX(NoisePerlin2D(&noise_mount_height->np, x, z, seed), 1.0f);
-       float density_gradient = -((float)(y - mount_zero_level) / mnt_h_n);
-       float mnt_n = NoisePerlin3D(&noise_mountain->np, x, y, z, seed);
-
-       return mnt_n + density_gradient >= 0.0;
-}
-
-
-bool MapgenV7::getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y)
-{
-       float mounthn = MYMAX(noise_mount_height->result[idx_xz], 1.0f);
-       float density_gradient = -((float)(y - mount_zero_level) / mounthn);
-       float mountn = noise_mountain->result[idx_xyz];
-
-       return mountn + density_gradient >= 0.0;
-}
-
-
-bool MapgenV7::getFloatlandMountainFromMap(int idx_xyz, int idx_xz, s16 y)
-{
-       // Make rim 2 nodes thick to match floatland base terrain
-       float density_gradient = (y >= floatland_level) ?
-               -pow((float)(y - floatland_level) / float_mount_height, 0.75f) :
-               -pow((float)(floatland_level - 1 - y) / float_mount_height, 0.75f);
-
-       float floatn = noise_mountain->result[idx_xyz] + float_mount_density;
-
-       return floatn + density_gradient >= 0.0f;
-}
-
-
-void MapgenV7::floatBaseExtentFromMap(s16 *float_base_min, s16 *float_base_max, int idx_xz)
-{
-       // '+1' to avoid a layer of stone at y = MAX_MAP_GENERATION_LIMIT
-       s16 base_min = MAX_MAP_GENERATION_LIMIT + 1;
-       s16 base_max = MAX_MAP_GENERATION_LIMIT;
-
-       float n_base = noise_floatland_base->result[idx_xz];
-       if (n_base > 0.0f) {
-               float n_base_height =
-                               MYMAX(noise_float_base_height->result[idx_xz], 1.0f);
-               float amp = n_base * n_base_height;
-               float ridge = n_base_height / 3.0f;
-               base_min = floatland_level - amp / 1.5f;
-
-               if (amp > ridge * 2.0f) {
-                       // Lake bed
-                       base_max = floatland_level - (amp - ridge * 2.0f) / 2.0f;
-               } else {
-                       // Hills and ridges
-                       float diff = fabs(amp - ridge) / ridge;
-                       // Smooth ridges using the 'smoothstep function'
-                       float smooth_diff = diff * diff * (3.0f - 2.0f * diff);
-                       base_max = floatland_level + ridge - smooth_diff * ridge;
-               }
-       }
-
-       *float_base_min = base_min;
-       *float_base_max = base_max;
-}
-
-
-int MapgenV7::generateTerrain()
-{
-       MapNode n_air(CONTENT_AIR);
-       MapNode n_stone(c_stone);
-       MapNode n_water(c_water_source);
-
-       //// Calculate noise for terrain generation
-       noise_terrain_persist->perlinMap2D(node_min.X, node_min.Z);
-       float *persistmap = noise_terrain_persist->result;
-
-       noise_terrain_base->perlinMap2D(node_min.X, node_min.Z, persistmap);
-       noise_terrain_alt->perlinMap2D(node_min.X, node_min.Z, persistmap);
-       noise_height_select->perlinMap2D(node_min.X, node_min.Z);
-
-       if ((spflags & MGV7_MOUNTAINS) || (spflags & MGV7_FLOATLANDS)) {
-               noise_mountain->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
-       }
-
-       if (spflags & MGV7_MOUNTAINS) {
-               noise_mount_height->perlinMap2D(node_min.X, node_min.Z);
-       }
-
-       if (spflags & MGV7_FLOATLANDS) {
-               noise_floatland_base->perlinMap2D(node_min.X, node_min.Z);
-               noise_float_base_height->perlinMap2D(node_min.X, node_min.Z);
-       }
-
-       //// Place nodes
-       const v3s16 &em = vm->m_area.getExtent();
-       s16 stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT;
-       u32 index2d = 0;
-
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 x = node_min.X; x <= node_max.X; x++, index2d++) {
-               s16 surface_y = baseTerrainLevelFromMap(index2d);
-               if (surface_y > stone_surface_max_y)
-                       stone_surface_max_y = surface_y;
-
-               // Get extent of floatland base terrain
-               // '+1' to avoid a layer of stone at y = MAX_MAP_GENERATION_LIMIT
-               s16 float_base_min = MAX_MAP_GENERATION_LIMIT + 1;
-               s16 float_base_max = MAX_MAP_GENERATION_LIMIT;
-               if (spflags & MGV7_FLOATLANDS)
-                       floatBaseExtentFromMap(&float_base_min, &float_base_max, index2d);
-
-               u32 vi = vm->m_area.index(x, node_min.Y - 1, z);
-               u32 index3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X);
-
-               for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
-                       if (vm->m_data[vi].getContent() == CONTENT_IGNORE) {
-                               if (y <= surface_y) {
-                                       vm->m_data[vi] = n_stone;  // Base terrain
-                               } else if ((spflags & MGV7_MOUNTAINS) &&
-                                               getMountainTerrainFromMap(index3d, index2d, y)) {
-                                       vm->m_data[vi] = n_stone;  // Mountain terrain
-                                       if (y > stone_surface_max_y)
-                                               stone_surface_max_y = y;
-                               } else if ((spflags & MGV7_FLOATLANDS) &&
-                                               ((y >= float_base_min && y <= float_base_max) ||
-                                               getFloatlandMountainFromMap(index3d, index2d, y))) {
-                                       vm->m_data[vi] = n_stone;  // Floatland terrain
-                                       stone_surface_max_y = node_max.Y;
-                               } else if (y <= water_level) {
-                                       vm->m_data[vi] = n_water;  // Ground level water
-                               } else if ((spflags & MGV7_FLOATLANDS) &&
-                                               (y >= float_base_max && y <= floatland_level)) {
-                                       vm->m_data[vi] = n_water;  // Floatland water
-                               } else {
-                                       vm->m_data[vi] = n_air;
-                               }
-                       }
-                       vm->m_area.add_y(em, vi, 1);
-                       index3d += ystride;
-               }
-       }
-
-       return stone_surface_max_y;
-}
-
-
-void MapgenV7::generateRidgeTerrain()
-{
-       if (node_max.Y < water_level - 16 ||
-                       ((spflags & MGV7_FLOATLANDS) && node_max.Y > shadow_limit))
-               return;
-
-       noise_ridge->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
-       noise_ridge_uwater->perlinMap2D(node_min.X, node_min.Z);
-
-       MapNode n_water(c_water_source);
-       MapNode n_air(CONTENT_AIR);
-       u32 index = 0;
-       float width = 0.2;
-
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
-               u32 vi = vm->m_area.index(node_min.X, y, z);
-               for (s16 x = node_min.X; x <= node_max.X; x++, index++, vi++) {
-                       int j = (z - node_min.Z) * csize.X + (x - node_min.X);
-
-                       float uwatern = noise_ridge_uwater->result[j] * 2;
-                       if (fabs(uwatern) > width)
-                               continue;
-
-                       float altitude = y - water_level;
-                       float height_mod = (altitude + 17) / 2.5;
-                       float width_mod  = width - fabs(uwatern);
-                       float nridge = noise_ridge->result[index] * MYMAX(altitude, 0) / 7.0;
-
-                       if (nridge + width_mod * height_mod < 0.6)
-                               continue;
-
-                       vm->m_data[vi] = (y > water_level) ? n_air : n_water;
-               }
-       }
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
-//// Code Boneyard
-////
-//// Much of the stuff here has potential to become useful again at some point
-//// in the future, but we don't want it to get lost or forgotten in version
-//// control.
-////
-
-#if 0
-int MapgenV7::generateMountainTerrain(s16 ymax)
-{
-       MapNode n_stone(c_stone);
-       u32 j = 0;
-
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
-               u32 vi = vm->m_area.index(node_min.X, y, z);
-               for (s16 x = node_min.X; x <= node_max.X; x++) {
-                       int index = (z - node_min.Z) * csize.X + (x - node_min.X);
-                       content_t c = vm->m_data[vi].getContent();
-
-                       if (getMountainTerrainFromMap(j, index, y)
-                                       && (c == CONTENT_AIR || c == c_water_source)) {
-                               vm->m_data[vi] = n_stone;
-                               if (y > ymax)
-                                       ymax = y;
-                       }
-
-                       vi++;
-                       j++;
-               }
-       }
-
-       return ymax;
-}
-#endif
-
-
-#if 0
-void MapgenV7::carveRivers() {
-       MapNode n_air(CONTENT_AIR), n_water_source(c_water_source);
-       MapNode n_stone(c_stone);
-       u32 index = 0;
-
-       int river_depth = 4;
-
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
-               float terrain_mod  = noise_terrain_mod->result[index];
-               NoiseParams *np = noise_terrain_river->np;
-               np.persist = noise_terrain_persist->result[index];
-               float terrain_river = NoisePerlin2DNoTxfm(np, x, z, seed);
-               float height = terrain_river * (1 - abs(terrain_mod)) *
-                                               noise_terrain_river->np.scale;
-               height = log(height * height); //log(h^3) is pretty interesting for terrain
-
-               s16 y = heightmap[index];
-               if (height < 1.0 && y > river_depth &&
-                       y - river_depth >= node_min.Y && y <= node_max.Y) {
-
-                       for (s16 ry = y; ry != y - river_depth; ry--) {
-                               u32 vi = vm->m_area.index(x, ry, z);
-                               vm->m_data[vi] = n_air;
-                       }
-
-                       u32 vi = vm->m_area.index(x, y - river_depth, z);
-                       vm->m_data[vi] = n_water_source;
-               }
-       }
-}
-#endif
-
-
-#if 0
-void MapgenV7::addTopNodes()
-{
-       v3s16 em = vm->m_area.getExtent();
-       s16 ntopnodes;
-       u32 index = 0;
-
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
-               Biome *biome = bmgr->biomes[biomemap[index]];
-
-               //////////////////// First, add top nodes below the ridge
-               s16 y = ridge_heightmap[index];
-
-               // This cutoff is good enough, but not perfect.
-               // It will cut off potentially placed top nodes at chunk boundaries
-               if (y < node_min.Y)
-                       continue;
-               if (y > node_max.Y) {
-                       y = node_max.Y; // Let's see if we can still go downward anyway
-                       u32 vi = vm->m_area.index(x, y, z);
-                       content_t c = vm->m_data[vi].getContent();
-                       if (ndef->get(c).walkable)
-                               continue;
-               }
-
-               // N.B.  It is necessary to search downward since ridge_heightmap[i]
-               // might not be the actual height, just the lowest part in the chunk
-               // where a ridge had been carved
-               u32 i = vm->m_area.index(x, y, z);
-               for (; y >= node_min.Y; y--) {
-                       content_t c = vm->m_data[i].getContent();
-                       if (ndef->get(c).walkable)
-                               break;
-                       vm->m_area.add_y(em, i, -1);
-               }
-
-               if (y != node_min.Y - 1 && y >= water_level) {
-                       ridge_heightmap[index] = y; //update ridgeheight
-                       ntopnodes = biome->top_depth;
-                       for (; y <= node_max.Y && ntopnodes; y++) {
-                               ntopnodes--;
-                               vm->m_data[i] = MapNode(biome->c_top);
-                               vm->m_area.add_y(em, i, 1);
-                       }
-                       // If dirt, grow grass on it.
-                       if (y > water_level - 10 &&
-                               vm->m_data[i].getContent() == CONTENT_AIR) {
-                               vm->m_area.add_y(em, i, -1);
-                               if (vm->m_data[i].getContent() == c_dirt)
-                                       vm->m_data[i] = MapNode(c_dirt_with_grass);
-                       }
-               }
-
-               //////////////////// Now, add top nodes on top of the ridge
-               y = heightmap[index];
-               if (y > node_max.Y) {
-                       y = node_max.Y; // Let's see if we can still go downward anyway
-                       u32 vi = vm->m_area.index(x, y, z);
-                       content_t c = vm->m_data[vi].getContent();
-                       if (ndef->get(c).walkable)
-                               continue;
-               }
-
-               i = vm->m_area.index(x, y, z);
-               for (; y >= node_min.Y; y--) {
-                       content_t c = vm->m_data[i].getContent();
-                       if (ndef->get(c).walkable)
-                               break;
-                       vm->m_area.add_y(em, i, -1);
-               }
-
-               if (y != node_min.Y - 1) {
-                       ntopnodes = biome->top_depth;
-                       // Let's see if we've already added it...
-                       if (y == ridge_heightmap[index] + ntopnodes - 1)
-                               continue;
-
-                       for (; y <= node_max.Y && ntopnodes; y++) {
-                               ntopnodes--;
-                               vm->m_data[i] = MapNode(biome->c_top);
-                               vm->m_area.add_y(em, i, 1);
-                       }
-                       // If dirt, grow grass on it.
-                       if (y > water_level - 10 &&
-                               vm->m_data[i].getContent() == CONTENT_AIR) {
-                               vm->m_area.add_y(em, i, -1);
-                               if (vm->m_data[i].getContent() == c_dirt)
-                                       vm->m_data[i] = MapNode(c_dirt_with_grass);
-                       }
-               }
-       }
-}
-#endif
diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h
deleted file mode 100644 (file)
index 6fb7dc4..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2014-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "mapgen.h"
-
-///////////// Mapgen V7 flags
-#define MGV7_MOUNTAINS   0x01
-#define MGV7_RIDGES      0x02
-#define MGV7_FLOATLANDS  0x04
-#define MGV7_CAVERNS     0x08
-#define MGV7_BIOMEREPEAT 0x10 // Now unused
-
-class BiomeManager;
-
-extern FlagDesc flagdesc_mapgen_v7[];
-
-
-struct MapgenV7Params : public MapgenParams {
-       u32 spflags = MGV7_MOUNTAINS | MGV7_RIDGES | MGV7_CAVERNS;
-       s16 mount_zero_level = 0;
-       float cave_width = 0.09f;
-       s16 large_cave_depth = -33;
-       s16 lava_depth = -256;
-       float float_mount_density = 0.6f;
-       float float_mount_height = 128.0f;
-       s16 floatland_level = 1280;
-       s16 shadow_limit = 1024;
-       s16 cavern_limit = -256;
-       s16 cavern_taper = 256;
-       float cavern_threshold = 0.7f;
-
-       NoiseParams np_terrain_base;
-       NoiseParams np_terrain_alt;
-       NoiseParams np_terrain_persist;
-       NoiseParams np_height_select;
-       NoiseParams np_filler_depth;
-       NoiseParams np_mount_height;
-       NoiseParams np_ridge_uwater;
-       NoiseParams np_floatland_base;
-       NoiseParams np_float_base_height;
-       NoiseParams np_mountain;
-       NoiseParams np_ridge;
-       NoiseParams np_cavern;
-       NoiseParams np_cave1;
-       NoiseParams np_cave2;
-
-       MapgenV7Params();
-       ~MapgenV7Params() = default;
-
-       void readParams(const Settings *settings);
-       void writeParams(Settings *settings) const;
-};
-
-class MapgenV7 : public MapgenBasic {
-public:
-       MapgenV7(int mapgenid, MapgenV7Params *params, EmergeManager *emerge);
-       ~MapgenV7();
-
-       virtual MapgenType getType() const { return MAPGEN_V7; }
-
-       virtual void makeChunk(BlockMakeData *data);
-       int getSpawnLevelAtPoint(v2s16 p);
-
-       float baseTerrainLevelAtPoint(s16 x, s16 z);
-       float baseTerrainLevelFromMap(int index);
-       bool getMountainTerrainAtPoint(s16 x, s16 y, s16 z);
-       bool getMountainTerrainFromMap(int idx_xyz, int idx_xz, s16 y);
-       bool getFloatlandMountainFromMap(int idx_xyz, int idx_xz, s16 y);
-       void floatBaseExtentFromMap(s16 *float_base_min, s16 *float_base_max, int idx_xz);
-
-       int generateTerrain();
-       void generateRidgeTerrain();
-
-private:
-       s16 mount_zero_level;
-       s16 large_cave_depth;
-       float float_mount_density;
-       float float_mount_height;
-       s16 floatland_level;
-       s16 shadow_limit;
-
-       Noise *noise_terrain_base;
-       Noise *noise_terrain_alt;
-       Noise *noise_terrain_persist;
-       Noise *noise_height_select;
-       Noise *noise_mount_height;
-       Noise *noise_ridge_uwater;
-       Noise *noise_floatland_base;
-       Noise *noise_float_base_height;
-       Noise *noise_mountain;
-       Noise *noise_ridge;
-};
diff --git a/src/mapgen_valleys.cpp b/src/mapgen_valleys.cpp
deleted file mode 100644 (file)
index a13bb45..0000000
+++ /dev/null
@@ -1,743 +0,0 @@
-/*
-Minetest Valleys C
-Copyright (C) 2016-2017 Duane Robertson <duane@duanerobertson.com>
-Copyright (C) 2016-2017 paramat
-
-Based on Valleys Mapgen by Gael de Sailly
- (https://forum.minetest.net/viewtopic.php?f=9&t=11430)
-and mapgen_v7, mapgen_flat by kwolekr and paramat.
-
-Licensing changed by permission of Gael de Sailly.
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "mapgen.h"
-#include "voxel.h"
-#include "noise.h"
-#include "mapblock.h"
-#include "mapnode.h"
-#include "map.h"
-#include "nodedef.h"
-#include "voxelalgorithms.h"
-#include "settings.h" // For g_settings
-#include "emerge.h"
-#include "dungeongen.h"
-#include "mg_biome.h"
-#include "mg_ore.h"
-#include "mg_decoration.h"
-#include "mapgen_valleys.h"
-#include "cavegen.h"
-
-
-//#undef NDEBUG
-//#include "assert.h"
-
-//#include "util/timetaker.h"
-//#include "profiler.h"
-
-
-//static Profiler mapgen_prof;
-//Profiler *mapgen_profiler = &mapgen_prof;
-
-static FlagDesc flagdesc_mapgen_valleys[] = {
-       {"altitude_chill", MGVALLEYS_ALT_CHILL},
-       {"humid_rivers",   MGVALLEYS_HUMID_RIVERS},
-       {NULL,             0}
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-MapgenValleys::MapgenValleys(int mapgenid, MapgenValleysParams *params, EmergeManager *emerge)
-       : MapgenBasic(mapgenid, params, emerge)
-{
-       // NOTE: MapgenValleys has a hard dependency on BiomeGenOriginal
-       m_bgen = (BiomeGenOriginal *)biomegen;
-
-       BiomeParamsOriginal *bp = (BiomeParamsOriginal *)params->bparams;
-
-       spflags            = params->spflags;
-       altitude_chill     = params->altitude_chill;
-       large_cave_depth   = params->large_cave_depth;
-       lava_features_lim  = rangelim(params->lava_features, 0, 10);
-       massive_cave_depth = params->massive_cave_depth;
-       river_depth_bed    = params->river_depth + 1.f;
-       river_size_factor  = params->river_size / 100.f;
-       water_features_lim = rangelim(params->water_features, 0, 10);
-       cave_width         = params->cave_width;
-
-       //// 2D Terrain noise
-       noise_filler_depth       = new Noise(&params->np_filler_depth,       seed, csize.X, csize.Z);
-       noise_inter_valley_slope = new Noise(&params->np_inter_valley_slope, seed, csize.X, csize.Z);
-       noise_rivers             = new Noise(&params->np_rivers,             seed, csize.X, csize.Z);
-       noise_terrain_height     = new Noise(&params->np_terrain_height,     seed, csize.X, csize.Z);
-       noise_valley_depth       = new Noise(&params->np_valley_depth,       seed, csize.X, csize.Z);
-       noise_valley_profile     = new Noise(&params->np_valley_profile,     seed, csize.X, csize.Z);
-
-       //// 3D Terrain noise
-       // 1-up 1-down overgeneration
-       noise_inter_valley_fill = new Noise(&params->np_inter_valley_fill, seed, csize.X, csize.Y + 2, csize.Z);
-       // 1-down overgeneraion
-       noise_cave1             = new Noise(&params->np_cave1,             seed, csize.X, csize.Y + 1, csize.Z);
-       noise_cave2             = new Noise(&params->np_cave2,             seed, csize.X, csize.Y + 1, csize.Z);
-       noise_massive_caves     = new Noise(&params->np_massive_caves,     seed, csize.X, csize.Y + 1, csize.Z);
-
-       humid_rivers       = (spflags & MGVALLEYS_HUMID_RIVERS);
-       use_altitude_chill = (spflags & MGVALLEYS_ALT_CHILL);
-       humidity_adjust    = bp->np_humidity.offset - 50.f;
-
-       // a small chance of overflows if the settings are very high
-       cave_water_max_height = water_level + MYMAX(0, water_features_lim - 4) * 50;
-       lava_max_height       = water_level + MYMAX(0, lava_features_lim - 4) * 50;
-
-       tcave_cache = new float[csize.Y + 2];
-}
-
-
-MapgenValleys::~MapgenValleys()
-{
-       delete noise_cave1;
-       delete noise_cave2;
-       delete noise_filler_depth;
-       delete noise_inter_valley_fill;
-       delete noise_inter_valley_slope;
-       delete noise_rivers;
-       delete noise_massive_caves;
-       delete noise_terrain_height;
-       delete noise_valley_depth;
-       delete noise_valley_profile;
-
-       delete[] tcave_cache;
-}
-
-
-MapgenValleysParams::MapgenValleysParams():
-       np_cave1              (0,     12,   v3f(61,   61,   61),   52534, 3, 0.5,   2.0),
-       np_cave2              (0,     12,   v3f(67,   67,   67),   10325, 3, 0.5,   2.0),
-       np_filler_depth       (0.f,   1.2f, v3f(256,  256,  256),  1605,  3, 0.5f,  2.f),
-       np_inter_valley_fill  (0.f,   1.f,  v3f(256,  512,  256),  1993,  6, 0.8f,  2.f),
-       np_inter_valley_slope (0.5f,  0.5f, v3f(128,  128,  128),  746,   1, 1.f,   2.f),
-       np_rivers             (0.f,   1.f,  v3f(256,  256,  256),  -6050, 5, 0.6f,  2.f),
-       np_massive_caves      (0.f,   1.f,  v3f(768,  256,  768),  59033, 6, 0.63f, 2.f),
-       np_terrain_height     (-10.f, 50.f, v3f(1024, 1024, 1024), 5202,  6, 0.4f,  2.f),
-       np_valley_depth       (5.f,   4.f,  v3f(512,  512,  512),  -1914, 1, 1.f,   2.f),
-       np_valley_profile     (0.6f,  0.5f, v3f(512,  512,  512),  777,   1, 1.f,   2.f)
-{
-}
-
-
-void MapgenValleysParams::readParams(const Settings *settings)
-{
-       settings->getFlagStrNoEx("mgvalleys_spflags",        spflags, flagdesc_mapgen_valleys);
-       settings->getU16NoEx("mgvalleys_altitude_chill",     altitude_chill);
-       settings->getS16NoEx("mgvalleys_large_cave_depth",   large_cave_depth);
-       settings->getU16NoEx("mgvalleys_lava_features",      lava_features);
-       settings->getS16NoEx("mgvalleys_massive_cave_depth", massive_cave_depth);
-       settings->getU16NoEx("mgvalleys_river_depth",        river_depth);
-       settings->getU16NoEx("mgvalleys_river_size",         river_size);
-       settings->getU16NoEx("mgvalleys_water_features",     water_features);
-       settings->getFloatNoEx("mgvalleys_cave_width",       cave_width);
-
-       settings->getNoiseParams("mgvalleys_np_cave1",              np_cave1);
-       settings->getNoiseParams("mgvalleys_np_cave2",              np_cave2);
-       settings->getNoiseParams("mgvalleys_np_filler_depth",       np_filler_depth);
-       settings->getNoiseParams("mgvalleys_np_inter_valley_fill",  np_inter_valley_fill);
-       settings->getNoiseParams("mgvalleys_np_inter_valley_slope", np_inter_valley_slope);
-       settings->getNoiseParams("mgvalleys_np_rivers",             np_rivers);
-       settings->getNoiseParams("mgvalleys_np_massive_caves",      np_massive_caves);
-       settings->getNoiseParams("mgvalleys_np_terrain_height",     np_terrain_height);
-       settings->getNoiseParams("mgvalleys_np_valley_depth",       np_valley_depth);
-       settings->getNoiseParams("mgvalleys_np_valley_profile",     np_valley_profile);
-}
-
-
-void MapgenValleysParams::writeParams(Settings *settings) const
-{
-       settings->setFlagStr("mgvalleys_spflags",        spflags, flagdesc_mapgen_valleys, U32_MAX);
-       settings->setU16("mgvalleys_altitude_chill",     altitude_chill);
-       settings->setS16("mgvalleys_large_cave_depth",   large_cave_depth);
-       settings->setU16("mgvalleys_lava_features",      lava_features);
-       settings->setS16("mgvalleys_massive_cave_depth", massive_cave_depth);
-       settings->setU16("mgvalleys_river_depth",        river_depth);
-       settings->setU16("mgvalleys_river_size",         river_size);
-       settings->setU16("mgvalleys_water_features",     water_features);
-       settings->setFloat("mgvalleys_cave_width",       cave_width);
-
-       settings->setNoiseParams("mgvalleys_np_cave1",              np_cave1);
-       settings->setNoiseParams("mgvalleys_np_cave2",              np_cave2);
-       settings->setNoiseParams("mgvalleys_np_filler_depth",       np_filler_depth);
-       settings->setNoiseParams("mgvalleys_np_inter_valley_fill",  np_inter_valley_fill);
-       settings->setNoiseParams("mgvalleys_np_inter_valley_slope", np_inter_valley_slope);
-       settings->setNoiseParams("mgvalleys_np_rivers",             np_rivers);
-       settings->setNoiseParams("mgvalleys_np_massive_caves",      np_massive_caves);
-       settings->setNoiseParams("mgvalleys_np_terrain_height",     np_terrain_height);
-       settings->setNoiseParams("mgvalleys_np_valley_depth",       np_valley_depth);
-       settings->setNoiseParams("mgvalleys_np_valley_profile",     np_valley_profile);
-}
-
-
-///////////////////////////////////////
-
-
-void MapgenValleys::makeChunk(BlockMakeData *data)
-{
-       // Pre-conditions
-       assert(data->vmanip);
-       assert(data->nodedef);
-       assert(data->blockpos_requested.X >= data->blockpos_min.X &&
-               data->blockpos_requested.Y >= data->blockpos_min.Y &&
-               data->blockpos_requested.Z >= data->blockpos_min.Z);
-       assert(data->blockpos_requested.X <= data->blockpos_max.X &&
-               data->blockpos_requested.Y <= data->blockpos_max.Y &&
-               data->blockpos_requested.Z <= data->blockpos_max.Z);
-
-       this->generating = true;
-       this->vm = data->vmanip;
-       this->ndef = data->nodedef;
-
-       //TimeTaker t("makeChunk");
-
-       v3s16 blockpos_min = data->blockpos_min;
-       v3s16 blockpos_max = data->blockpos_max;
-       node_min = blockpos_min * MAP_BLOCKSIZE;
-       node_max = (blockpos_max + v3s16(1, 1, 1)) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-       full_node_min = (blockpos_min - 1) * MAP_BLOCKSIZE;
-       full_node_max = (blockpos_max + 2) * MAP_BLOCKSIZE - v3s16(1, 1, 1);
-
-       blockseed = getBlockSeed2(full_node_min, seed);
-
-       // Generate biome noises.  Note this must be executed strictly before
-       // generateTerrain, because generateTerrain depends on intermediate
-       // biome-related noises.
-       m_bgen->calcBiomeNoise(node_min);
-
-       // Generate noise maps and base terrain height.
-       // Modify heat and humidity maps.
-       calculateNoise();
-
-       // Generate base terrain with initial heightmaps
-       s16 stone_surface_max_y = generateTerrain();
-
-       // Recalculate heightmap
-       updateHeightmap(node_min, node_max);
-
-       // Place biome-specific nodes and build biomemap
-       MgStoneType mgstone_type;
-       content_t biome_stone;
-       generateBiomes(&mgstone_type, &biome_stone);
-
-       // Cave creation.
-       if (flags & MG_CAVES)
-               generateCaves(stone_surface_max_y, large_cave_depth);
-
-       // Dungeon creation
-       if ((flags & MG_DUNGEONS) && node_max.Y < 50)
-               generateDungeons(stone_surface_max_y, mgstone_type, biome_stone);
-
-       // Generate the registered decorations
-       if (flags & MG_DECORATIONS)
-               m_emerge->decomgr->placeAllDecos(this, blockseed, node_min, node_max);
-
-       // Generate the registered ores
-       m_emerge->oremgr->placeAllOres(this, blockseed, node_min, node_max);
-
-       // Sprinkle some dust on top after everything else was generated
-       dustTopNodes();
-
-       //TimeTaker tll("liquid_lighting");
-
-       updateLiquid(&data->transforming_liquid, full_node_min, full_node_max);
-
-       if (flags & MG_LIGHT)
-               calcLighting(
-                               node_min - v3s16(0, 1, 0),
-                               node_max + v3s16(0, 1, 0),
-                               full_node_min,
-                               full_node_max);
-
-       //mapgen_profiler->avg("liquid_lighting", tll.stop() / 1000.f);
-       //mapgen_profiler->avg("makeChunk", t.stop() / 1000.f);
-
-       this->generating = false;
-}
-
-
-// Populate the noise tables and do most of the
-// calculation necessary to determine terrain height.
-void MapgenValleys::calculateNoise()
-{
-       //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO);
-
-       int x = node_min.X;
-       int y = node_min.Y - 1;
-       int z = node_min.Z;
-
-       //TimeTaker tcn("actualNoise");
-
-       noise_inter_valley_slope->perlinMap2D(x, z);
-       noise_rivers->perlinMap2D(x, z);
-       noise_terrain_height->perlinMap2D(x, z);
-       noise_valley_depth->perlinMap2D(x, z);
-       noise_valley_profile->perlinMap2D(x, z);
-
-       noise_inter_valley_fill->perlinMap3D(x, y, z);
-
-       //mapgen_profiler->avg("noisemaps", tcn.stop() / 1000.f);
-
-       float heat_offset = 0.f;
-       float humidity_scale = 1.f;
-
-       // Altitude chill tends to reduce the average heat.
-       if (use_altitude_chill)
-               heat_offset = 5.f;
-
-       // River humidity tends to increase the humidity range.
-       if (humid_rivers) {
-               humidity_scale = 0.8f;
-       }
-
-       for (s32 index = 0; index < csize.X * csize.Z; index++) {
-               m_bgen->heatmap[index] += heat_offset;
-               m_bgen->humidmap[index] *= humidity_scale;
-       }
-
-       TerrainNoise tn;
-
-       u32 index = 0;
-       for (tn.z = node_min.Z; tn.z <= node_max.Z; tn.z++)
-       for (tn.x = node_min.X; tn.x <= node_max.X; tn.x++, index++) {
-               // The parameters that we actually need to generate terrain
-               //  are passed by address (and the return value).
-               tn.terrain_height    = noise_terrain_height->result[index];
-               // River noise is replaced with base terrain, which
-               // is basically the height of the water table.
-               tn.rivers            = &noise_rivers->result[index];
-               // Valley depth noise is replaced with the valley
-               // number that represents the height of terrain
-               // over rivers and is used to determine about
-               // how close a river is for humidity calculation.
-               tn.valley            = &noise_valley_depth->result[index];
-               tn.valley_profile    = noise_valley_profile->result[index];
-               // Slope noise is replaced by the calculated slope
-               // which is used to get terrain height in the slow
-               // method, to create sharper mountains.
-               tn.slope             = &noise_inter_valley_slope->result[index];
-               tn.inter_valley_fill = noise_inter_valley_fill->result[index];
-
-               // This is the actual terrain height.
-               float mount = terrainLevelFromNoise(&tn);
-               noise_terrain_height->result[index] = mount;
-       }
-}
-
-
-// This keeps us from having to maintain two similar sets of
-//  complicated code to determine ground level.
-float MapgenValleys::terrainLevelFromNoise(TerrainNoise *tn)
-{
-       // The square function changes the behaviour of this noise:
-       //  very often small, and sometimes very high.
-       float valley_d = MYSQUARE(*tn->valley);
-
-       // valley_d is here because terrain is generally higher where valleys
-       //  are deep (mountains). base represents the height of the
-       //  rivers, most of the surface is above.
-       float base = tn->terrain_height + valley_d;
-
-       // "river" represents the distance from the river, in arbitrary units.
-       float river = fabs(*tn->rivers) - river_size_factor;
-
-       // Use the curve of the function 1-exp(-(x/a)^2) to model valleys.
-       //  Making "a" vary (0 < a <= 1) changes the shape of the valleys.
-       //  Try it with a geometry software !
-       //   (here x = "river" and a = valley_profile).
-       //  "valley" represents the height of the terrain, from the rivers.
-       {
-               float t = river / tn->valley_profile;
-               *tn->valley = valley_d * (1.f - exp(- MYSQUARE(t)));
-       }
-
-       // approximate height of the terrain at this point
-       float mount = base + *tn->valley;
-
-       *tn->slope *= *tn->valley;
-
-       // Rivers are placed where "river" is negative, so where the original
-       //  noise value is close to zero.
-       // Base ground is returned as rivers since it's basically the water table.
-       *tn->rivers = base;
-       if (river < 0.f) {
-               // Use the the function -sqrt(1-x^2) which models a circle.
-               float depth;
-               {
-                       float t = river / river_size_factor + 1;
-                       depth = (river_depth_bed * sqrt(MYMAX(0, 1.f - MYSQUARE(t))));
-               }
-
-               // base - depth : height of the bottom of the river
-               // water_level - 3 : don't make rivers below 3 nodes under the surface
-               // We use three because that's as low as the swamp biomes go.
-               // There is no logical equivalent to this using rangelim.
-               mount = MYMIN(MYMAX(base - depth, (float)(water_level - 3)), mount);
-
-               // Slope has no influence on rivers.
-               *tn->slope = 0.f;
-       }
-
-       return mount;
-}
-
-
-// This avoids duplicating the code in terrainLevelFromNoise, adding
-// only the final step of terrain generation without a noise map.
-float MapgenValleys::adjustedTerrainLevelFromNoise(TerrainNoise *tn)
-{
-       float mount = terrainLevelFromNoise(tn);
-       s16 y_start = myround(mount);
-
-       for (s16 y = y_start; y <= y_start + 1000; y++) {
-               float fill = NoisePerlin3D(&noise_inter_valley_fill->np, tn->x, y, tn->z, seed);
-
-               if (fill * *tn->slope < y - mount) {
-                       mount = MYMAX(y - 1, mount);
-                       break;
-               }
-       }
-
-       return mount;
-}
-
-
-int MapgenValleys::getSpawnLevelAtPoint(v2s16 p)
-{
-       // Check to make sure this isn't a request for a location in a river.
-       float rivers = NoisePerlin2D(&noise_rivers->np, p.X, p.Y, seed);
-       if (fabs(rivers) < river_size_factor)
-               return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
-
-       s16 level_at_point = terrainLevelAtPoint(p.X, p.Y);
-       if (level_at_point <= water_level ||
-                       level_at_point > water_level + 32)
-               return MAX_MAP_GENERATION_LIMIT;  // Unsuitable spawn point
-
-       return level_at_point;
-}
-
-
-float MapgenValleys::terrainLevelAtPoint(s16 x, s16 z)
-{
-       TerrainNoise tn;
-
-       float rivers = NoisePerlin2D(&noise_rivers->np, x, z, seed);
-       float valley = NoisePerlin2D(&noise_valley_depth->np, x, z, seed);
-       float inter_valley_slope = NoisePerlin2D(&noise_inter_valley_slope->np, x, z, seed);
-
-       tn.x                 = x;
-       tn.z                 = z;
-       tn.terrain_height    = NoisePerlin2D(&noise_terrain_height->np, x, z, seed);
-       tn.rivers            = &rivers;
-       tn.valley            = &valley;
-       tn.valley_profile    = NoisePerlin2D(&noise_valley_profile->np, x, z, seed);
-       tn.slope             = &inter_valley_slope;
-       tn.inter_valley_fill = 0.f;
-
-       return adjustedTerrainLevelFromNoise(&tn);
-}
-
-
-int MapgenValleys::generateTerrain()
-{
-       // Raising this reduces the rate of evaporation.
-       static const float evaporation = 300.f;
-       // from the lua
-       static const float humidity_dropoff = 4.f;
-       // constant to convert altitude chill (compatible with lua) to heat
-       static const float alt_to_heat = 20.f;
-       // humidity reduction by altitude
-       static const float alt_to_humid = 10.f;
-
-       MapNode n_air(CONTENT_AIR);
-       MapNode n_river_water(c_river_water_source);
-       MapNode n_stone(c_stone);
-       MapNode n_water(c_water_source);
-
-       const v3s16 &em = vm->m_area.getExtent();
-       s16 surface_max_y = -MAX_MAP_GENERATION_LIMIT;
-       u32 index_2d = 0;
-
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
-               float river_y = noise_rivers->result[index_2d];
-               float surface_y = noise_terrain_height->result[index_2d];
-               float slope = noise_inter_valley_slope->result[index_2d];
-               float t_heat = m_bgen->heatmap[index_2d];
-
-               heightmap[index_2d] = -MAX_MAP_GENERATION_LIMIT;
-
-               if (surface_y > surface_max_y)
-                       surface_max_y = ceil(surface_y);
-
-               if (humid_rivers) {
-                       // Derive heat from (base) altitude. This will be most correct
-                       // at rivers, since other surface heights may vary below.
-                       if (use_altitude_chill && (surface_y > 0.f || river_y > 0.f))
-                               t_heat -= alt_to_heat * MYMAX(surface_y, river_y) / altitude_chill;
-
-                       // If humidity is low or heat is high, lower the water table.
-                       float delta = m_bgen->humidmap[index_2d] - 50.f;
-                       if (delta < 0.f) {
-                               float t_evap = (t_heat - 32.f) / evaporation;
-                               river_y += delta * MYMAX(t_evap, 0.08f);
-                       }
-               }
-
-               u32 index_3d = (z - node_min.Z) * zstride_1u1d + (x - node_min.X);
-               u32 index_data = vm->m_area.index(x, node_min.Y - 1, z);
-
-               // Mapgens concern themselves with stone and water.
-               for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) {
-                       if (vm->m_data[index_data].getContent() == CONTENT_IGNORE) {
-                               float fill = noise_inter_valley_fill->result[index_3d];
-                               float surface_delta = (float)y - surface_y;
-                               bool river = y + 1 < river_y;
-
-                               if (slope * fill > surface_delta) {
-                                       // ground
-                                       vm->m_data[index_data] = n_stone;
-                                       if (y > heightmap[index_2d])
-                                               heightmap[index_2d] = y;
-                                       if (y > surface_max_y)
-                                               surface_max_y = y;
-                               } else if (y <= water_level) {
-                                       // sea
-                                       vm->m_data[index_data] = n_water;
-                               } else if (river) {
-                                       // river
-                                       vm->m_data[index_data] = n_river_water;
-                               } else {  // air
-                                       vm->m_data[index_data] = n_air;
-                               }
-                       }
-
-                       vm->m_area.add_y(em, index_data, 1);
-                       index_3d += ystride;
-               }
-
-               if (heightmap[index_2d] == -MAX_MAP_GENERATION_LIMIT) {
-                       s16 surface_y_int = myround(surface_y);
-                       if (surface_y_int > node_max.Y + 1 || surface_y_int < node_min.Y - 1) {
-                               // If surface_y is outside the chunk, it's good enough.
-                               heightmap[index_2d] = surface_y_int;
-                       } else {
-                               // If the ground is outside of this chunk, but surface_y
-                               // is within the chunk, give a value outside.
-                               heightmap[index_2d] = node_min.Y - 2;
-                       }
-               }
-
-               if (humid_rivers) {
-                       // Use base ground (water table) in a riverbed, to
-                       // avoid an unnatural rise in humidity.
-                       float t_alt = MYMAX(noise_rivers->result[index_2d], (float)heightmap[index_2d]);
-                       float humid = m_bgen->humidmap[index_2d];
-                       float water_depth = (t_alt - river_y) / humidity_dropoff;
-                       humid *= 1.f + pow(0.5f, MYMAX(water_depth, 1.f));
-
-                       // Reduce humidity with altitude (ignoring riverbeds).
-                       // This is similar to the lua version's seawater adjustment,
-                       // but doesn't increase the base humidity, which causes
-                       // problems with the default biomes.
-                       if (t_alt > 0.f)
-                               humid -= alt_to_humid * t_alt / altitude_chill;
-
-                       m_bgen->humidmap[index_2d] = humid;
-               }
-
-               // Assign the heat adjusted by any changed altitudes.
-               // The altitude will change about half the time.
-               if (use_altitude_chill) {
-                       // ground height ignoring riverbeds
-                       float t_alt = MYMAX(noise_rivers->result[index_2d], (float)heightmap[index_2d]);
-                       if (humid_rivers && heightmap[index_2d] == (s16)myround(surface_y))
-                               // The altitude hasn't changed. Use the first result.
-                               m_bgen->heatmap[index_2d] = t_heat;
-                       else if (t_alt > 0.f)
-                               m_bgen->heatmap[index_2d] -= alt_to_heat * t_alt / altitude_chill;
-               }
-       }
-
-       return surface_max_y;
-}
-
-void MapgenValleys::generateCaves(s16 max_stone_y, s16 large_cave_depth)
-{
-       if (max_stone_y < node_min.Y)
-               return;
-
-       noise_cave1->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
-       noise_cave2->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
-
-       PseudoRandom ps(blockseed + 72202);
-
-       MapNode n_air(CONTENT_AIR);
-       MapNode n_lava(c_lava_source);
-       MapNode n_water(c_river_water_source);
-
-       const v3s16 &em = vm->m_area.getExtent();
-
-       // Cave blend distance near YMIN, YMAX
-       const float massive_cave_blend = 128.f;
-       // noise threshold for massive caves
-       const float massive_cave_threshold = 0.6f;
-       // mct: 1 = small rare caves, 0.5 1/3rd ground volume, 0 = 1/2 ground volume.
-
-       float yblmin = -mapgen_limit + massive_cave_blend * 1.5f;
-       float yblmax = massive_cave_depth - massive_cave_blend * 1.5f;
-       bool made_a_big_one = false;
-
-       // Cache the tcave values as they only vary by altitude.
-       if (node_max.Y <= massive_cave_depth) {
-               noise_massive_caves->perlinMap3D(node_min.X, node_min.Y - 1, node_min.Z);
-
-               for (s16 y = node_min.Y - 1; y <= node_max.Y; y++) {
-                       float tcave = massive_cave_threshold;
-
-                       if (y < yblmin) {
-                               float t = (yblmin - y) / massive_cave_blend;
-                               tcave += MYSQUARE(t);
-                       } else if (y > yblmax) {
-                               float t = (y - yblmax) / massive_cave_blend;
-                               tcave += MYSQUARE(t);
-                       }
-
-                       tcave_cache[y - node_min.Y + 1] = tcave;
-               }
-       }
-
-       // lava_depth varies between one and ten as you approach
-       //  the bottom of the world.
-       s16 lava_depth = ceil((lava_max_height - node_min.Y + 1) * 10.f / mapgen_limit);
-       // This allows random lava spawns to be less common at the surface.
-       s16 lava_chance = MYCUBE(lava_features_lim) * lava_depth;
-       // water_depth varies between ten and one on the way down.
-       s16 water_depth = ceil((mapgen_limit - abs(node_min.Y) + 1) * 10.f / mapgen_limit);
-       // This allows random water spawns to be more common at the surface.
-       s16 water_chance = MYCUBE(water_features_lim) * water_depth;
-
-       // Reduce the odds of overflows even further.
-       if (node_max.Y > water_level) {
-               lava_chance /= 3;
-               water_chance /= 3;
-       }
-
-       u32 index_2d = 0;
-       for (s16 z = node_min.Z; z <= node_max.Z; z++)
-       for (s16 x = node_min.X; x <= node_max.X; x++, index_2d++) {
-               Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index_2d]);
-               bool tunnel_air_above = false;
-               bool is_under_river = false;
-               bool underground = false;
-               u32 index_data = vm->m_area.index(x, node_max.Y, z);
-               u32 index_3d = (z - node_min.Z) * zstride_1d + csize.Y * ystride + (x - node_min.X);
-
-               // Dig caves on down loop to check for air above.
-               // Don't excavate the overgenerated stone at node_max.Y + 1,
-               // this creates a 'roof' over the tunnel, preventing light in
-               // tunnels at mapchunk borders when generating mapchunks upwards.
-               // This 'roof' is removed when the mapchunk above is generated.
-               for (s16 y = node_max.Y; y >= node_min.Y - 1; y--,
-                               index_3d -= ystride,
-                               vm->m_area.add_y(em, index_data, -1)) {
-
-                       float terrain = noise_terrain_height->result[index_2d];
-
-                       // Saves some time.
-                       if (y > terrain + 10)
-                               continue;
-
-                       if (y < terrain - 40)
-                               underground = true;
-
-                       // Dig massive caves.
-                       if (node_max.Y <= massive_cave_depth
-                                       && noise_massive_caves->result[index_3d]
-                                       > tcave_cache[y - node_min.Y + 1]) {
-                               vm->m_data[index_data] = n_air;
-                               made_a_big_one = true;
-                               continue;
-                       }
-
-                       content_t c = vm->m_data[index_data].getContent();
-                       // Detect river water to place riverbed nodes in tunnels
-                       if (c == biome->c_river_water)
-                               is_under_river = true;
-
-                       float d1 = contour(noise_cave1->result[index_3d]);
-                       float d2 = contour(noise_cave2->result[index_3d]);
-
-                       if (d1 * d2 > cave_width && ndef->get(c).is_ground_content) {
-                               // in a tunnel
-                               vm->m_data[index_data] = n_air;
-                               tunnel_air_above = true;
-                       } else if (c == biome->c_filler || c == biome->c_stone) {
-                               if (tunnel_air_above) {
-                                       // at the tunnel floor
-                                       s16 sr = ps.range(0, 39);
-                                       u32 j = index_data;
-                                       vm->m_area.add_y(em, j, 1);
-
-                                       if (sr > terrain - y) {
-                                               // Put biome nodes in tunnels near the surface
-                                               if (is_under_river)
-                                                       vm->m_data[index_data] = MapNode(biome->c_riverbed);
-                                               else if (underground)
-                                                       vm->m_data[index_data] = MapNode(biome->c_filler);
-                                               else
-                                                       vm->m_data[index_data] = MapNode(biome->c_top);
-                                       } else if (sr < 3 && underground) {
-                                               sr = abs(ps.next());
-                                               if (lava_features_lim > 0 && y <= lava_max_height
-                                                               && c == biome->c_stone && sr < lava_chance)
-                                                       vm->m_data[j] = n_lava;
-
-                                               sr -= lava_chance;
-
-                                               // If sr < 0 then we should have already placed lava --
-                                               // don't immediately dump water on it.
-                                               if (water_features_lim > 0 && y <= cave_water_max_height
-                                                               && sr >= 0 && sr < water_chance)
-                                                       vm->m_data[j] = n_water;
-                                       }
-                               }
-
-                               tunnel_air_above = false;
-                               underground = true;
-                       } else {
-                               tunnel_air_above = false;
-                       }
-               }
-       }
-
-       if (node_max.Y <= large_cave_depth && !made_a_big_one) {
-               u32 bruises_count = ps.range(0, 2);
-               for (u32 i = 0; i < bruises_count; i++) {
-                       CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
-                               c_water_source, c_lava_source, lava_max_height);
-
-                       cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
-               }
-       }
-}
diff --git a/src/mapgen_valleys.h b/src/mapgen_valleys.h
deleted file mode 100644 (file)
index 7b5eb18..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
-Minetest Valleys C
-Copyright (C) 2016-2017 Duane Robertson <duane@duanerobertson.com>
-Copyright (C) 2016-2017 paramat
-
-Based on Valleys Mapgen by Gael de Sailly
- (https://forum.minetest.net/viewtopic.php?f=9&t=11430)
-and mapgen_v7 by kwolekr and paramat.
-
-Licensing changed by permission of Gael de Sailly.
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "mapgen.h"
-
-////////////// Mapgen Valleys flags
-#define MGVALLEYS_ALT_CHILL    0x01
-#define MGVALLEYS_HUMID_RIVERS 0x02
-
-// Feed only one variable into these.
-#define MYSQUARE(x) (x) * (x)
-#define MYCUBE(x) (x) * (x) * (x)
-
-class BiomeManager;
-class BiomeGenOriginal;
-
-// Global profiler
-//class Profiler;
-//extern Profiler *mapgen_profiler;
-
-
-struct MapgenValleysParams : public MapgenParams {
-       u32 spflags = MGVALLEYS_HUMID_RIVERS | MGVALLEYS_ALT_CHILL;
-       s16 large_cave_depth = -33;
-       s16 massive_cave_depth = -256; // highest altitude of massive caves
-       u16 altitude_chill = 90; // The altitude at which temperature drops by 20C.
-       u16 lava_features = 0; // How often water will occur in caves.
-       u16 river_depth = 4; // How deep to carve river channels.
-       u16 river_size = 5; // How wide to make rivers.
-       u16 water_features = 0; // How often water will occur in caves.
-       float cave_width = 0.09f;
-       NoiseParams np_cave1;
-       NoiseParams np_cave2;
-       NoiseParams np_filler_depth;
-       NoiseParams np_inter_valley_fill;
-       NoiseParams np_inter_valley_slope;
-       NoiseParams np_rivers;
-       NoiseParams np_massive_caves;
-       NoiseParams np_terrain_height;
-       NoiseParams np_valley_depth;
-       NoiseParams np_valley_profile;
-
-       MapgenValleysParams();
-       ~MapgenValleysParams() = default;
-
-       void readParams(const Settings *settings);
-       void writeParams(Settings *settings) const;
-};
-
-struct TerrainNoise {
-       s16 x;
-       s16 z;
-       float terrain_height;
-       float *rivers;
-       float *valley;
-       float valley_profile;
-       float *slope;
-       float inter_valley_fill;
-};
-
-class MapgenValleys : public MapgenBasic {
-public:
-
-       MapgenValleys(int mapgenid, MapgenValleysParams *params, EmergeManager *emerge);
-       ~MapgenValleys();
-
-       virtual MapgenType getType() const { return MAPGEN_VALLEYS; }
-
-       virtual void makeChunk(BlockMakeData *data);
-       int getSpawnLevelAtPoint(v2s16 p);
-
-       s16 large_cave_depth;
-
-private:
-       BiomeGenOriginal *m_bgen;
-
-       bool humid_rivers;
-       bool use_altitude_chill;
-       float humidity_adjust;
-       s16 cave_water_max_height;
-       s16 lava_max_height;
-
-       float altitude_chill;
-       s16 lava_features_lim;
-       s16 massive_cave_depth;
-       float river_depth_bed;
-       float river_size_factor;
-       float *tcave_cache;
-       s16 water_features_lim;
-       Noise *noise_inter_valley_fill;
-       Noise *noise_inter_valley_slope;
-       Noise *noise_rivers;
-       Noise *noise_cave1;
-       Noise *noise_cave2;
-       Noise *noise_massive_caves;
-       Noise *noise_terrain_height;
-       Noise *noise_valley_depth;
-       Noise *noise_valley_profile;
-
-       float terrainLevelAtPoint(s16 x, s16 z);
-
-       void calculateNoise();
-
-       virtual int generateTerrain();
-       float terrainLevelFromNoise(TerrainNoise *tn);
-       float adjustedTerrainLevelFromNoise(TerrainNoise *tn);
-
-       virtual void generateCaves(s16 max_stone_y, s16 large_cave_depth);
-};
diff --git a/src/mg_biome.cpp b/src/mg_biome.cpp
deleted file mode 100644 (file)
index 8dbb78e..0000000
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2014-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "mg_biome.h"
-#include "mg_decoration.h"
-#include "emerge.h"
-#include "server.h"
-#include "nodedef.h"
-#include "map.h" //for MMVManip
-#include "util/numeric.h"
-#include "porting.h"
-#include "settings.h"
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-BiomeManager::BiomeManager(Server *server) :
-       ObjDefManager(server, OBJDEF_BIOME)
-{
-       m_server = server;
-
-       // Create default biome to be used in case none exist
-       Biome *b = new Biome;
-
-       b->name            = "Default";
-       b->flags           = 0;
-       b->depth_top       = 0;
-       b->depth_filler    = -MAX_MAP_GENERATION_LIMIT;
-       b->depth_water_top = 0;
-       b->depth_riverbed  = 0;
-       b->y_min           = -MAX_MAP_GENERATION_LIMIT;
-       b->y_max           = MAX_MAP_GENERATION_LIMIT;
-       b->heat_point      = 0.0;
-       b->humidity_point  = 0.0;
-
-       b->m_nodenames.emplace_back("mapgen_stone");
-       b->m_nodenames.emplace_back("mapgen_stone");
-       b->m_nodenames.emplace_back("mapgen_stone");
-       b->m_nodenames.emplace_back("mapgen_water_source");
-       b->m_nodenames.emplace_back("mapgen_water_source");
-       b->m_nodenames.emplace_back("mapgen_river_water_source");
-       b->m_nodenames.emplace_back("mapgen_stone");
-       b->m_nodenames.emplace_back("ignore");
-       m_ndef->pendNodeResolve(b);
-
-       add(b);
-}
-
-
-void BiomeManager::clear()
-{
-       EmergeManager *emerge = m_server->getEmergeManager();
-
-       // Remove all dangling references in Decorations
-       DecorationManager *decomgr = emerge->decomgr;
-       for (size_t i = 0; i != decomgr->getNumObjects(); i++) {
-               Decoration *deco = (Decoration *)decomgr->getRaw(i);
-               deco->biomes.clear();
-       }
-
-       // Don't delete the first biome
-       for (size_t i = 1; i < m_objects.size(); i++)
-               delete (Biome *)m_objects[i];
-
-       m_objects.resize(1);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-
-void BiomeParamsOriginal::readParams(const Settings *settings)
-{
-       settings->getNoiseParams("mg_biome_np_heat",           np_heat);
-       settings->getNoiseParams("mg_biome_np_heat_blend",     np_heat_blend);
-       settings->getNoiseParams("mg_biome_np_humidity",       np_humidity);
-       settings->getNoiseParams("mg_biome_np_humidity_blend", np_humidity_blend);
-}
-
-
-void BiomeParamsOriginal::writeParams(Settings *settings) const
-{
-       settings->setNoiseParams("mg_biome_np_heat",           np_heat);
-       settings->setNoiseParams("mg_biome_np_heat_blend",     np_heat_blend);
-       settings->setNoiseParams("mg_biome_np_humidity",       np_humidity);
-       settings->setNoiseParams("mg_biome_np_humidity_blend", np_humidity_blend);
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
-
-BiomeGenOriginal::BiomeGenOriginal(BiomeManager *biomemgr,
-       BiomeParamsOriginal *params, v3s16 chunksize)
-{
-       m_bmgr   = biomemgr;
-       m_params = params;
-       m_csize  = chunksize;
-
-       noise_heat           = new Noise(&params->np_heat,
-                                                                       params->seed, m_csize.X, m_csize.Z);
-       noise_humidity       = new Noise(&params->np_humidity,
-                                                                       params->seed, m_csize.X, m_csize.Z);
-       noise_heat_blend     = new Noise(&params->np_heat_blend,
-                                                                       params->seed, m_csize.X, m_csize.Z);
-       noise_humidity_blend = new Noise(&params->np_humidity_blend,
-                                                                       params->seed, m_csize.X, m_csize.Z);
-
-       heatmap  = noise_heat->result;
-       humidmap = noise_humidity->result;
-       biomemap = new biome_t[m_csize.X * m_csize.Z];
-}
-
-BiomeGenOriginal::~BiomeGenOriginal()
-{
-       delete []biomemap;
-
-       delete noise_heat;
-       delete noise_humidity;
-       delete noise_heat_blend;
-       delete noise_humidity_blend;
-}
-
-
-Biome *BiomeGenOriginal::calcBiomeAtPoint(v3s16 pos) const
-{
-       float heat =
-               NoisePerlin2D(&m_params->np_heat,       pos.X, pos.Z, m_params->seed) +
-               NoisePerlin2D(&m_params->np_heat_blend, pos.X, pos.Z, m_params->seed);
-       float humidity =
-               NoisePerlin2D(&m_params->np_humidity,       pos.X, pos.Z, m_params->seed) +
-               NoisePerlin2D(&m_params->np_humidity_blend, pos.X, pos.Z, m_params->seed);
-
-       return calcBiomeFromNoise(heat, humidity, pos.Y);
-}
-
-
-void BiomeGenOriginal::calcBiomeNoise(v3s16 pmin)
-{
-       m_pmin = pmin;
-
-       noise_heat->perlinMap2D(pmin.X, pmin.Z);
-       noise_humidity->perlinMap2D(pmin.X, pmin.Z);
-       noise_heat_blend->perlinMap2D(pmin.X, pmin.Z);
-       noise_humidity_blend->perlinMap2D(pmin.X, pmin.Z);
-
-       for (s32 i = 0; i < m_csize.X * m_csize.Z; i++) {
-               noise_heat->result[i]     += noise_heat_blend->result[i];
-               noise_humidity->result[i] += noise_humidity_blend->result[i];
-       }
-}
-
-
-biome_t *BiomeGenOriginal::getBiomes(s16 *heightmap)
-{
-       for (s32 i = 0; i != m_csize.X * m_csize.Z; i++) {
-               Biome *biome = calcBiomeFromNoise(
-                       noise_heat->result[i],
-                       noise_humidity->result[i],
-                       heightmap[i]);
-
-               biomemap[i] = biome->index;
-       }
-
-       return biomemap;
-}
-
-
-Biome *BiomeGenOriginal::getBiomeAtPoint(v3s16 pos) const
-{
-       return getBiomeAtIndex(
-               (pos.Z - m_pmin.Z) * m_csize.X + (pos.X - m_pmin.X),
-               pos.Y);
-}
-
-
-Biome *BiomeGenOriginal::getBiomeAtIndex(size_t index, s16 y) const
-{
-       return calcBiomeFromNoise(
-               noise_heat->result[index],
-               noise_humidity->result[index],
-               y);
-}
-
-
-Biome *BiomeGenOriginal::calcBiomeFromNoise(float heat, float humidity, s16 y) const
-{
-       Biome *b, *biome_closest = NULL;
-       float dist_min = FLT_MAX;
-
-       for (size_t i = 1; i < m_bmgr->getNumObjects(); i++) {
-               b = (Biome *)m_bmgr->getRaw(i);
-               if (!b || y > b->y_max || y < b->y_min)
-                       continue;
-
-               float d_heat     = heat     - b->heat_point;
-               float d_humidity = humidity - b->humidity_point;
-               float dist = (d_heat * d_heat) +
-                                        (d_humidity * d_humidity);
-               if (dist < dist_min) {
-                       dist_min = dist;
-                       biome_closest = b;
-               }
-       }
-
-       return biome_closest ? biome_closest : (Biome *)m_bmgr->getRaw(BIOME_NONE);
-}
-
-
-////////////////////////////////////////////////////////////////////////////////
-
-void Biome::resolveNodeNames()
-{
-       getIdFromNrBacklog(&c_top,         "mapgen_stone",              CONTENT_AIR);
-       getIdFromNrBacklog(&c_filler,      "mapgen_stone",              CONTENT_AIR);
-       getIdFromNrBacklog(&c_stone,       "mapgen_stone",              CONTENT_AIR);
-       getIdFromNrBacklog(&c_water_top,   "mapgen_water_source",       CONTENT_AIR);
-       getIdFromNrBacklog(&c_water,       "mapgen_water_source",       CONTENT_AIR);
-       getIdFromNrBacklog(&c_river_water, "mapgen_river_water_source", CONTENT_AIR);
-       getIdFromNrBacklog(&c_riverbed,    "mapgen_stone",              CONTENT_AIR);
-       getIdFromNrBacklog(&c_dust,        "ignore",                    CONTENT_IGNORE);
-}
diff --git a/src/mg_biome.h b/src/mg_biome.h
deleted file mode 100644 (file)
index f45238f..0000000
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2014-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "objdef.h"
-#include "nodedef.h"
-#include "noise.h"
-
-class Server;
-class Settings;
-class BiomeManager;
-
-////
-//// Biome
-////
-
-typedef u8 biome_t;
-
-#define BIOME_NONE ((biome_t)0)
-
-// TODO(hmmmm): Decide whether this is obsolete or will be used in the future
-enum BiomeType {
-       BIOMETYPE_NORMAL,
-       BIOMETYPE_LIQUID,
-       BIOMETYPE_NETHER,
-       BIOMETYPE_AETHER,
-       BIOMETYPE_FLAT,
-};
-
-class Biome : public ObjDef, public NodeResolver {
-public:
-       u32 flags;
-
-       content_t c_top;
-       content_t c_filler;
-       content_t c_stone;
-       content_t c_water_top;
-       content_t c_water;
-       content_t c_river_water;
-       content_t c_riverbed;
-       content_t c_dust;
-
-       s16 depth_top;
-       s16 depth_filler;
-       s16 depth_water_top;
-       s16 depth_riverbed;
-
-       s16 y_min;
-       s16 y_max;
-       float heat_point;
-       float humidity_point;
-
-       virtual void resolveNodeNames();
-};
-
-
-////
-//// BiomeGen
-////
-
-enum BiomeGenType {
-       BIOMEGEN_ORIGINAL,
-};
-
-struct BiomeParams {
-       virtual void readParams(const Settings *settings) = 0;
-       virtual void writeParams(Settings *settings) const = 0;
-       virtual ~BiomeParams() = default;
-
-       s32 seed;
-};
-
-class BiomeGen {
-public:
-       virtual ~BiomeGen() = default;
-
-       virtual BiomeGenType getType() const = 0;
-
-       // Calculates the biome at the exact position provided.  This function can
-       // be called at any time, but may be less efficient than the latter methods,
-       // depending on implementation.
-       virtual Biome *calcBiomeAtPoint(v3s16 pos) const = 0;
-
-       // Computes any intermediate results needed for biome generation.  Must be
-       // called before using any of: getBiomes, getBiomeAtPoint, or getBiomeAtIndex.
-       // Calling this invalidates the previous results stored in biomemap.
-       virtual void calcBiomeNoise(v3s16 pmin) = 0;
-
-       // Gets all biomes in current chunk using each corresponding element of
-       // heightmap as the y position, then stores the results by biome index in
-       // biomemap (also returned)
-       virtual biome_t *getBiomes(s16 *heightmap) = 0;
-
-       // Gets a single biome at the specified position, which must be contained
-       // in the region formed by m_pmin and (m_pmin + m_csize - 1).
-       virtual Biome *getBiomeAtPoint(v3s16 pos) const = 0;
-
-       // Same as above, but uses a raw numeric index correlating to the (x,z) position.
-       virtual Biome *getBiomeAtIndex(size_t index, s16 y) const = 0;
-
-       // Result of calcBiomes bulk computation.
-       biome_t *biomemap = nullptr;
-
-protected:
-       BiomeManager *m_bmgr = nullptr;
-       v3s16 m_pmin;
-       v3s16 m_csize;
-};
-
-
-////
-//// BiomeGen implementations
-////
-
-//
-// Original biome algorithm (Whittaker's classification + surface height)
-//
-
-struct BiomeParamsOriginal : public BiomeParams {
-       BiomeParamsOriginal() :
-               np_heat(50, 50, v3f(1000.0, 1000.0, 1000.0), 5349, 3, 0.5, 2.0),
-               np_humidity(50, 50, v3f(1000.0, 1000.0, 1000.0), 842, 3, 0.5, 2.0),
-               np_heat_blend(0, 1.5, v3f(8.0, 8.0, 8.0), 13, 2, 1.0, 2.0),
-               np_humidity_blend(0, 1.5, v3f(8.0, 8.0, 8.0), 90003, 2, 1.0, 2.0)
-       {
-       }
-
-       virtual void readParams(const Settings *settings);
-       virtual void writeParams(Settings *settings) const;
-
-       NoiseParams np_heat;
-       NoiseParams np_humidity;
-       NoiseParams np_heat_blend;
-       NoiseParams np_humidity_blend;
-};
-
-class BiomeGenOriginal : public BiomeGen {
-public:
-       BiomeGenOriginal(BiomeManager *biomemgr,
-               BiomeParamsOriginal *params, v3s16 chunksize);
-       virtual ~BiomeGenOriginal();
-
-       BiomeGenType getType() const { return BIOMEGEN_ORIGINAL; }
-
-       Biome *calcBiomeAtPoint(v3s16 pos) const;
-       void calcBiomeNoise(v3s16 pmin);
-
-       biome_t *getBiomes(s16 *heightmap);
-       Biome *getBiomeAtPoint(v3s16 pos) const;
-       Biome *getBiomeAtIndex(size_t index, s16 y) const;
-
-       Biome *calcBiomeFromNoise(float heat, float humidity, s16 y) const;
-
-       float *heatmap;
-       float *humidmap;
-
-private:
-       BiomeParamsOriginal *m_params;
-
-       Noise *noise_heat;
-       Noise *noise_humidity;
-       Noise *noise_heat_blend;
-       Noise *noise_humidity_blend;
-};
-
-
-////
-//// BiomeManager
-////
-
-class BiomeManager : public ObjDefManager {
-public:
-       BiomeManager(Server *server);
-       virtual ~BiomeManager() = default;
-
-       const char *getObjectTitle() const
-       {
-               return "biome";
-       }
-
-       static Biome *create(BiomeType type)
-       {
-               return new Biome;
-       }
-
-       BiomeGen *createBiomeGen(BiomeGenType type, BiomeParams *params, v3s16 chunksize)
-       {
-               switch (type) {
-               case BIOMEGEN_ORIGINAL:
-                       return new BiomeGenOriginal(this,
-                               (BiomeParamsOriginal *)params, chunksize);
-               default:
-                       return NULL;
-               }
-       }
-
-       static BiomeParams *createBiomeParams(BiomeGenType type)
-       {
-               switch (type) {
-               case BIOMEGEN_ORIGINAL:
-                       return new BiomeParamsOriginal;
-               default:
-                       return NULL;
-               }
-       }
-
-       virtual void clear();
-
-private:
-       Server *m_server;
-
-};
diff --git a/src/mg_decoration.cpp b/src/mg_decoration.cpp
deleted file mode 100644 (file)
index 2c2fbc6..0000000
+++ /dev/null
@@ -1,377 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2015-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "mg_decoration.h"
-#include "mg_schematic.h"
-#include "mapgen.h"
-#include "noise.h"
-#include "map.h"
-#include "log.h"
-#include "util/numeric.h"
-#include <algorithm>
-
-
-FlagDesc flagdesc_deco[] = {
-       {"place_center_x",  DECO_PLACE_CENTER_X},
-       {"place_center_y",  DECO_PLACE_CENTER_Y},
-       {"place_center_z",  DECO_PLACE_CENTER_Z},
-       {"force_placement", DECO_FORCE_PLACEMENT},
-       {"liquid_surface",  DECO_LIQUID_SURFACE},
-       {"all_floors",      DECO_ALL_FLOORS},
-       {"all_ceilings",    DECO_ALL_CEILINGS},
-       {NULL,              0}
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-DecorationManager::DecorationManager(IGameDef *gamedef) :
-       ObjDefManager(gamedef, OBJDEF_DECORATION)
-{
-}
-
-
-size_t DecorationManager::placeAllDecos(Mapgen *mg, u32 blockseed,
-       v3s16 nmin, v3s16 nmax)
-{
-       size_t nplaced = 0;
-
-       for (size_t i = 0; i != m_objects.size(); i++) {
-               Decoration *deco = (Decoration *)m_objects[i];
-               if (!deco)
-                       continue;
-
-               nplaced += deco->placeDeco(mg, blockseed, nmin, nmax);
-               blockseed++;
-       }
-
-       return nplaced;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-void Decoration::resolveNodeNames()
-{
-       getIdsFromNrBacklog(&c_place_on);
-       getIdsFromNrBacklog(&c_spawnby);
-}
-
-
-bool Decoration::canPlaceDecoration(MMVManip *vm, v3s16 p)
-{
-       // Check if the decoration can be placed on this node
-       u32 vi = vm->m_area.index(p);
-       if (!CONTAINS(c_place_on, vm->m_data[vi].getContent()))
-               return false;
-
-       // Don't continue if there are no spawnby constraints
-       if (nspawnby == -1)
-               return true;
-
-       int nneighs = 0;
-       static const v3s16 dirs[16] = {
-               v3s16( 0, 0,  1),
-               v3s16( 0, 0, -1),
-               v3s16( 1, 0,  0),
-               v3s16(-1, 0,  0),
-               v3s16( 1, 0,  1),
-               v3s16(-1, 0,  1),
-               v3s16(-1, 0, -1),
-               v3s16( 1, 0, -1),
-
-               v3s16( 0, 1,  1),
-               v3s16( 0, 1, -1),
-               v3s16( 1, 1,  0),
-               v3s16(-1, 1,  0),
-               v3s16( 1, 1,  1),
-               v3s16(-1, 1,  1),
-               v3s16(-1, 1, -1),
-               v3s16( 1, 1, -1)
-       };
-
-       // Check these 16 neighbouring nodes for enough spawnby nodes
-       for (size_t i = 0; i != ARRLEN(dirs); i++) {
-               u32 index = vm->m_area.index(p + dirs[i]);
-               if (!vm->m_area.contains(index))
-                       continue;
-
-               if (CONTAINS(c_spawnby, vm->m_data[index].getContent()))
-                       nneighs++;
-       }
-
-       if (nneighs < nspawnby)
-               return false;
-
-       return true;
-}
-
-
-size_t Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
-{
-       PcgRandom ps(blockseed + 53);
-       int carea_size = nmax.X - nmin.X + 1;
-
-       // Divide area into parts
-       // If chunksize is changed it may no longer be divisable by sidelen
-       if (carea_size % sidelen)
-               sidelen = carea_size;
-
-       s16 divlen = carea_size / sidelen;
-       int area = sidelen * sidelen;
-
-       for (s16 z0 = 0; z0 < divlen; z0++)
-       for (s16 x0 = 0; x0 < divlen; x0++) {
-               v2s16 p2d_center( // Center position of part of division
-                       nmin.X + sidelen / 2 + sidelen * x0,
-                       nmin.Z + sidelen / 2 + sidelen * z0
-               );
-               v2s16 p2d_min( // Minimum edge of part of division
-                       nmin.X + sidelen * x0,
-                       nmin.Z + sidelen * z0
-               );
-               v2s16 p2d_max( // Maximum edge of part of division
-                       nmin.X + sidelen + sidelen * x0 - 1,
-                       nmin.Z + sidelen + sidelen * z0 - 1
-               );
-
-               // Amount of decorations
-               float nval = (flags & DECO_USE_NOISE) ?
-                       NoisePerlin2D(&np, p2d_center.X, p2d_center.Y, mapseed) :
-                       fill_ratio;
-               u32 deco_count = 0;
-               float deco_count_f = (float)area * nval;
-               if (deco_count_f >= 1.f) {
-                       deco_count = deco_count_f;
-               } else if (deco_count_f > 0.f) {
-                       // For low density decorations calculate a chance for 1 decoration
-                       if (ps.range(1000) <= deco_count_f * 1000.f)
-                               deco_count = 1;
-               }
-
-               for (u32 i = 0; i < deco_count; i++) {
-                       s16 x = ps.range(p2d_min.X, p2d_max.X);
-                       s16 z = ps.range(p2d_min.Y, p2d_max.Y);
-                       int mapindex = carea_size * (z - nmin.Z) + (x - nmin.X);
-
-                       if ((flags & DECO_ALL_FLOORS) ||
-                                       (flags & DECO_ALL_CEILINGS)) {
-                               // All-surfaces decorations
-                               // Check biome of column
-                               if (mg->biomemap && !biomes.empty()) {
-                                       std::unordered_set<u8>::const_iterator iter =
-                                               biomes.find(mg->biomemap[mapindex]);
-                                       if (iter == biomes.end())
-                                               continue;
-                               }
-
-                               // Get all floors and ceilings in node column
-                               u16 size = (nmax.Y - nmin.Y + 1) / 2;
-                               s16 floors[size];
-                               s16 ceilings[size];
-                               u16 num_floors = 0;
-                               u16 num_ceilings = 0;
-
-                               mg->getSurfaces(v2s16(x, z), nmin.Y, nmax.Y,
-                                       floors, ceilings, &num_floors, &num_ceilings);
-
-                               if ((flags & DECO_ALL_FLOORS) && num_floors > 0) {
-                                       // Floor decorations
-                                       for (u16 fi = 0; fi < num_floors; fi++) {
-                                               s16 y = floors[fi];
-                                               if (y < y_min || y > y_max)
-                                                       continue;
-
-                                               v3s16 pos(x, y, z);
-                                               if (generate(mg->vm, &ps, pos, false))
-                                                       mg->gennotify.addEvent(
-                                                                       GENNOTIFY_DECORATION, pos, index);
-                                       }
-                               }
-
-                               if ((flags & DECO_ALL_CEILINGS) && num_ceilings > 0) {
-                                       // Ceiling decorations
-                                       for (u16 ci = 0; ci < num_ceilings; ci++) {
-                                               s16 y = ceilings[ci];
-                                               if (y < y_min || y > y_max)
-                                                       continue;
-
-                                               v3s16 pos(x, y, z);
-                                               if (generate(mg->vm, &ps, pos, true))
-                                                       mg->gennotify.addEvent(
-                                                                       GENNOTIFY_DECORATION, pos, index);
-                                       }
-                               }
-                       } else { // Heightmap decorations
-                               s16 y = -MAX_MAP_GENERATION_LIMIT;
-                               if (flags & DECO_LIQUID_SURFACE)
-                                       y = mg->findLiquidSurface(v2s16(x, z), nmin.Y, nmax.Y);
-                               else if (mg->heightmap)
-                                       y = mg->heightmap[mapindex];
-                               else
-                                       y = mg->findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
-
-                               if (y < y_min || y > y_max || y < nmin.Y || y > nmax.Y)
-                                       continue;
-
-                               if (mg->biomemap && !biomes.empty()) {
-                                       std::unordered_set<u8>::const_iterator iter =
-                                               biomes.find(mg->biomemap[mapindex]);
-                                       if (iter == biomes.end())
-                                               continue;
-                               }
-
-                               v3s16 pos(x, y, z);
-                               if (generate(mg->vm, &ps, pos, false))
-                                       mg->gennotify.addEvent(GENNOTIFY_DECORATION, pos, index);
-                       }
-               }
-       }
-
-       return 0;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-void DecoSimple::resolveNodeNames()
-{
-       Decoration::resolveNodeNames();
-       getIdsFromNrBacklog(&c_decos);
-}
-
-
-size_t DecoSimple::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling)
-{
-       // Don't bother if there aren't any decorations to place
-       if (c_decos.empty())
-               return 0;
-
-       if (!canPlaceDecoration(vm, p))
-               return 0;
-
-       // Check for placement outside the voxelmanip volume
-       if (ceiling) {
-               // Ceiling decorations
-               // 'place offset y' is inverted
-               if (p.Y - place_offset_y - std::max(deco_height, deco_height_max) <
-                               vm->m_area.MinEdge.Y)
-                       return 0;
-
-               if (p.Y - 1 - place_offset_y > vm->m_area.MaxEdge.Y)
-                       return 0;
-
-       } else { // Heightmap and floor decorations
-               if (p.Y + place_offset_y + std::max(deco_height, deco_height_max) >
-                               vm->m_area.MaxEdge.Y)
-                       return 0;
-
-               if (p.Y + 1 + place_offset_y < vm->m_area.MinEdge.Y)
-                       return 0;
-       }
-
-       content_t c_place = c_decos[pr->range(0, c_decos.size() - 1)];
-       s16 height = (deco_height_max > 0) ?
-               pr->range(deco_height, deco_height_max) : deco_height;
-       u8 param2 = (deco_param2_max > 0) ?
-               pr->range(deco_param2, deco_param2_max) : deco_param2;
-       bool force_placement = (flags & DECO_FORCE_PLACEMENT);
-
-       const v3s16 &em = vm->m_area.getExtent();
-       u32 vi = vm->m_area.index(p);
-
-       if (ceiling) {
-               // Ceiling decorations
-               // 'place offset y' is inverted
-               vm->m_area.add_y(em, vi, -place_offset_y);
-
-               for (int i = 0; i < height; i++) {
-                       vm->m_area.add_y(em, vi, -1);
-                       content_t c = vm->m_data[vi].getContent();
-                       if (c != CONTENT_AIR && c != CONTENT_IGNORE && !force_placement)
-                               break;
-
-                       vm->m_data[vi] = MapNode(c_place, 0, param2);
-               }
-       } else { // Heightmap and floor decorations
-               vm->m_area.add_y(em, vi, place_offset_y);
-
-               for (int i = 0; i < height; i++) {
-                       vm->m_area.add_y(em, vi, 1);
-                       content_t c = vm->m_data[vi].getContent();
-                       if (c != CONTENT_AIR && c != CONTENT_IGNORE && !force_placement)
-                               break;
-
-                       vm->m_data[vi] = MapNode(c_place, 0, param2);
-               }
-       }
-
-       return 1;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-size_t DecoSchematic::generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling)
-{
-       // Schematic could have been unloaded but not the decoration
-       // In this case generate() does nothing (but doesn't *fail*)
-       if (schematic == NULL)
-               return 0;
-
-       if (!canPlaceDecoration(vm, p))
-               return 0;
-
-       if (flags & DECO_PLACE_CENTER_Y) {
-               p.Y -= (schematic->size.Y - 1) / 2;
-       } else {
-               // Only apply 'place offset y' if not 'deco place center y'
-               if (ceiling)
-                       // Shift down so that schematic top layer is level with ceiling
-                       // 'place offset y' is inverted
-                       p.Y -= (place_offset_y + schematic->size.Y - 1);
-               else
-                       p.Y += place_offset_y;
-       }
-
-       // Check schematic top and base are in voxelmanip
-       if (p.Y + schematic->size.Y - 1 > vm->m_area.MaxEdge.Y)
-               return 0;
-
-       if (p.Y < vm->m_area.MinEdge.Y)
-               return 0;
-
-       if (flags & DECO_PLACE_CENTER_X)
-               p.X -= (schematic->size.X - 1) / 2;
-       if (flags & DECO_PLACE_CENTER_Z)
-               p.Z -= (schematic->size.Z - 1) / 2;
-
-       Rotation rot = (rotation == ROTATE_RAND) ?
-               (Rotation)pr->range(ROTATE_0, ROTATE_270) : rotation;
-       bool force_placement = (flags & DECO_FORCE_PLACEMENT);
-
-       schematic->blitToVManip(vm, p, rot, force_placement);
-
-       return 1;
-}
diff --git a/src/mg_decoration.h b/src/mg_decoration.h
deleted file mode 100644 (file)
index 1ca632f..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2015-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include <unordered_set>
-#include "objdef.h"
-#include "noise.h"
-#include "nodedef.h"
-
-class Mapgen;
-class MMVManip;
-class PcgRandom;
-class Schematic;
-
-enum DecorationType {
-       DECO_SIMPLE,
-       DECO_SCHEMATIC,
-       DECO_LSYSTEM
-};
-
-#define DECO_PLACE_CENTER_X  0x01
-#define DECO_PLACE_CENTER_Y  0x02
-#define DECO_PLACE_CENTER_Z  0x04
-#define DECO_USE_NOISE       0x08
-#define DECO_FORCE_PLACEMENT 0x10
-#define DECO_LIQUID_SURFACE  0x20
-#define DECO_ALL_FLOORS      0x40
-#define DECO_ALL_CEILINGS    0x80
-
-extern FlagDesc flagdesc_deco[];
-
-
-class Decoration : public ObjDef, public NodeResolver {
-public:
-       Decoration() = default;
-       virtual ~Decoration() = default;
-
-       virtual void resolveNodeNames();
-
-       bool canPlaceDecoration(MMVManip *vm, v3s16 p);
-       size_t placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
-
-       virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling) = 0;
-
-       u32 flags = 0;
-       int mapseed = 0;
-       std::vector<content_t> c_place_on;
-       s16 sidelen = 1;
-       s16 y_min;
-       s16 y_max;
-       float fill_ratio = 0.0f;
-       NoiseParams np;
-       std::vector<content_t> c_spawnby;
-       s16 nspawnby;
-       s16 place_offset_y = 0;
-
-       std::unordered_set<u8> biomes;
-};
-
-
-class DecoSimple : public Decoration {
-public:
-       virtual void resolveNodeNames();
-       virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling);
-
-       std::vector<content_t> c_decos;
-       s16 deco_height;
-       s16 deco_height_max;
-       u8 deco_param2;
-       u8 deco_param2_max;
-};
-
-
-class DecoSchematic : public Decoration {
-public:
-       DecoSchematic() = default;
-
-       virtual size_t generate(MMVManip *vm, PcgRandom *pr, v3s16 p, bool ceiling);
-
-       Rotation rotation;
-       Schematic *schematic = nullptr;
-};
-
-
-/*
-class DecoLSystem : public Decoration {
-public:
-       virtual void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
-};
-*/
-
-
-class DecorationManager : public ObjDefManager {
-public:
-       DecorationManager(IGameDef *gamedef);
-       virtual ~DecorationManager() = default;
-
-       const char *getObjectTitle() const
-       {
-               return "decoration";
-       }
-
-       static Decoration *create(DecorationType type)
-       {
-               switch (type) {
-               case DECO_SIMPLE:
-                       return new DecoSimple;
-               case DECO_SCHEMATIC:
-                       return new DecoSchematic;
-               //case DECO_LSYSTEM:
-               //      return new DecoLSystem;
-               default:
-                       return NULL;
-               }
-       }
-
-       size_t placeAllDecos(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
-};
diff --git a/src/mg_ore.cpp b/src/mg_ore.cpp
deleted file mode 100644 (file)
index 979135e..0000000
+++ /dev/null
@@ -1,483 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2015-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "mg_ore.h"
-#include "mapgen.h"
-#include "noise.h"
-#include "map.h"
-#include "log.h"
-#include "util/numeric.h"
-#include <algorithm>
-
-
-FlagDesc flagdesc_ore[] = {
-       {"absheight",                 OREFLAG_ABSHEIGHT}, // Non-functional
-       {"puff_cliffs",               OREFLAG_PUFF_CLIFFS},
-       {"puff_additive_composition", OREFLAG_PUFF_ADDITIVE},
-       {NULL,                        0}
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-OreManager::OreManager(IGameDef *gamedef) :
-       ObjDefManager(gamedef, OBJDEF_ORE)
-{
-}
-
-
-size_t OreManager::placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
-{
-       size_t nplaced = 0;
-
-       for (size_t i = 0; i != m_objects.size(); i++) {
-               Ore *ore = (Ore *)m_objects[i];
-               if (!ore)
-                       continue;
-
-               nplaced += ore->placeOre(mg, blockseed, nmin, nmax);
-               blockseed++;
-       }
-
-       return nplaced;
-}
-
-
-void OreManager::clear()
-{
-       for (ObjDef *object : m_objects) {
-               Ore *ore = (Ore *) object;
-               delete ore;
-       }
-       m_objects.clear();
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-Ore::~Ore()
-{
-       delete noise;
-}
-
-
-void Ore::resolveNodeNames()
-{
-       getIdFromNrBacklog(&c_ore, "", CONTENT_AIR);
-       getIdsFromNrBacklog(&c_wherein);
-}
-
-
-size_t Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax)
-{
-       if (nmin.Y > y_max || nmax.Y < y_min)
-               return 0;
-
-       int actual_ymin = MYMAX(nmin.Y, y_min);
-       int actual_ymax = MYMIN(nmax.Y, y_max);
-       if (clust_size >= actual_ymax - actual_ymin + 1)
-               return 0;
-
-       nmin.Y = actual_ymin;
-       nmax.Y = actual_ymax;
-       generate(mg->vm, mg->seed, blockseed, nmin, nmax, mg->biomemap);
-
-       return 1;
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-void OreScatter::generate(MMVManip *vm, int mapseed, u32 blockseed,
-       v3s16 nmin, v3s16 nmax, u8 *biomemap)
-{
-       PcgRandom pr(blockseed);
-       MapNode n_ore(c_ore, 0, ore_param2);
-
-       u32 sizex  = (nmax.X - nmin.X + 1);
-       u32 volume = (nmax.X - nmin.X + 1) *
-                                (nmax.Y - nmin.Y + 1) *
-                                (nmax.Z - nmin.Z + 1);
-       u32 csize     = clust_size;
-       u32 cvolume    = csize * csize * csize;
-       u32 nclusters = volume / clust_scarcity;
-
-       for (u32 i = 0; i != nclusters; i++) {
-               int x0 = pr.range(nmin.X, nmax.X - csize + 1);
-               int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
-               int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
-
-               if ((flags & OREFLAG_USE_NOISE) &&
-                       (NoisePerlin3D(&np, x0, y0, z0, mapseed) < nthresh))
-                       continue;
-
-               if (biomemap && !biomes.empty()) {
-                       u32 index = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
-                       std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
-                       if (it == biomes.end())
-                               continue;
-               }
-
-               for (u32 z1 = 0; z1 != csize; z1++)
-               for (u32 y1 = 0; y1 != csize; y1++)
-               for (u32 x1 = 0; x1 != csize; x1++) {
-                       if (pr.range(1, cvolume) > clust_num_ores)
-                               continue;
-
-                       u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
-                       if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
-                               continue;
-
-                       vm->m_data[i] = n_ore;
-               }
-       }
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-void OreSheet::generate(MMVManip *vm, int mapseed, u32 blockseed,
-       v3s16 nmin, v3s16 nmax, u8 *biomemap)
-{
-       PcgRandom pr(blockseed + 4234);
-       MapNode n_ore(c_ore, 0, ore_param2);
-
-       u16 max_height = column_height_max;
-       int y_start_min = nmin.Y + max_height;
-       int y_start_max = nmax.Y - max_height;
-
-       int y_start = y_start_min < y_start_max ?
-               pr.range(y_start_min, y_start_max) :
-               (y_start_min + y_start_max) / 2;
-
-       if (!noise) {
-               int sx = nmax.X - nmin.X + 1;
-               int sz = nmax.Z - nmin.Z + 1;
-               noise = new Noise(&np, 0, sx, sz);
-       }
-       noise->seed = mapseed + y_start;
-       noise->perlinMap2D(nmin.X, nmin.Z);
-
-       size_t index = 0;
-       for (int z = nmin.Z; z <= nmax.Z; z++)
-       for (int x = nmin.X; x <= nmax.X; x++, index++) {
-               float noiseval = noise->result[index];
-               if (noiseval < nthresh)
-                       continue;
-
-               if (biomemap && !biomes.empty()) {
-                       std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
-                       if (it == biomes.end())
-                               continue;
-               }
-
-               u16 height = pr.range(column_height_min, column_height_max);
-               int ymidpoint = y_start + noiseval;
-               int y0 = MYMAX(nmin.Y, ymidpoint - height * (1 - column_midpoint_factor));
-               int y1 = MYMIN(nmax.Y, y0 + height - 1);
-
-               for (int y = y0; y <= y1; y++) {
-                       u32 i = vm->m_area.index(x, y, z);
-                       if (!vm->m_area.contains(i))
-                               continue;
-                       if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
-                               continue;
-
-                       vm->m_data[i] = n_ore;
-               }
-       }
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-OrePuff::~OrePuff()
-{
-       delete noise_puff_top;
-       delete noise_puff_bottom;
-}
-
-
-void OrePuff::generate(MMVManip *vm, int mapseed, u32 blockseed,
-       v3s16 nmin, v3s16 nmax, u8 *biomemap)
-{
-       PcgRandom pr(blockseed + 4234);
-       MapNode n_ore(c_ore, 0, ore_param2);
-
-       int y_start = pr.range(nmin.Y, nmax.Y);
-
-       if (!noise) {
-               int sx = nmax.X - nmin.X + 1;
-               int sz = nmax.Z - nmin.Z + 1;
-               noise = new Noise(&np, 0, sx, sz);
-               noise_puff_top = new Noise(&np_puff_top, 0, sx, sz);
-               noise_puff_bottom = new Noise(&np_puff_bottom, 0, sx, sz);
-       }
-
-       noise->seed = mapseed + y_start;
-       noise->perlinMap2D(nmin.X, nmin.Z);
-       bool noise_generated = false;
-
-       size_t index = 0;
-       for (int z = nmin.Z; z <= nmax.Z; z++)
-       for (int x = nmin.X; x <= nmax.X; x++, index++) {
-               float noiseval = noise->result[index];
-               if (noiseval < nthresh)
-                       continue;
-
-               if (biomemap && !biomes.empty()) {
-                       std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
-                       if (it == biomes.end())
-                               continue;
-               }
-
-               if (!noise_generated) {
-                       noise_generated = true;
-                       noise_puff_top->perlinMap2D(nmin.X, nmin.Z);
-                       noise_puff_bottom->perlinMap2D(nmin.X, nmin.Z);
-               }
-
-               float ntop    = noise_puff_top->result[index];
-               float nbottom = noise_puff_bottom->result[index];
-
-               if (!(flags & OREFLAG_PUFF_CLIFFS)) {
-                       float ndiff = noiseval - nthresh;
-                       if (ndiff < 1.0f) {
-                               ntop *= ndiff;
-                               nbottom *= ndiff;
-                       }
-               }
-
-               int ymid = y_start;
-               int y0 = ymid - nbottom;
-               int y1 = ymid + ntop;
-
-               if ((flags & OREFLAG_PUFF_ADDITIVE) && (y0 > y1))
-                       SWAP(int, y0, y1);
-
-               for (int y = y0; y <= y1; y++) {
-                       u32 i = vm->m_area.index(x, y, z);
-                       if (!vm->m_area.contains(i))
-                               continue;
-                       if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
-                               continue;
-
-                       vm->m_data[i] = n_ore;
-               }
-       }
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-void OreBlob::generate(MMVManip *vm, int mapseed, u32 blockseed,
-       v3s16 nmin, v3s16 nmax, u8 *biomemap)
-{
-       PcgRandom pr(blockseed + 2404);
-       MapNode n_ore(c_ore, 0, ore_param2);
-
-       u32 sizex  = (nmax.X - nmin.X + 1);
-       u32 volume = (nmax.X - nmin.X + 1) *
-                                (nmax.Y - nmin.Y + 1) *
-                                (nmax.Z - nmin.Z + 1);
-       u32 csize  = clust_size;
-       u32 nblobs = volume / clust_scarcity;
-
-       if (!noise)
-               noise = new Noise(&np, mapseed, csize, csize, csize);
-
-       for (u32 i = 0; i != nblobs; i++) {
-               int x0 = pr.range(nmin.X, nmax.X - csize + 1);
-               int y0 = pr.range(nmin.Y, nmax.Y - csize + 1);
-               int z0 = pr.range(nmin.Z, nmax.Z - csize + 1);
-
-               if (biomemap && !biomes.empty()) {
-                       u32 bmapidx = sizex * (z0 - nmin.Z) + (x0 - nmin.X);
-                       std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[bmapidx]);
-                       if (it == biomes.end())
-                               continue;
-               }
-
-               bool noise_generated = false;
-               noise->seed = blockseed + i;
-
-               size_t index = 0;
-               for (u32 z1 = 0; z1 != csize; z1++)
-               for (u32 y1 = 0; y1 != csize; y1++)
-               for (u32 x1 = 0; x1 != csize; x1++, index++) {
-                       u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1);
-                       if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
-                               continue;
-
-                       // Lazily generate noise only if there's a chance of ore being placed
-                       // This simple optimization makes calls 6x faster on average
-                       if (!noise_generated) {
-                               noise_generated = true;
-                               noise->perlinMap3D(x0, y0, z0);
-                       }
-
-                       float noiseval = noise->result[index];
-
-                       float xdist = (s32)x1 - (s32)csize / 2;
-                       float ydist = (s32)y1 - (s32)csize / 2;
-                       float zdist = (s32)z1 - (s32)csize / 2;
-
-                       noiseval -= (sqrt(xdist * xdist + ydist * ydist + zdist * zdist) / csize);
-
-                       if (noiseval < nthresh)
-                               continue;
-
-                       vm->m_data[i] = n_ore;
-               }
-       }
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-OreVein::~OreVein()
-{
-       delete noise2;
-}
-
-
-void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed,
-       v3s16 nmin, v3s16 nmax, u8 *biomemap)
-{
-       PcgRandom pr(blockseed + 520);
-       MapNode n_ore(c_ore, 0, ore_param2);
-
-       u32 sizex = (nmax.X - nmin.X + 1);
-
-       if (!noise) {
-               int sx = nmax.X - nmin.X + 1;
-               int sy = nmax.Y - nmin.Y + 1;
-               int sz = nmax.Z - nmin.Z + 1;
-               noise  = new Noise(&np, mapseed, sx, sy, sz);
-               noise2 = new Noise(&np, mapseed + 436, sx, sy, sz);
-       }
-       bool noise_generated = false;
-
-       size_t index = 0;
-       for (int z = nmin.Z; z <= nmax.Z; z++)
-       for (int y = nmin.Y; y <= nmax.Y; y++)
-       for (int x = nmin.X; x <= nmax.X; x++, index++) {
-               u32 i = vm->m_area.index(x, y, z);
-               if (!vm->m_area.contains(i))
-                       continue;
-               if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
-                       continue;
-
-               if (biomemap && !biomes.empty()) {
-                       u32 bmapidx = sizex * (z - nmin.Z) + (x - nmin.X);
-                       std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[bmapidx]);
-                       if (it == biomes.end())
-                               continue;
-               }
-
-               // Same lazy generation optimization as in OreBlob
-               if (!noise_generated) {
-                       noise_generated = true;
-                       noise->perlinMap3D(nmin.X, nmin.Y, nmin.Z);
-                       noise2->perlinMap3D(nmin.X, nmin.Y, nmin.Z);
-               }
-
-               // randval ranges from -1..1
-               float randval   = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f;
-               float noiseval  = contour(noise->result[index]);
-               float noiseval2 = contour(noise2->result[index]);
-               if (noiseval * noiseval2 + randval * random_factor < nthresh)
-                       continue;
-
-               vm->m_data[i] = n_ore;
-       }
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-OreStratum::~OreStratum()
-{
-       delete noise_stratum_thickness;
-}
-
-
-void OreStratum::generate(MMVManip *vm, int mapseed, u32 blockseed,
-       v3s16 nmin, v3s16 nmax, u8 *biomemap)
-{
-       PcgRandom pr(blockseed + 4234);
-       MapNode n_ore(c_ore, 0, ore_param2);
-
-       if (flags & OREFLAG_USE_NOISE) {
-               if (!(noise && noise_stratum_thickness)) {
-                       int sx = nmax.X - nmin.X + 1;
-                       int sz = nmax.Z - nmin.Z + 1;
-                       noise = new Noise(&np, 0, sx, sz);
-                       noise_stratum_thickness = new Noise(&np_stratum_thickness, 0, sx, sz);
-               }
-               noise->perlinMap2D(nmin.X, nmin.Z);
-               noise_stratum_thickness->perlinMap2D(nmin.X, nmin.Z);
-       }
-
-       size_t index = 0;
-
-       for (int z = nmin.Z; z <= nmax.Z; z++)
-       for (int x = nmin.X; x <= nmax.X; x++, index++) {
-               if (biomemap && !biomes.empty()) {
-                       std::unordered_set<u8>::const_iterator it = biomes.find(biomemap[index]);
-                       if (it == biomes.end())
-                               continue;
-               }
-
-               int y0;
-               int y1;
-
-               if (flags & OREFLAG_USE_NOISE) {
-                       float nmid = noise->result[index];
-                       float nhalfthick = noise_stratum_thickness->result[index] / 2.0f;
-                       y0 = MYMAX(nmin.Y, nmid - nhalfthick);
-                       y1 = MYMIN(nmax.Y, nmid + nhalfthick);
-               } else {
-                       y0 = nmin.Y;
-                       y1 = nmax.Y;
-               }
-
-               for (int y = y0; y <= y1; y++) {
-                       if (pr.range(1, clust_scarcity) != 1)
-                               continue;
-
-                       u32 i = vm->m_area.index(x, y, z);
-                       if (!vm->m_area.contains(i))
-                               continue;
-                       if (!CONTAINS(c_wherein, vm->m_data[i].getContent()))
-                               continue;
-
-                       vm->m_data[i] = n_ore;
-               }
-       }
-}
diff --git a/src/mg_ore.h b/src/mg_ore.h
deleted file mode 100644 (file)
index e715f34..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2015-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include <unordered_set>
-#include "objdef.h"
-#include "noise.h"
-#include "nodedef.h"
-
-class Noise;
-class Mapgen;
-class MMVManip;
-
-/////////////////// Ore generation flags
-
-#define OREFLAG_ABSHEIGHT     0x01 // Non-functional but kept to not break flags
-#define OREFLAG_PUFF_CLIFFS   0x02
-#define OREFLAG_PUFF_ADDITIVE 0x04
-#define OREFLAG_USE_NOISE     0x08
-
-enum OreType {
-       ORE_SCATTER,
-       ORE_SHEET,
-       ORE_PUFF,
-       ORE_BLOB,
-       ORE_VEIN,
-       ORE_STRATUM,
-};
-
-extern FlagDesc flagdesc_ore[];
-
-class Ore : public ObjDef, public NodeResolver {
-public:
-       static const bool NEEDS_NOISE = false;
-
-       content_t c_ore;                  // the node to place
-       std::vector<content_t> c_wherein; // the nodes to be placed in
-       u32 clust_scarcity; // ore cluster has a 1-in-clust_scarcity chance of appearing at a node
-       s16 clust_num_ores; // how many ore nodes are in a chunk
-       s16 clust_size;     // how large (in nodes) a chunk of ore is
-       s16 y_min;
-       s16 y_max;
-       u8 ore_param2;          // to set node-specific attributes
-       u32 flags = 0;          // attributes for this ore
-       float nthresh;      // threshold for noise at which an ore is placed
-       NoiseParams np;     // noise for distribution of clusters (NULL for uniform scattering)
-       Noise *noise = nullptr;
-       std::unordered_set<u8> biomes;
-
-       Ore() = default;;
-       virtual ~Ore();
-
-       virtual void resolveNodeNames();
-
-       size_t placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
-       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
-               v3s16 nmin, v3s16 nmax, u8 *biomemap) = 0;
-};
-
-class OreScatter : public Ore {
-public:
-       static const bool NEEDS_NOISE = false;
-
-       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
-               v3s16 nmin, v3s16 nmax, u8 *biomemap);
-};
-
-class OreSheet : public Ore {
-public:
-       static const bool NEEDS_NOISE = true;
-
-       u16 column_height_min;
-       u16 column_height_max;
-       float column_midpoint_factor;
-
-       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
-               v3s16 nmin, v3s16 nmax, u8 *biomemap);
-};
-
-class OrePuff : public Ore {
-public:
-       static const bool NEEDS_NOISE = true;
-
-       NoiseParams np_puff_top;
-       NoiseParams np_puff_bottom;
-       Noise *noise_puff_top = nullptr;
-       Noise *noise_puff_bottom = nullptr;
-
-       OrePuff() = default;
-       virtual ~OrePuff();
-
-       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
-               v3s16 nmin, v3s16 nmax, u8 *biomemap);
-};
-
-class OreBlob : public Ore {
-public:
-       static const bool NEEDS_NOISE = true;
-
-       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
-               v3s16 nmin, v3s16 nmax, u8 *biomemap);
-};
-
-class OreVein : public Ore {
-public:
-       static const bool NEEDS_NOISE = true;
-
-       float random_factor;
-       Noise *noise2 = nullptr;
-
-       OreVein() = default;
-       virtual ~OreVein();
-
-       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
-               v3s16 nmin, v3s16 nmax, u8 *biomemap);
-};
-
-class OreStratum : public Ore {
-public:
-       static const bool NEEDS_NOISE = false;
-
-       NoiseParams np_stratum_thickness;
-       Noise *noise_stratum_thickness = nullptr;
-
-       OreStratum() = default;
-       virtual ~OreStratum();
-
-       virtual void generate(MMVManip *vm, int mapseed, u32 blockseed,
-               v3s16 nmin, v3s16 nmax, u8 *biomemap);
-};
-
-class OreManager : public ObjDefManager {
-public:
-       OreManager(IGameDef *gamedef);
-       virtual ~OreManager() = default;
-
-       const char *getObjectTitle() const
-       {
-               return "ore";
-       }
-
-       static Ore *create(OreType type)
-       {
-               switch (type) {
-               case ORE_SCATTER:
-                       return new OreScatter;
-               case ORE_SHEET:
-                       return new OreSheet;
-               case ORE_PUFF:
-                       return new OrePuff;
-               case ORE_BLOB:
-                       return new OreBlob;
-               case ORE_VEIN:
-                       return new OreVein;
-               case ORE_STRATUM:
-                       return new OreStratum;
-               default:
-                       return nullptr;
-               }
-       }
-
-       void clear();
-
-       size_t placeAllOres(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax);
-};
diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp
deleted file mode 100644 (file)
index 8874abd..0000000
+++ /dev/null
@@ -1,578 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2015-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 <fstream>
-#include <typeinfo>
-#include "mg_schematic.h"
-#include "server.h"
-#include "mapgen.h"
-#include "emerge.h"
-#include "map.h"
-#include "mapblock.h"
-#include "log.h"
-#include "util/numeric.h"
-#include "util/serialize.h"
-#include "serialization.h"
-#include "filesys.h"
-#include "voxelalgorithms.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-SchematicManager::SchematicManager(Server *server) :
-       ObjDefManager(server, OBJDEF_SCHEMATIC),
-       m_server(server)
-{
-}
-
-
-void SchematicManager::clear()
-{
-       EmergeManager *emerge = m_server->getEmergeManager();
-
-       // Remove all dangling references in Decorations
-       DecorationManager *decomgr = emerge->decomgr;
-       for (size_t i = 0; i != decomgr->getNumObjects(); i++) {
-               Decoration *deco = (Decoration *)decomgr->getRaw(i);
-
-               try {
-                       DecoSchematic *dschem = dynamic_cast<DecoSchematic *>(deco);
-                       if (dschem)
-                               dschem->schematic = NULL;
-               } catch (const std::bad_cast &) {
-               }
-       }
-
-       ObjDefManager::clear();
-}
-
-
-///////////////////////////////////////////////////////////////////////////////
-
-
-Schematic::Schematic()
-= default;
-
-
-Schematic::~Schematic()
-{
-       delete []schemdata;
-       delete []slice_probs;
-}
-
-
-void Schematic::resolveNodeNames()
-{
-       getIdsFromNrBacklog(&c_nodes, true, CONTENT_AIR);
-
-       size_t bufsize = size.X * size.Y * size.Z;
-       for (size_t i = 0; i != bufsize; i++) {
-               content_t c_original = schemdata[i].getContent();
-               content_t c_new = c_nodes[c_original];
-               schemdata[i].setContent(c_new);
-       }
-}
-
-
-void Schematic::blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place)
-{
-       sanity_check(m_ndef != NULL);
-
-       int xstride = 1;
-       int ystride = size.X;
-       int zstride = size.X * size.Y;
-
-       s16 sx = size.X;
-       s16 sy = size.Y;
-       s16 sz = size.Z;
-
-       int i_start, i_step_x, i_step_z;
-       switch (rot) {
-               case ROTATE_90:
-                       i_start  = sx - 1;
-                       i_step_x = zstride;
-                       i_step_z = -xstride;
-                       SWAP(s16, sx, sz);
-                       break;
-               case ROTATE_180:
-                       i_start  = zstride * (sz - 1) + sx - 1;
-                       i_step_x = -xstride;
-                       i_step_z = -zstride;
-                       break;
-               case ROTATE_270:
-                       i_start  = zstride * (sz - 1);
-                       i_step_x = -zstride;
-                       i_step_z = xstride;
-                       SWAP(s16, sx, sz);
-                       break;
-               default:
-                       i_start  = 0;
-                       i_step_x = xstride;
-                       i_step_z = zstride;
-       }
-
-       s16 y_map = p.Y;
-       for (s16 y = 0; y != sy; y++) {
-               if ((slice_probs[y] != MTSCHEM_PROB_ALWAYS) &&
-                       (slice_probs[y] <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
-                       continue;
-
-               for (s16 z = 0; z != sz; z++) {
-                       u32 i = z * i_step_z + y * ystride + i_start;
-                       for (s16 x = 0; x != sx; x++, i += i_step_x) {
-                               u32 vi = vm->m_area.index(p.X + x, y_map, p.Z + z);
-                               if (!vm->m_area.contains(vi))
-                                       continue;
-
-                               if (schemdata[i].getContent() == CONTENT_IGNORE)
-                                       continue;
-
-                               u8 placement_prob     = schemdata[i].param1 & MTSCHEM_PROB_MASK;
-                               bool force_place_node = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
-
-                               if (placement_prob == MTSCHEM_PROB_NEVER)
-                                       continue;
-
-                               if (!force_place && !force_place_node) {
-                                       content_t c = vm->m_data[vi].getContent();
-                                       if (c != CONTENT_AIR && c != CONTENT_IGNORE)
-                                               continue;
-                               }
-
-                               if ((placement_prob != MTSCHEM_PROB_ALWAYS) &&
-                                       (placement_prob <= myrand_range(1, MTSCHEM_PROB_ALWAYS)))
-                                       continue;
-
-                               vm->m_data[vi] = schemdata[i];
-                               vm->m_data[vi].param1 = 0;
-
-                               if (rot)
-                                       vm->m_data[vi].rotateAlongYAxis(m_ndef, rot);
-                       }
-               }
-               y_map++;
-       }
-}
-
-
-bool Schematic::placeOnVManip(MMVManip *vm, v3s16 p, u32 flags,
-       Rotation rot, bool force_place)
-{
-       assert(vm != NULL);
-       assert(schemdata != NULL);
-       sanity_check(m_ndef != NULL);
-
-       //// Determine effective rotation and effective schematic dimensions
-       if (rot == ROTATE_RAND)
-               rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270);
-
-       v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ?
-               v3s16(size.Z, size.Y, size.X) : size;
-
-       //// Adjust placement position if necessary
-       if (flags & DECO_PLACE_CENTER_X)
-               p.X -= (s.X + 1) / 2;
-       if (flags & DECO_PLACE_CENTER_Y)
-               p.Y -= (s.Y + 1) / 2;
-       if (flags & DECO_PLACE_CENTER_Z)
-               p.Z -= (s.Z + 1) / 2;
-
-       blitToVManip(vm, p, rot, force_place);
-
-       return vm->m_area.contains(VoxelArea(p, p + s - v3s16(1,1,1)));
-}
-
-void Schematic::placeOnMap(ServerMap *map, v3s16 p, u32 flags,
-       Rotation rot, bool force_place)
-{
-       std::map<v3s16, MapBlock *> lighting_modified_blocks;
-       std::map<v3s16, MapBlock *> modified_blocks;
-       std::map<v3s16, MapBlock *>::iterator it;
-
-       assert(map != NULL);
-       assert(schemdata != NULL);
-       sanity_check(m_ndef != NULL);
-
-       //// Determine effective rotation and effective schematic dimensions
-       if (rot == ROTATE_RAND)
-               rot = (Rotation)myrand_range(ROTATE_0, ROTATE_270);
-
-       v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ?
-                       v3s16(size.Z, size.Y, size.X) : size;
-
-       //// Adjust placement position if necessary
-       if (flags & DECO_PLACE_CENTER_X)
-               p.X -= (s.X + 1) / 2;
-       if (flags & DECO_PLACE_CENTER_Y)
-               p.Y -= (s.Y + 1) / 2;
-       if (flags & DECO_PLACE_CENTER_Z)
-               p.Z -= (s.Z + 1) / 2;
-
-       //// Create VManip for effected area, emerge our area, modify area
-       //// inside VManip, then blit back.
-       v3s16 bp1 = getNodeBlockPos(p);
-       v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1));
-
-       MMVManip vm(map);
-       vm.initialEmerge(bp1, bp2);
-
-       blitToVManip(&vm, p, rot, force_place);
-
-       voxalgo::blit_back_with_light(map, &vm, &modified_blocks);
-
-       //// Carry out post-map-modification actions
-
-       //// Create & dispatch map modification events to observers
-       MapEditEvent event;
-       event.type = MEET_OTHER;
-       for (it = modified_blocks.begin(); it != modified_blocks.end(); ++it)
-               event.modified_blocks.insert(it->first);
-
-       map->dispatchEvent(&event);
-}
-
-
-bool Schematic::deserializeFromMts(std::istream *is,
-       std::vector<std::string> *names)
-{
-       std::istream &ss = *is;
-       content_t cignore = CONTENT_IGNORE;
-       bool have_cignore = false;
-
-       //// Read signature
-       u32 signature = readU32(ss);
-       if (signature != MTSCHEM_FILE_SIGNATURE) {
-               errorstream << __FUNCTION__ << ": invalid schematic "
-                       "file" << std::endl;
-               return false;
-       }
-
-       //// Read version
-       u16 version = readU16(ss);
-       if (version > MTSCHEM_FILE_VER_HIGHEST_READ) {
-               errorstream << __FUNCTION__ << ": unsupported schematic "
-                       "file version" << std::endl;
-               return false;
-       }
-
-       //// Read size
-       size = readV3S16(ss);
-
-       //// Read Y-slice probability values
-       delete []slice_probs;
-       slice_probs = new u8[size.Y];
-       for (int y = 0; y != size.Y; y++)
-               slice_probs[y] = (version >= 3) ? readU8(ss) : MTSCHEM_PROB_ALWAYS_OLD;
-
-       //// Read node names
-       u16 nidmapcount = readU16(ss);
-       for (int i = 0; i != nidmapcount; i++) {
-               std::string name = deSerializeString(ss);
-
-               // Instances of "ignore" from v1 are converted to air (and instances
-               // are fixed to have MTSCHEM_PROB_NEVER later on).
-               if (name == "ignore") {
-                       name = "air";
-                       cignore = i;
-                       have_cignore = true;
-               }
-
-               names->push_back(name);
-       }
-
-       //// Read node data
-       size_t nodecount = size.X * size.Y * size.Z;
-
-       delete []schemdata;
-       schemdata = new MapNode[nodecount];
-
-       MapNode::deSerializeBulk(ss, SER_FMT_VER_HIGHEST_READ, schemdata,
-               nodecount, 2, 2, true);
-
-       // Fix probability values for nodes that were ignore; removed in v2
-       if (version < 2) {
-               for (size_t i = 0; i != nodecount; i++) {
-                       if (schemdata[i].param1 == 0)
-                               schemdata[i].param1 = MTSCHEM_PROB_ALWAYS_OLD;
-                       if (have_cignore && schemdata[i].getContent() == cignore)
-                               schemdata[i].param1 = MTSCHEM_PROB_NEVER;
-               }
-       }
-
-       // Fix probability values for probability range truncation introduced in v4
-       if (version < 4) {
-               for (s16 y = 0; y != size.Y; y++)
-                       slice_probs[y] >>= 1;
-               for (size_t i = 0; i != nodecount; i++)
-                       schemdata[i].param1 >>= 1;
-       }
-
-       return true;
-}
-
-
-bool Schematic::serializeToMts(std::ostream *os,
-       const std::vector<std::string> &names)
-{
-       std::ostream &ss = *os;
-
-       writeU32(ss, MTSCHEM_FILE_SIGNATURE);         // signature
-       writeU16(ss, MTSCHEM_FILE_VER_HIGHEST_WRITE); // version
-       writeV3S16(ss, size);                         // schematic size
-
-       for (int y = 0; y != size.Y; y++)             // Y slice probabilities
-               writeU8(ss, slice_probs[y]);
-
-       writeU16(ss, names.size()); // name count
-       for (size_t i = 0; i != names.size(); i++)
-               ss << serializeString(names[i]); // node names
-
-       // compressed bulk node data
-       MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE,
-               schemdata, size.X * size.Y * size.Z, 2, 2, true);
-
-       return true;
-}
-
-
-bool Schematic::serializeToLua(std::ostream *os,
-       const std::vector<std::string> &names, bool use_comments, u32 indent_spaces)
-{
-       std::ostream &ss = *os;
-
-       std::string indent("\t");
-       if (indent_spaces > 0)
-               indent.assign(indent_spaces, ' ');
-
-       //// Write header
-       {
-               ss << "schematic = {" << std::endl;
-               ss << indent << "size = "
-                       << "{x=" << size.X
-                       << ", y=" << size.Y
-                       << ", z=" << size.Z
-                       << "}," << std::endl;
-       }
-
-       //// Write y-slice probabilities
-       {
-               ss << indent << "yslice_prob = {" << std::endl;
-
-               for (u16 y = 0; y != size.Y; y++) {
-                       u8 probability = slice_probs[y] & MTSCHEM_PROB_MASK;
-
-                       ss << indent << indent << "{"
-                               << "ypos=" << y
-                               << ", prob=" << (u16)probability * 2
-                               << "}," << std::endl;
-               }
-
-               ss << indent << "}," << std::endl;
-       }
-
-       //// Write node data
-       {
-               ss << indent << "data = {" << std::endl;
-
-               u32 i = 0;
-               for (u16 z = 0; z != size.Z; z++)
-               for (u16 y = 0; y != size.Y; y++) {
-                       if (use_comments) {
-                               ss << std::endl
-                                       << indent << indent
-                                       << "-- z=" << z
-                                       << ", y=" << y << std::endl;
-                       }
-
-                       for (u16 x = 0; x != size.X; x++, i++) {
-                               u8 probability   = schemdata[i].param1 & MTSCHEM_PROB_MASK;
-                               bool force_place = schemdata[i].param1 & MTSCHEM_FORCE_PLACE;
-
-                               ss << indent << indent << "{"
-                                       << "name=\"" << names[schemdata[i].getContent()]
-                                       << "\", prob=" << (u16)probability * 2
-                                       << ", param2=" << (u16)schemdata[i].param2;
-
-                               if (force_place)
-                                       ss << ", force_place=true";
-
-                               ss << "}," << std::endl;
-                       }
-               }
-
-               ss << indent << "}," << std::endl;
-       }
-
-       ss << "}" << std::endl;
-
-       return true;
-}
-
-
-bool Schematic::loadSchematicFromFile(const std::string &filename,
-       INodeDefManager *ndef, StringMap *replace_names)
-{
-       std::ifstream is(filename.c_str(), std::ios_base::binary);
-       if (!is.good()) {
-               errorstream << __FUNCTION__ << ": unable to open file '"
-                       << filename << "'" << std::endl;
-               return false;
-       }
-
-       size_t origsize = m_nodenames.size();
-       if (!deserializeFromMts(&is, &m_nodenames))
-               return false;
-
-       m_nnlistsizes.push_back(m_nodenames.size() - origsize);
-
-       name = filename;
-
-       if (replace_names) {
-               for (size_t i = origsize; i < m_nodenames.size(); i++) {
-                       std::string &node_name = m_nodenames[i];
-                       StringMap::iterator it = replace_names->find(node_name);
-                       if (it != replace_names->end())
-                               node_name = it->second;
-               }
-       }
-
-       if (ndef)
-               ndef->pendNodeResolve(this);
-
-       return true;
-}
-
-
-bool Schematic::saveSchematicToFile(const std::string &filename,
-       INodeDefManager *ndef)
-{
-       MapNode *orig_schemdata = schemdata;
-       std::vector<std::string> ndef_nodenames;
-       std::vector<std::string> *names;
-
-       if (m_resolve_done && ndef == NULL)
-               ndef = m_ndef;
-
-       if (ndef) {
-               names = &ndef_nodenames;
-
-               u32 volume = size.X * size.Y * size.Z;
-               schemdata = new MapNode[volume];
-               for (u32 i = 0; i != volume; i++)
-                       schemdata[i] = orig_schemdata[i];
-
-               generate_nodelist_and_update_ids(schemdata, volume, names, ndef);
-       } else { // otherwise, use the names we have on hand in the list
-               names = &m_nodenames;
-       }
-
-       std::ostringstream os(std::ios_base::binary);
-       bool status = serializeToMts(&os, *names);
-
-       if (ndef) {
-               delete []schemdata;
-               schemdata = orig_schemdata;
-       }
-
-       if (!status)
-               return false;
-
-       return fs::safeWriteToFile(filename, os.str());
-}
-
-
-bool Schematic::getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2)
-{
-       MMVManip *vm = new MMVManip(map);
-
-       v3s16 bp1 = getNodeBlockPos(p1);
-       v3s16 bp2 = getNodeBlockPos(p2);
-       vm->initialEmerge(bp1, bp2);
-
-       size = p2 - p1 + 1;
-
-       slice_probs = new u8[size.Y];
-       for (s16 y = 0; y != size.Y; y++)
-               slice_probs[y] = MTSCHEM_PROB_ALWAYS;
-
-       schemdata = new MapNode[size.X * size.Y * size.Z];
-
-       u32 i = 0;
-       for (s16 z = p1.Z; z <= p2.Z; z++)
-       for (s16 y = p1.Y; y <= p2.Y; y++) {
-               u32 vi = vm->m_area.index(p1.X, y, z);
-               for (s16 x = p1.X; x <= p2.X; x++, i++, vi++) {
-                       schemdata[i] = vm->m_data[vi];
-                       schemdata[i].param1 = MTSCHEM_PROB_ALWAYS;
-               }
-       }
-
-       delete vm;
-       return true;
-}
-
-
-void Schematic::applyProbabilities(v3s16 p0,
-       std::vector<std::pair<v3s16, u8> > *plist,
-       std::vector<std::pair<s16, u8> > *splist)
-{
-       for (size_t i = 0; i != plist->size(); i++) {
-               v3s16 p = (*plist)[i].first - p0;
-               int index = p.Z * (size.Y * size.X) + p.Y * size.X + p.X;
-               if (index < size.Z * size.Y * size.X) {
-                       u8 prob = (*plist)[i].second;
-                       schemdata[index].param1 = prob;
-
-                       // trim unnecessary node names from schematic
-                       if (prob == MTSCHEM_PROB_NEVER)
-                               schemdata[index].setContent(CONTENT_AIR);
-               }
-       }
-
-       for (size_t i = 0; i != splist->size(); i++) {
-               s16 y = (*splist)[i].first - p0.Y;
-               slice_probs[y] = (*splist)[i].second;
-       }
-}
-
-
-void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount,
-       std::vector<std::string> *usednodes, INodeDefManager *ndef)
-{
-       std::unordered_map<content_t, content_t> nodeidmap;
-       content_t numids = 0;
-
-       for (size_t i = 0; i != nodecount; i++) {
-               content_t id;
-               content_t c = nodes[i].getContent();
-
-               std::unordered_map<content_t, content_t>::const_iterator it = nodeidmap.find(c);
-               if (it == nodeidmap.end()) {
-                       id = numids;
-                       numids++;
-
-                       usednodes->push_back(ndef->get(c).name);
-                       nodeidmap.insert(std::make_pair(c, id));
-               } else {
-                       id = it->second;
-               }
-               nodes[i].setContent(id);
-       }
-}
diff --git a/src/mg_schematic.h b/src/mg_schematic.h
deleted file mode 100644 (file)
index 069b594..0000000
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
-Minetest
-Copyright (C) 2014-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
-Copyright (C) 2015-2017 paramat
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include <map>
-#include "mg_decoration.h"
-#include "util/string.h"
-
-class Map;
-class ServerMap;
-class Mapgen;
-class MMVManip;
-class PseudoRandom;
-class NodeResolver;
-class Server;
-
-/*
-       Minetest Schematic File Format
-
-       All values are stored in big-endian byte order.
-       [u32] signature: 'MTSM'
-       [u16] version: 4
-       [u16] size X
-       [u16] size Y
-       [u16] size Z
-       For each Y:
-               [u8] slice probability value
-       [Name-ID table] Name ID Mapping Table
-               [u16] name-id count
-               For each name-id mapping:
-                       [u16] name length
-                       [u8[]] name
-       ZLib deflated {
-       For each node in schematic:  (for z, y, x)
-               [u16] content
-       For each node in schematic:
-               [u8] param1
-                 bit 0-6: probability
-                 bit 7:   specific node force placement
-       For each node in schematic:
-               [u8] param2
-       }
-
-       Version changes:
-       1 - Initial version
-       2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always
-       3 - Added y-slice probabilities; this allows for variable height structures
-       4 - Compressed range of node occurence prob., added per-node force placement bit
-*/
-
-//// Schematic constants
-#define MTSCHEM_FILE_SIGNATURE 0x4d54534d // 'MTSM'
-#define MTSCHEM_FILE_VER_HIGHEST_READ  4
-#define MTSCHEM_FILE_VER_HIGHEST_WRITE 4
-
-#define MTSCHEM_PROB_MASK       0x7F
-
-#define MTSCHEM_PROB_NEVER      0x00
-#define MTSCHEM_PROB_ALWAYS     0x7F
-#define MTSCHEM_PROB_ALWAYS_OLD 0xFF
-
-#define MTSCHEM_FORCE_PLACE     0x80
-
-enum SchematicType
-{
-       SCHEMATIC_NORMAL,
-};
-
-enum SchematicFormatType {
-       SCHEM_FMT_HANDLE,
-       SCHEM_FMT_MTS,
-       SCHEM_FMT_LUA,
-};
-
-class Schematic : public ObjDef, public NodeResolver {
-public:
-       Schematic();
-       virtual ~Schematic();
-
-       virtual void resolveNodeNames();
-
-       bool loadSchematicFromFile(const std::string &filename, INodeDefManager *ndef,
-               StringMap *replace_names=NULL);
-       bool saveSchematicToFile(const std::string &filename, INodeDefManager *ndef);
-       bool getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2);
-
-       bool deserializeFromMts(std::istream *is, std::vector<std::string> *names);
-       bool serializeToMts(std::ostream *os, const std::vector<std::string> &names);
-       bool serializeToLua(std::ostream *os, const std::vector<std::string> &names,
-               bool use_comments, u32 indent_spaces);
-
-       void blitToVManip(MMVManip *vm, v3s16 p, Rotation rot, bool force_place);
-       bool placeOnVManip(MMVManip *vm, v3s16 p, u32 flags, Rotation rot, bool force_place);
-       void placeOnMap(ServerMap *map, v3s16 p, u32 flags, Rotation rot, bool force_place);
-
-       void applyProbabilities(v3s16 p0,
-               std::vector<std::pair<v3s16, u8> > *plist,
-               std::vector<std::pair<s16, u8> > *splist);
-
-       std::vector<content_t> c_nodes;
-       u32 flags = 0;
-       v3s16 size;
-       MapNode *schemdata = nullptr;
-       u8 *slice_probs = nullptr;
-};
-
-class SchematicManager : public ObjDefManager {
-public:
-       SchematicManager(Server *server);
-       virtual ~SchematicManager() = default;
-
-       virtual void clear();
-
-       const char *getObjectTitle() const
-       {
-               return "schematic";
-       }
-
-       static Schematic *create(SchematicType type)
-       {
-               return new Schematic;
-       }
-
-private:
-       Server *m_server;
-};
-
-void generate_nodelist_and_update_ids(MapNode *nodes, size_t nodecount,
-       std::vector<std::string> *usednodes, INodeDefManager *ndef);
diff --git a/src/modalMenu.h b/src/modalMenu.h
deleted file mode 100644 (file)
index f41591c..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
-Minetest
-Copyright (C) 2013 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 Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include "irrlichttypes_extrabloated.h"
-#ifdef HAVE_TOUCHSCREENGUI
-#include "touchscreengui.h"
-#endif
-
-class GUIModalMenu;
-
-class IMenuManager
-{
-public:
-       // A GUIModalMenu calls these when this class is passed as a parameter
-       virtual void createdMenu(gui::IGUIElement *menu) = 0;
-       virtual void deletingMenu(gui::IGUIElement *menu) = 0;
-};
-
-/*
-       Remember to drop() the menu after creating, so that it can
-       remove itself when it wants to.
-*/
-
-class GUIModalMenu : public gui::IGUIElement
-{
-public:
-       GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, s32 id,
-                       IMenuManager *menumgr):
-               IGUIElement(gui::EGUIET_ELEMENT, env, parent, id,
-                               core::rect<s32>(0,0,100,100))
-       {
-               m_menumgr = menumgr;
-
-               setVisible(true);
-               Environment->setFocus(this);
-               m_menumgr->createdMenu(this);
-       }
-
-       virtual ~GUIModalMenu()
-       {
-               m_menumgr->deletingMenu(this);
-       }
-
-       void allowFocusRemoval(bool allow)
-       {
-               m_allow_focus_removal = allow;
-       }
-
-       bool canTakeFocus(gui::IGUIElement *e)
-       {
-               return (e && (e == this || isMyChild(e))) || m_allow_focus_removal;
-       }
-
-       void draw()
-       {
-               if(!IsVisible)
-                       return;
-
-               video::IVideoDriver* driver = Environment->getVideoDriver();
-               v2u32 screensize = driver->getScreenSize();
-               if(screensize != m_screensize_old /*|| m_force_regenerate_gui*/)
-               {
-                       m_screensize_old = screensize;
-                       regenerateGui(screensize);
-                       //m_force_regenerate_gui = false;
-               }
-
-               drawMenu();
-       }
-
-       /*
-               This should be called when the menu wants to quit.
-
-               WARNING: THIS DEALLOCATES THE MENU FROM MEMORY. Return
-               immediately if you call this from the menu itself.
-
-               (More precisely, this decrements the reference count.)
-       */
-       void quitMenu()
-       {
-               allowFocusRemoval(true);
-               // This removes Environment's grab on us
-               Environment->removeFocus(this);
-               m_menumgr->deletingMenu(this);
-               this->remove();
-#ifdef HAVE_TOUCHSCREENGUI
-               if (g_touchscreengui)
-                       g_touchscreengui->show();
-#endif
-       }
-
-       void removeChildren()
-       {
-               const core::list<gui::IGUIElement*> &children = getChildren();
-               core::list<gui::IGUIElement*> children_copy;
-               for (gui::IGUIElement *i : children) {
-                       children_copy.push_back(i);
-               }
-
-               for (gui::IGUIElement *i : children_copy) {
-                       i->remove();
-               }
-       }
-
-       virtual void regenerateGui(v2u32 screensize) = 0;
-       virtual void drawMenu() = 0;
-       virtual bool preprocessEvent(const SEvent& event) { return false; };
-       virtual bool OnEvent(const SEvent& event) { return false; };
-       virtual bool pausesGame(){ return false; } // Used for pause menu
-
-protected:
-       //bool m_force_regenerate_gui;
-       v2u32 m_screensize_old;
-private:
-       IMenuManager *m_menumgr;
-       // This might be necessary to expose to the implementation if it
-       // wants to launch other menus
-       bool m_allow_focus_removal = false;
-};
index 15c247f76c9c87c25e0682cf7901a8d8bf6a7b62..ddac4ae20f66ff5de320399c7f6893d26e5019f6 100644 (file)
@@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #pragma once
 
 #include <cassert>
-#include "../threading/thread.h"
+#include "threading/thread.h"
 #include "connection.h"
 
 namespace con
index 73661a7b63cb9dc9547aefb18eede6117504589f..a9ff1be4481fc3c672978e79f4aaab64c82f8e10 100644 (file)
@@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "tool.h"
 #include "serverobject.h"
 #include "porting.h"
-#include "mg_schematic.h"
+#include "mapgen/mg_schematic.h"
 #include "noise.h"
 #include "util/pointedthing.h"
 #include "debug.h" // For FATAL_ERROR
index 517892b038c70e28ed2cb58e554e79c4477803aa..170640ce01f1a1088aa35b673a17626d8fc9d669 100644 (file)
@@ -22,7 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "common/c_converter.h"
 #include "log.h"
 #include "environment.h"
-#include "mapgen.h"
+#include "mapgen/mapgen.h"
 #include "lua_api/l_env.h"
 #include "server.h"
 
index f89d5d889efdac4b7c954b644c60b7ba2f520d80..aef36ce39539f61a2ecbfff160eea88ec57acbc4 100644 (file)
@@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "cpp_api/s_base.h"
 #include "util/string.h"
-#include "../guiMainMenu.h"
+#include "gui/guiMainMenu.h"
 
 class ScriptApiMainMenu : virtual public ScriptApiBase {
 public:
index a3605be79c9b675a5e5cd364bd7884f17c4eb583..4e08c15d4d03c223d9724b858d304616955941ab 100644 (file)
@@ -30,7 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "l_internal.h"
 #include "lua_api/l_item.h"
 #include "lua_api/l_nodemeta.h"
-#include "mainmenumanager.h"
+#include "gui/mainmenumanager.h"
 #include "map.h"
 #include "util/string.h"
 #include "nodedef.h"
index ca8b6bf13711875484676b0b3a4fd4a9bd483950..f45a8da80d2d78863402674f4ca74f5e1a7046fb 100644 (file)
@@ -34,7 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "daynightratio.h"
 #include "util/pointedthing.h"
 #include "content_sao.h"
-#include "treegen.h"
+#include "mapgen/treegen.h"
 #include "emerge.h"
 #include "pathfinder.h"
 #include "face_position_cache.h"
index 9cc4533a8fdfdaabafc6ce2e4c3096e8661e81f2..cdb27fb60ead44608058bbb9fa955dde859f9341 100644 (file)
@@ -21,17 +21,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "lua_api/l_internal.h"
 #include "common/c_content.h"
 #include "cpp_api/s_async.h"
-#include "guiEngine.h"
-#include "guiMainMenu.h"
-#include "guiKeyChangeMenu.h"
-#include "guiPathSelectMenu.h"
+#include "gui/guiEngine.h"
+#include "gui/guiMainMenu.h"
+#include "gui/guiKeyChangeMenu.h"
+#include "gui/guiPathSelectMenu.h"
 #include "subgame.h"
 #include "version.h"
 #include "porting.h"
 #include "filesys.h"
 #include "convert_json.h"
 #include "serverlist.h"
-#include "mapgen.h"
+#include "mapgen/mapgen.h"
 #include "settings.h"
 
 #include <IFileArchive.h>
index 9ec4d5002ba935239a9d6f44ca05bc29ce6848d3..01f9d1c41033dd964f181c4dd1a6bdad607194bd 100644 (file)
@@ -27,12 +27,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "server.h"
 #include "environment.h"
 #include "emerge.h"
-#include "mg_biome.h"
-#include "mg_ore.h"
-#include "mg_decoration.h"
-#include "mg_schematic.h"
-#include "mapgen_v5.h"
-#include "mapgen_v7.h"
+#include "mapgen/mg_biome.h"
+#include "mapgen/mg_ore.h"
+#include "mapgen/mg_decoration.h"
+#include "mapgen/mg_schematic.h"
+#include "mapgen/mapgen_v5.h"
+#include "mapgen/mapgen_v7.h"
 #include "filesys.h"
 #include "settings.h"
 #include "log.h"
index 07ce36daae05d70aa606a811970201b0c0b2d6c9..e7d517ce0f1ac96ecf76fe0f2cffcc0256d74657 100644 (file)
@@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "l_sound.h"
 #include "l_internal.h"
 #include "common/c_content.h"
-#include "guiEngine.h"
+#include "gui/guiEngine.h"
 
 
 int ModApiSound::l_sound_play(lua_State *L)
index a84ecfc0fa265947b664e4a2808b89e517338164..ca837b6552a6f761fe92816dc13889e891779cd2 100644 (file)
@@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "map.h"
 #include "mapblock.h"
 #include "server.h"
-#include "mapgen.h"
+#include "mapgen/mapgen.h"
 #include "voxelalgorithms.h"
 
 // garbage collector
index b7627f6cd06232f3d8d9b0e5c63301b0cf15851e..98c3eca9a6387c058b59b3dcf7dcdcfca294bec4 100644 (file)
@@ -44,8 +44,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "itemdef.h"
 #include "craftdef.h"
 #include "emerge.h"
-#include "mapgen.h"
-#include "mg_biome.h"
+#include "mapgen/mapgen.h"
+#include "mapgen/mg_biome.h"
 #include "content_mapnode.h"
 #include "content_nodemeta.h"
 #include "content_sao.h"
@@ -61,7 +61,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/base64.h"
 #include "util/sha1.h"
 #include "util/hex.h"
-#include "database.h"
+#include "database/database.h"
 #include "chatmessage.h"
 #include "chat_interface.h"
 #include "remoteplayer.h"
index aed1dc22f42f63a3be6caf7ab63890d5530cdd1b..f68f2a27de4490b66995a41d0fe096bb2205366d 100644 (file)
@@ -37,11 +37,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "threading/mutex_auto_lock.h"
 #include "filesys.h"
 #include "gameparams.h"
-#include "database-dummy.h"
-#include "database-files.h"
-#include "database-sqlite3.h"
+#include "database/database-dummy.h"
+#include "database/database-files.h"
+#include "database/database-sqlite3.h"
 #if USE_POSTGRESQL
-#include "database-postgresql.h"
+#include "database/database-postgresql.h"
 #endif
 #include <algorithm>
 
index eb2ea01c15b3c8d084ace5a662e9be3f900fa092..f5ff870fbb58390f6011fff079206f1a69a72b4b 100644 (file)
@@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 #include "util/strfnd.h"
 #include "defaultsettings.h"  // for override_default_settings
-#include "mapgen.h"  // for MapgenParams
+#include "mapgen/mapgen.h"  // for MapgenParams
 #include "util/string.h"
 
 #ifndef SERVER
diff --git a/src/touchscreengui.cpp b/src/touchscreengui.cpp
deleted file mode 100644 (file)
index e849b40..0000000
+++ /dev/null
@@ -1,1063 +0,0 @@
-/*
-Copyright (C) 2014 sapier
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "touchscreengui.h"
-#include "irrlichttypes.h"
-#include "irr_v2d.h"
-#include "log.h"
-#include "keycode.h"
-#include "settings.h"
-#include "gettime.h"
-#include "util/numeric.h"
-#include "porting.h"
-#include "guiscalingfilter.h"
-
-#include <iostream>
-#include <algorithm>
-
-#include <ISceneCollisionManager.h>
-
-// Very slow button repeat frequency (in seconds)
-#define SLOW_BUTTON_REPEAT     (1.0f)
-
-using namespace irr::core;
-
-const char** touchgui_button_imagenames = (const char*[]) {
-       "up_arrow.png",
-       "down_arrow.png",
-       "left_arrow.png",
-       "right_arrow.png",
-       "jump_btn.png",
-       "down.png"
-};
-
-static irr::EKEY_CODE id2keycode(touch_gui_button_id id)
-{
-       std::string key = "";
-       switch (id) {
-               case forward_id:
-                       key = "forward";
-                       break;
-               case left_id:
-                       key = "left";
-                       break;
-               case right_id:
-                       key = "right";
-                       break;
-               case backward_id:
-                       key = "backward";
-                       break;
-               case inventory_id:
-                       key = "inventory";
-                       break;
-               case drop_id:
-                       key = "drop";
-                       break;
-               case jump_id:
-                       key = "jump";
-                       break;
-               case crunch_id:
-                       key = "sneak";
-                       break;
-               case fly_id:
-                       key = "freemove";
-                       break;
-               case noclip_id:
-                       key = "noclip";
-                       break;
-               case fast_id:
-                       key = "fastmove";
-                       break;
-               case debug_id:
-                       key = "toggle_debug";
-                       break;
-               case chat_id:
-                       key = "chat";
-                       break;
-               case camera_id:
-                       key = "camera_mode";
-                       break;
-               case range_id:
-                       key = "rangeselect";
-                       break;
-       }
-       assert(key != "");
-       return keyname_to_keycode(g_settings->get("keymap_" + key).c_str());
-}
-
-TouchScreenGUI *g_touchscreengui;
-
-static void load_button_texture(button_info* btn, const char* path,
-               rect<s32> button_rect, ISimpleTextureSource* tsrc, video::IVideoDriver *driver)
-{
-       unsigned int tid;
-       video::ITexture *texture = guiScalingImageButton(driver,
-                       tsrc->getTexture(path, &tid), button_rect.getWidth(),
-                       button_rect.getHeight());
-       if (texture) {
-               btn->guibutton->setUseAlphaChannel(true);
-               if (g_settings->getBool("gui_scaling_filter")) {
-                       rect<s32> txr_rect = rect<s32>(0, 0, button_rect.getWidth(), button_rect.getHeight());
-                       btn->guibutton->setImage(texture, txr_rect);
-                       btn->guibutton->setPressedImage(texture, txr_rect);
-                       btn->guibutton->setScaleImage(false);
-               } else {
-                       btn->guibutton->setImage(texture);
-                       btn->guibutton->setPressedImage(texture);
-                       btn->guibutton->setScaleImage(true);
-               }
-               btn->guibutton->setDrawBorder(false);
-               btn->guibutton->setText(L"");
-               }
-}
-
-AutoHideButtonBar::AutoHideButtonBar(IrrlichtDevice *device,
-               IEventReceiver* receiver) :
-                       m_driver(device->getVideoDriver()),
-                       m_guienv(device->getGUIEnvironment()),
-                       m_receiver(receiver)
-{
-}
-
-void AutoHideButtonBar::init(ISimpleTextureSource* tsrc,
-               const char* starter_img, int button_id, v2s32 UpperLeft,
-               v2s32 LowerRight, autohide_button_bar_dir dir, float timeout)
-{
-       m_texturesource = tsrc;
-
-       m_upper_left = UpperLeft;
-       m_lower_right = LowerRight;
-
-       /* init settings bar */
-
-       irr::core::rect<int> current_button = rect<s32>(UpperLeft.X, UpperLeft.Y,
-                       LowerRight.X, LowerRight.Y);
-
-       m_starter.guibutton         = m_guienv->addButton(current_button, 0, button_id, L"", 0);
-       m_starter.guibutton->grab();
-       m_starter.repeatcounter     = -1;
-       m_starter.keycode           = KEY_OEM_8; // use invalid keycode as it's not relevant
-       m_starter.immediate_release = true;
-       m_starter.ids.clear();
-
-       load_button_texture(&m_starter, starter_img, current_button,
-                       m_texturesource, m_driver);
-
-       m_dir = dir;
-       m_timeout_value = timeout;
-
-       m_initialized = true;
-}
-
-AutoHideButtonBar::~AutoHideButtonBar()
-{
-       if (m_starter.guibutton) {
-               m_starter.guibutton->setVisible(false);
-               m_starter.guibutton->drop();
-       }
-}
-
-void AutoHideButtonBar::addButton(touch_gui_button_id button_id,
-               const wchar_t* caption, const char* btn_image)
-{
-
-       if (!m_initialized) {
-               errorstream << "AutoHideButtonBar::addButton not yet initialized!"
-                               << std::endl;
-               return;
-       }
-       int button_size = 0;
-
-       if ((m_dir == AHBB_Dir_Top_Bottom) || (m_dir == AHBB_Dir_Bottom_Top)) {
-               button_size = m_lower_right.X - m_upper_left.X;
-       } else {
-               button_size = m_lower_right.Y - m_upper_left.Y;
-       }
-
-       irr::core::rect<int> current_button;
-
-       if ((m_dir == AHBB_Dir_Right_Left) || (m_dir == AHBB_Dir_Left_Right)) {
-
-               int x_start = 0;
-               int x_end = 0;
-
-               if (m_dir == AHBB_Dir_Left_Right) {
-                       x_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size())
-                                       + (button_size * 0.25);
-                       x_end = x_start + button_size;
-               } else {
-                       x_end = m_upper_left.X - (button_size * 1.25 * m_buttons.size())
-                                       - (button_size * 0.25);
-                       x_start = x_end - button_size;
-               }
-
-               current_button = rect<s32>(x_start, m_upper_left.Y, x_end,
-                               m_lower_right.Y);
-       } else {
-               int y_start = 0;
-               int y_end = 0;
-
-               if (m_dir == AHBB_Dir_Top_Bottom) {
-                       y_start = m_lower_right.X + (button_size * 1.25 * m_buttons.size())
-                                       + (button_size * 0.25);
-                       y_end = y_start + button_size;
-               } else {
-                       y_end = m_upper_left.X - (button_size * 1.25 * m_buttons.size())
-                                       - (button_size * 0.25);
-                       y_start = y_end - button_size;
-               }
-
-               current_button = rect<s32>(m_upper_left.X, y_start, m_lower_right.Y,
-                               y_end);
-       }
-
-       button_info* btn       = new button_info();
-       btn->guibutton         = m_guienv->addButton(current_button, 0, button_id, caption, 0);
-       btn->guibutton->grab();
-       btn->guibutton->setVisible(false);
-       btn->guibutton->setEnabled(false);
-       btn->repeatcounter     = -1;
-       btn->keycode           = id2keycode(button_id);
-       btn->immediate_release = true;
-       btn->ids.clear();
-
-       load_button_texture(btn, btn_image, current_button, m_texturesource,
-                       m_driver);
-
-       m_buttons.push_back(btn);
-}
-
-bool AutoHideButtonBar::isButton(const SEvent &event)
-{
-       IGUIElement* rootguielement = m_guienv->getRootGUIElement();
-
-       if (rootguielement == NULL) {
-               return false;
-       }
-
-       gui::IGUIElement *element = rootguielement->getElementFromPoint(
-                       core::position2d<s32>(event.TouchInput.X, event.TouchInput.Y));
-
-       if (element == NULL) {
-               return false;
-       }
-
-       if (m_active) {
-               /* check for all buttons in vector */
-
-               std::vector<button_info*>::iterator iter = m_buttons.begin();
-
-               while (iter != m_buttons.end()) {
-                       if ((*iter)->guibutton == element) {
-
-                               SEvent* translated = new SEvent();
-                               memset(translated, 0, sizeof(SEvent));
-                               translated->EventType            = irr::EET_KEY_INPUT_EVENT;
-                               translated->KeyInput.Key         = (*iter)->keycode;
-                               translated->KeyInput.Control     = false;
-                               translated->KeyInput.Shift       = false;
-                               translated->KeyInput.Char        = 0;
-
-                               /* add this event */
-                               translated->KeyInput.PressedDown = true;
-                               m_receiver->OnEvent(*translated);
-
-                               /* remove this event */
-                               translated->KeyInput.PressedDown = false;
-                               m_receiver->OnEvent(*translated);
-
-                               delete translated;
-
-                               (*iter)->ids.push_back(event.TouchInput.ID);
-
-                               m_timeout = 0;
-
-                               return true;
-                       }
-                       ++iter;
-               }
-       } else {
-               /* check for starter button only */
-               if (element == m_starter.guibutton) {
-                       m_starter.ids.push_back(event.TouchInput.ID);
-                       m_starter.guibutton->setVisible(false);
-                       m_starter.guibutton->setEnabled(false);
-                       m_active = true;
-                       m_timeout = 0;
-
-                       std::vector<button_info*>::iterator iter = m_buttons.begin();
-
-                       while (iter != m_buttons.end()) {
-                               (*iter)->guibutton->setVisible(true);
-                               (*iter)->guibutton->setEnabled(true);
-                               ++iter;
-                       }
-
-                       return true;
-               }
-       }
-       return false;
-}
-
-bool AutoHideButtonBar::isReleaseButton(int eventID)
-{
-       std::vector<int>::iterator id = std::find(m_starter.ids.begin(),
-                       m_starter.ids.end(), eventID);
-
-       if (id != m_starter.ids.end()) {
-               m_starter.ids.erase(id);
-               return true;
-       }
-
-       std::vector<button_info*>::iterator iter = m_buttons.begin();
-
-       while (iter != m_buttons.end()) {
-               std::vector<int>::iterator id = std::find((*iter)->ids.begin(),
-                               (*iter)->ids.end(), eventID);
-
-               if (id != (*iter)->ids.end()) {
-                       (*iter)->ids.erase(id);
-                       // TODO handle settings button release
-                       return true;
-               }
-               ++iter;
-       }
-
-       return false;
-}
-
-void AutoHideButtonBar::step(float dtime)
-{
-       if (m_active) {
-               m_timeout += dtime;
-
-               if (m_timeout > m_timeout_value) {
-                       deactivate();
-               }
-       }
-}
-
-void AutoHideButtonBar::deactivate()
-{
-       if (m_visible) {
-               m_starter.guibutton->setVisible(true);
-               m_starter.guibutton->setEnabled(true);
-       }
-       m_active = false;
-
-       std::vector<button_info*>::iterator iter = m_buttons.begin();
-
-       while (iter != m_buttons.end()) {
-                       (*iter)->guibutton->setVisible(false);
-                       (*iter)->guibutton->setEnabled(false);
-               ++iter;
-       }
-}
-
-void AutoHideButtonBar::hide()
-{
-       m_visible = false;
-       m_starter.guibutton->setVisible(false);
-       m_starter.guibutton->setEnabled(false);
-
-       std::vector<button_info*>::iterator iter = m_buttons.begin();
-
-       while (iter != m_buttons.end()) {
-               (*iter)->guibutton->setVisible(false);
-               (*iter)->guibutton->setEnabled(false);
-               ++iter;
-       }
-}
-
-void AutoHideButtonBar::show()
-{
-       m_visible = true;
-
-       if (m_active) {
-               std::vector<button_info*>::iterator iter = m_buttons.begin();
-
-               while (iter != m_buttons.end()) {
-                       (*iter)->guibutton->setVisible(true);
-                       (*iter)->guibutton->setEnabled(true);
-                       ++iter;
-               }
-       } else {
-               m_starter.guibutton->setVisible(true);
-               m_starter.guibutton->setEnabled(true);
-       }
-}
-
-TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver* receiver):
-       m_device(device),
-       m_guienv(device->getGUIEnvironment()),
-       m_receiver(receiver),
-       m_settingsbar(device, receiver),
-       m_rarecontrolsbar(device, receiver)
-{
-       for (unsigned int i=0; i < after_last_element_id; i++) {
-               m_buttons[i].guibutton     =  0;
-               m_buttons[i].repeatcounter = -1;
-               m_buttons[i].repeatdelay   = BUTTON_REPEAT_DELAY;
-       }
-
-       m_screensize = m_device->getVideoDriver()->getScreenSize();
-}
-
-void TouchScreenGUI::initButton(touch_gui_button_id id, rect<s32> button_rect,
-               std::wstring caption, bool immediate_release, float repeat_delay)
-{
-
-       button_info* btn       = &m_buttons[id];
-       btn->guibutton         = m_guienv->addButton(button_rect, 0, id, caption.c_str());
-       btn->guibutton->grab();
-       btn->repeatcounter     = -1;
-       btn->repeatdelay       = repeat_delay;
-       btn->keycode           = id2keycode(id);
-       btn->immediate_release = immediate_release;
-       btn->ids.clear();
-
-       load_button_texture(btn,touchgui_button_imagenames[id],button_rect,
-                       m_texturesource, m_device->getVideoDriver());
-}
-
-static int getMaxControlPadSize(float density) {
-       return 200 * density * g_settings->getFloat("hud_scaling");
-}
-
-int TouchScreenGUI::getGuiButtonSize()
-{
-       u32 control_pad_size = MYMIN((2 * m_screensize.Y) / 3,
-                       getMaxControlPadSize(porting::getDisplayDensity()));
-
-       return control_pad_size / 3;
-}
-
-void TouchScreenGUI::init(ISimpleTextureSource* tsrc)
-{
-       assert(tsrc != 0);
-
-       u32 button_size      = getGuiButtonSize();
-       m_visible            = true;
-       m_texturesource      = tsrc;
-       /*
-       draw control pad
-       0 1 2
-       3 4 5
-       for now only 0, 1, 2, and 4 are used
-       */
-       int number = 0;
-       for (int y = 0; y < 2; ++y)
-               for (int x = 0; x < 3; ++x, ++number) {
-                       rect<s32> button_rect(
-                                       x * button_size, m_screensize.Y - button_size * (2 - y),
-                                       (x + 1) * button_size, m_screensize.Y - button_size * (1 - y)
-                       );
-                       touch_gui_button_id id = after_last_element_id;
-                       std::wstring caption;
-                       switch (number) {
-                       case 0:
-                               id = left_id;
-                               caption = L"<";
-                               break;
-                       case 1:
-                               id = forward_id;
-                               caption = L"^";
-                               break;
-                       case 2:
-                               id = right_id;
-                               caption = L">";
-                               break;
-                       case 4:
-                               id = backward_id;
-                               caption = L"v";
-                               break;
-                       }
-                       if (id != after_last_element_id) {
-                               initButton(id, button_rect, caption, false);
-                               }
-               }
-
-       /* init jump button */
-       initButton(jump_id,
-                       rect<s32>(m_screensize.X-(1.75*button_size),
-                                       m_screensize.Y - (0.5*button_size),
-                                       m_screensize.X-(0.25*button_size),
-                                       m_screensize.Y),
-                       L"x",false);
-
-       /* init crunch button */
-       initButton(crunch_id,
-                       rect<s32>(m_screensize.X-(3.25*button_size),
-                                       m_screensize.Y - (0.5*button_size),
-                                       m_screensize.X-(1.75*button_size),
-                                       m_screensize.Y),
-                       L"H",false);
-
-       m_settingsbar.init(m_texturesource, "gear_icon.png", settings_starter_id,
-                       v2s32(m_screensize.X - (button_size / 2),
-                                       m_screensize.Y - ((SETTINGS_BAR_Y_OFFSET + 1) * button_size)
-                                                       + (button_size * 0.5)),
-                       v2s32(m_screensize.X,
-                                       m_screensize.Y - (SETTINGS_BAR_Y_OFFSET * button_size)
-                                                       + (button_size * 0.5)), AHBB_Dir_Right_Left,
-                       3.0);
-
-       m_settingsbar.addButton(fly_id,    L"fly",       "fly_btn.png");
-       m_settingsbar.addButton(noclip_id, L"noclip",    "noclip_btn.png");
-       m_settingsbar.addButton(fast_id,   L"fast",      "fast_btn.png");
-       m_settingsbar.addButton(debug_id,  L"debug",     "debug_btn.png");
-       m_settingsbar.addButton(camera_id, L"camera",    "camera_btn.png");
-       m_settingsbar.addButton(range_id,  L"rangeview", "rangeview_btn.png");
-
-       m_rarecontrolsbar.init(m_texturesource, "rare_controls.png",
-                       rare_controls_starter_id,
-                       v2s32(0,
-                                       m_screensize.Y
-                                                       - ((RARE_CONTROLS_BAR_Y_OFFSET + 1) * button_size)
-                                                       + (button_size * 0.5)),
-                       v2s32(button_size / 2,
-                                       m_screensize.Y - (RARE_CONTROLS_BAR_Y_OFFSET * button_size)
-                                                       + (button_size * 0.5)), AHBB_Dir_Left_Right,
-                       2);
-
-       m_rarecontrolsbar.addButton(chat_id,      L"Chat", "chat_btn.png");
-       m_rarecontrolsbar.addButton(inventory_id, L"inv",  "inventory_btn.png");
-       m_rarecontrolsbar.addButton(drop_id,      L"drop", "drop_btn.png");
-
-}
-
-touch_gui_button_id TouchScreenGUI::getButtonID(s32 x, s32 y)
-{
-       IGUIElement* rootguielement = m_guienv->getRootGUIElement();
-
-       if (rootguielement != NULL) {
-               gui::IGUIElement *element =
-                               rootguielement->getElementFromPoint(core::position2d<s32>(x,y));
-
-               if (element) {
-                       for (unsigned int i=0; i < after_last_element_id; i++) {
-                               if (element == m_buttons[i].guibutton) {
-                                       return (touch_gui_button_id) i;
-                               }
-                       }
-               }
-       }
-       return after_last_element_id;
-}
-
-touch_gui_button_id TouchScreenGUI::getButtonID(int eventID)
-{
-       for (unsigned int i=0; i < after_last_element_id; i++) {
-               button_info* btn = &m_buttons[i];
-
-               std::vector<int>::iterator id =
-                               std::find(btn->ids.begin(),btn->ids.end(), eventID);
-
-               if (id != btn->ids.end())
-                       return (touch_gui_button_id) i;
-       }
-
-       return after_last_element_id;
-}
-
-bool TouchScreenGUI::isHUDButton(const SEvent &event)
-{
-       // check if hud item is pressed
-       for (std::map<int,rect<s32> >::iterator iter = m_hud_rects.begin();
-                       iter != m_hud_rects.end(); ++iter) {
-               if (iter->second.isPointInside(
-                               v2s32(event.TouchInput.X,
-                                               event.TouchInput.Y)
-                       )) {
-                       if ( iter->first < 8) {
-                               SEvent* translated = new SEvent();
-                               memset(translated,0,sizeof(SEvent));
-                               translated->EventType = irr::EET_KEY_INPUT_EVENT;
-                               translated->KeyInput.Key         = (irr::EKEY_CODE) (KEY_KEY_1 + iter->first);
-                               translated->KeyInput.Control     = false;
-                               translated->KeyInput.Shift       = false;
-                               translated->KeyInput.PressedDown = true;
-                               m_receiver->OnEvent(*translated);
-                               m_hud_ids[event.TouchInput.ID]   = translated->KeyInput.Key;
-                               delete translated;
-                               return true;
-                       }
-               }
-       }
-       return false;
-}
-
-bool TouchScreenGUI::isReleaseHUDButton(int eventID)
-{
-       std::map<int,irr::EKEY_CODE>::iterator iter = m_hud_ids.find(eventID);
-
-       if (iter != m_hud_ids.end()) {
-               SEvent* translated = new SEvent();
-               memset(translated,0,sizeof(SEvent));
-               translated->EventType            = irr::EET_KEY_INPUT_EVENT;
-               translated->KeyInput.Key         = iter->second;
-               translated->KeyInput.PressedDown = false;
-               translated->KeyInput.Control     = false;
-               translated->KeyInput.Shift       = false;
-               m_receiver->OnEvent(*translated);
-               m_hud_ids.erase(iter);
-               delete translated;
-               return true;
-       }
-       return false;
-}
-
-void TouchScreenGUI::handleButtonEvent(touch_gui_button_id button,
-               int eventID, bool action)
-{
-       button_info* btn = &m_buttons[button];
-       SEvent* translated = new SEvent();
-       memset(translated,0,sizeof(SEvent));
-       translated->EventType            = irr::EET_KEY_INPUT_EVENT;
-       translated->KeyInput.Key         = btn->keycode;
-       translated->KeyInput.Control     = false;
-       translated->KeyInput.Shift       = false;
-       translated->KeyInput.Char        = 0;
-
-       /* add this event */
-       if (action) {
-               assert(std::find(btn->ids.begin(),btn->ids.end(), eventID) == btn->ids.end());
-
-               btn->ids.push_back(eventID);
-
-               if (btn->ids.size() > 1) return;
-
-               btn->repeatcounter = 0;
-               translated->KeyInput.PressedDown = true;
-               translated->KeyInput.Key = btn->keycode;
-               m_receiver->OnEvent(*translated);
-       }
-       /* remove event */
-       if ((!action) || (btn->immediate_release)) {
-
-               std::vector<int>::iterator pos =
-                               std::find(btn->ids.begin(),btn->ids.end(), eventID);
-               /* has to be in touch list */
-               assert(pos != btn->ids.end());
-               btn->ids.erase(pos);
-
-               if (btn->ids.size() > 0)  { return; }
-
-               translated->KeyInput.PressedDown = false;
-               btn->repeatcounter               = -1;
-               m_receiver->OnEvent(*translated);
-       }
-       delete translated;
-}
-
-
-void TouchScreenGUI::handleReleaseEvent(int evt_id)
-{
-       touch_gui_button_id button = getButtonID(evt_id);
-
-       /* handle button events */
-       if (button != after_last_element_id) {
-               handleButtonEvent(button, evt_id, false);
-       }
-       /* handle hud button events */
-       else if (isReleaseHUDButton(evt_id)) {
-               /* nothing to do here */
-       } else if (m_settingsbar.isReleaseButton(evt_id)) {
-               /* nothing to do here */
-       } else if (m_rarecontrolsbar.isReleaseButton(evt_id)) {
-               /* nothing to do here */
-       }
-       /* handle the point used for moving view */
-       else if (evt_id == m_move_id) {
-               m_move_id = -1;
-
-               /* if this pointer issued a mouse event issue symmetric release here */
-               if (m_move_sent_as_mouse_event) {
-                       SEvent* translated = new SEvent;
-                       memset(translated,0,sizeof(SEvent));
-                       translated->EventType               = EET_MOUSE_INPUT_EVENT;
-                       translated->MouseInput.X            = m_move_downlocation.X;
-                       translated->MouseInput.Y            = m_move_downlocation.Y;
-                       translated->MouseInput.Shift        = false;
-                       translated->MouseInput.Control      = false;
-                       translated->MouseInput.ButtonStates = 0;
-                       translated->MouseInput.Event        = EMIE_LMOUSE_LEFT_UP;
-                       m_receiver->OnEvent(*translated);
-                       delete translated;
-               }
-               else {
-                       /* do double tap detection */
-                       doubleTapDetection();
-               }
-       }
-       else {
-               infostream
-                       << "TouchScreenGUI::translateEvent released unknown button: "
-                       << evt_id << std::endl;
-       }
-
-       for (std::vector<id_status>::iterator iter = m_known_ids.begin();
-                       iter != m_known_ids.end(); ++iter) {
-               if (iter->id == evt_id) {
-                       m_known_ids.erase(iter);
-                       break;
-               }
-       }
-}
-
-void TouchScreenGUI::translateEvent(const SEvent &event)
-{
-       if (!m_visible) {
-               infostream << "TouchScreenGUI::translateEvent got event but not visible?!" << std::endl;
-               return;
-       }
-
-       if (event.EventType != EET_TOUCH_INPUT_EVENT) {
-               return;
-       }
-
-       if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {
-
-               /* add to own copy of eventlist ...
-                * android would provide this information but irrlicht guys don't
-                * wanna design a efficient interface
-                */
-               id_status toadd;
-               toadd.id = event.TouchInput.ID;
-               toadd.X  = event.TouchInput.X;
-               toadd.Y  = event.TouchInput.Y;
-               m_known_ids.push_back(toadd);
-
-               int eventID = event.TouchInput.ID;
-
-               touch_gui_button_id button =
-                               getButtonID(event.TouchInput.X, event.TouchInput.Y);
-
-               /* handle button events */
-               if (button != after_last_element_id) {
-                       handleButtonEvent(button, eventID, true);
-                       m_settingsbar.deactivate();
-                       m_rarecontrolsbar.deactivate();
-               } else if (isHUDButton(event)) {
-                       m_settingsbar.deactivate();
-                       m_rarecontrolsbar.deactivate();
-                       /* already handled in isHUDButton() */
-               } else if (m_settingsbar.isButton(event)) {
-                       m_rarecontrolsbar.deactivate();
-                       /* already handled in isSettingsBarButton() */
-               } else if (m_rarecontrolsbar.isButton(event)) {
-                       m_settingsbar.deactivate();
-                       /* already handled in isSettingsBarButton() */
-               }
-               /* handle non button events */
-               else {
-                       m_settingsbar.deactivate();
-                       m_rarecontrolsbar.deactivate();
-                       /* if we don't already have a moving point make this the moving one */
-                       if (m_move_id == -1) {
-                               m_move_id                  = event.TouchInput.ID;
-                               m_move_has_really_moved    = false;
-                               m_move_downtime            = porting::getTimeMs();
-                               m_move_downlocation        = v2s32(event.TouchInput.X, event.TouchInput.Y);
-                               m_move_sent_as_mouse_event = false;
-                       }
-               }
-
-               m_pointerpos[event.TouchInput.ID] = v2s32(event.TouchInput.X, event.TouchInput.Y);
-       }
-       else if (event.TouchInput.Event == ETIE_LEFT_UP) {
-               verbosestream << "Up event for pointerid: " << event.TouchInput.ID << std::endl;
-               handleReleaseEvent(event.TouchInput.ID);
-       }
-       else {
-               assert(event.TouchInput.Event == ETIE_MOVED);
-               int move_idx = event.TouchInput.ID;
-
-               if (m_pointerpos[event.TouchInput.ID] ==
-                               v2s32(event.TouchInput.X, event.TouchInput.Y)) {
-                       return;
-               }
-
-               if (m_move_id != -1) {
-                       if ((event.TouchInput.ID == m_move_id) &&
-                               (!m_move_sent_as_mouse_event)) {
-
-                               double distance = sqrt(
-                                               (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) *
-                                               (m_pointerpos[event.TouchInput.ID].X - event.TouchInput.X) +
-                                               (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y) *
-                                               (m_pointerpos[event.TouchInput.ID].Y - event.TouchInput.Y));
-
-                               if ((distance > g_settings->getU16("touchscreen_threshold")) ||
-                                               (m_move_has_really_moved)) {
-                                       m_move_has_really_moved = true;
-                                       s32 X = event.TouchInput.X;
-                                       s32 Y = event.TouchInput.Y;
-
-                                       // update camera_yaw and camera_pitch
-                                       s32 dx = X - m_pointerpos[event.TouchInput.ID].X;
-                                       s32 dy = Y - m_pointerpos[event.TouchInput.ID].Y;
-
-                                       /* adapt to similar behaviour as pc screen */
-                                       double d         = g_settings->getFloat("mouse_sensitivity") *4;
-                                       double old_yaw   = m_camera_yaw_change;
-                                       double old_pitch = m_camera_pitch;
-
-                                       m_camera_yaw_change -= dx * d;
-                                       m_camera_pitch = MYMIN(MYMAX(m_camera_pitch + (dy * d), -180), 180);
-
-                                       // update shootline
-                                       m_shootline = m_device
-                                                       ->getSceneManager()
-                                                       ->getSceneCollisionManager()
-                                                       ->getRayFromScreenCoordinates(v2s32(X, Y));
-                                       m_pointerpos[event.TouchInput.ID] = v2s32(X, Y);
-                               }
-                       }
-                       else if ((event.TouchInput.ID == m_move_id) &&
-                                       (m_move_sent_as_mouse_event)) {
-                               m_shootline = m_device
-                                               ->getSceneManager()
-                                               ->getSceneCollisionManager()
-                                               ->getRayFromScreenCoordinates(
-                                                               v2s32(event.TouchInput.X,event.TouchInput.Y));
-                       }
-               } else {
-                       handleChangedButton(event);
-               }
-       }
-}
-
-void TouchScreenGUI::handleChangedButton(const SEvent &event)
-{
-       for (unsigned int i = 0; i < after_last_element_id; i++) {
-
-               if (m_buttons[i].ids.empty()) {
-                       continue;
-               }
-               for (std::vector<int>::iterator iter = m_buttons[i].ids.begin();
-                               iter != m_buttons[i].ids.end(); ++iter) {
-
-                       if (event.TouchInput.ID == *iter) {
-
-                               int current_button_id =
-                                               getButtonID(event.TouchInput.X, event.TouchInput.Y);
-
-                               if (current_button_id == i) {
-                                       continue;
-                               }
-
-                               /* remove old button */
-                               handleButtonEvent((touch_gui_button_id) i,*iter,false);
-
-                               if (current_button_id == after_last_element_id) {
-                                       return;
-                               }
-                               handleButtonEvent((touch_gui_button_id) current_button_id,*iter,true);
-                               return;
-
-                       }
-               }
-       }
-
-       int current_button_id = getButtonID(event.TouchInput.X, event.TouchInput.Y);
-
-       if (current_button_id == after_last_element_id) {
-               return;
-       }
-
-       button_info* btn = &m_buttons[current_button_id];
-       if (std::find(btn->ids.begin(),btn->ids.end(), event.TouchInput.ID)
-                       == btn->ids.end())
-       {
-               handleButtonEvent((touch_gui_button_id) current_button_id,
-                               event.TouchInput.ID, true);
-       }
-
-}
-
-bool TouchScreenGUI::doubleTapDetection()
-{
-       m_key_events[0].down_time = m_key_events[1].down_time;
-       m_key_events[0].x         = m_key_events[1].x;
-       m_key_events[0].y         = m_key_events[1].y;
-       m_key_events[1].down_time = m_move_downtime;
-       m_key_events[1].x         = m_move_downlocation.X;
-       m_key_events[1].y         = m_move_downlocation.Y;
-
-       u64 delta = porting::getDeltaMs(m_key_events[0].down_time, porting::getTimeMs());
-       if (delta > 400)
-               return false;
-
-       double distance = sqrt(
-                       (m_key_events[0].x - m_key_events[1].x) * (m_key_events[0].x - m_key_events[1].x) +
-                       (m_key_events[0].y - m_key_events[1].y) * (m_key_events[0].y - m_key_events[1].y));
-
-
-       if (distance > (20 + g_settings->getU16("touchscreen_threshold")))
-               return false;
-
-       SEvent* translated = new SEvent();
-       memset(translated, 0, sizeof(SEvent));
-       translated->EventType               = EET_MOUSE_INPUT_EVENT;
-       translated->MouseInput.X            = m_key_events[0].x;
-       translated->MouseInput.Y            = m_key_events[0].y;
-       translated->MouseInput.Shift        = false;
-       translated->MouseInput.Control      = false;
-       translated->MouseInput.ButtonStates = EMBSM_RIGHT;
-
-       // update shootline
-       m_shootline = m_device
-                       ->getSceneManager()
-                       ->getSceneCollisionManager()
-                       ->getRayFromScreenCoordinates(v2s32(m_key_events[0].x, m_key_events[0].y));
-
-       translated->MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
-       verbosestream << "TouchScreenGUI::translateEvent right click press" << std::endl;
-       m_receiver->OnEvent(*translated);
-
-       translated->MouseInput.ButtonStates = 0;
-       translated->MouseInput.Event        = EMIE_RMOUSE_LEFT_UP;
-       verbosestream << "TouchScreenGUI::translateEvent right click release" << std::endl;
-       m_receiver->OnEvent(*translated);
-       delete translated;
-       return true;
-
-}
-
-TouchScreenGUI::~TouchScreenGUI()
-{
-       for (unsigned int i = 0; i < after_last_element_id; i++) {
-               button_info* btn = &m_buttons[i];
-               if (btn->guibutton != 0) {
-                       btn->guibutton->drop();
-                       btn->guibutton = NULL;
-               }
-       }
-}
-
-void TouchScreenGUI::step(float dtime)
-{
-       /* simulate keyboard repeats */
-       for (unsigned int i = 0; i < after_last_element_id; i++) {
-               button_info* btn = &m_buttons[i];
-
-               if (btn->ids.size() > 0) {
-                       btn->repeatcounter += dtime;
-
-                       /* in case we're moving around digging does not happen */
-                       if (m_move_id != -1)
-                               m_move_has_really_moved = true;
-
-                       if (btn->repeatcounter < btn->repeatdelay) continue;
-
-                       btn->repeatcounter              = 0;
-                       SEvent translated;
-                       memset(&translated, 0, sizeof(SEvent));
-                       translated.EventType            = irr::EET_KEY_INPUT_EVENT;
-                       translated.KeyInput.Key         = btn->keycode;
-                       translated.KeyInput.PressedDown = false;
-                       m_receiver->OnEvent(translated);
-
-                       translated.KeyInput.PressedDown = true;
-                       m_receiver->OnEvent(translated);
-               }
-       }
-
-       /* if a new placed pointer isn't moved for some time start digging */
-       if ((m_move_id != -1) &&
-                       (!m_move_has_really_moved) &&
-                       (!m_move_sent_as_mouse_event)) {
-
-               u64 delta = porting::getDeltaMs(m_move_downtime, porting::getTimeMs());
-
-               if (delta > MIN_DIG_TIME_MS) {
-                       m_shootline = m_device
-                                       ->getSceneManager()
-                                       ->getSceneCollisionManager()
-                                       ->getRayFromScreenCoordinates(
-                                                       v2s32(m_move_downlocation.X,m_move_downlocation.Y));
-
-                       SEvent translated;
-                       memset(&translated, 0, sizeof(SEvent));
-                       translated.EventType               = EET_MOUSE_INPUT_EVENT;
-                       translated.MouseInput.X            = m_move_downlocation.X;
-                       translated.MouseInput.Y            = m_move_downlocation.Y;
-                       translated.MouseInput.Shift        = false;
-                       translated.MouseInput.Control      = false;
-                       translated.MouseInput.ButtonStates = EMBSM_LEFT;
-                       translated.MouseInput.Event        = EMIE_LMOUSE_PRESSED_DOWN;
-                       verbosestream << "TouchScreenGUI::step left click press" << std::endl;
-                       m_receiver->OnEvent(translated);
-                       m_move_sent_as_mouse_event         = true;
-               }
-       }
-
-       m_settingsbar.step(dtime);
-       m_rarecontrolsbar.step(dtime);
-}
-
-void TouchScreenGUI::resetHud()
-{
-       m_hud_rects.clear();
-}
-
-void TouchScreenGUI::registerHudItem(int index, const rect<s32> &rect)
-{
-       m_hud_rects[index] = rect;
-}
-
-void TouchScreenGUI::Toggle(bool visible)
-{
-       m_visible = visible;
-       for (unsigned int i = 0; i < after_last_element_id; i++) {
-               button_info* btn = &m_buttons[i];
-               if (btn->guibutton != 0) {
-                       btn->guibutton->setVisible(visible);
-               }
-       }
-
-       /* clear all active buttons */
-       if (!visible) {
-               while (m_known_ids.size() > 0) {
-                       handleReleaseEvent(m_known_ids.begin()->id);
-               }
-
-               m_settingsbar.hide();
-               m_rarecontrolsbar.hide();
-       } else {
-               m_settingsbar.show();
-               m_rarecontrolsbar.show();
-       }
-}
-
-void TouchScreenGUI::hide()
-{
-       if (!m_visible)
-               return;
-
-       Toggle(false);
-}
-
-void TouchScreenGUI::show()
-{
-       if (m_visible)
-               return;
-
-       Toggle(true);
-}
diff --git a/src/touchscreengui.h b/src/touchscreengui.h
deleted file mode 100644 (file)
index da97381..0000000
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
-Copyright (C) 2014 sapier
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include <IEventReceiver.h>
-#include <IGUIButton.h>
-#include <IGUIEnvironment.h>
-
-#include <map>
-#include <vector>
-
-#include "client/tile.h"
-#include "game.h"
-
-using namespace irr;
-using namespace irr::core;
-using namespace irr::gui;
-
-typedef enum {
-       forward_id = 0,
-       backward_id,
-       left_id,
-       right_id,
-       jump_id,
-       crunch_id,
-       after_last_element_id,
-       settings_starter_id,
-       rare_controls_starter_id,
-       fly_id,
-       noclip_id,
-       fast_id,
-       debug_id,
-       camera_id,
-       range_id,
-       chat_id,
-       inventory_id,
-       drop_id
-} touch_gui_button_id;
-
-typedef enum {
-       AHBB_Dir_Top_Bottom,
-       AHBB_Dir_Bottom_Top,
-       AHBB_Dir_Left_Right,
-       AHBB_Dir_Right_Left
-} autohide_button_bar_dir;
-
-#define MIN_DIG_TIME_MS 500
-#define MAX_TOUCH_COUNT 64
-#define BUTTON_REPEAT_DELAY 0.2f
-
-#define SETTINGS_BAR_Y_OFFSET 6.5
-#define RARE_CONTROLS_BAR_Y_OFFSET 4
-
-extern const char **touchgui_button_imagenames;
-
-struct button_info
-{
-       float repeatcounter;
-       float repeatdelay;
-       irr::EKEY_CODE keycode;
-       std::vector<int> ids;
-       IGUIButton *guibutton = nullptr;
-       bool immediate_release;
-};
-
-class AutoHideButtonBar
-{
-public:
-       AutoHideButtonBar(IrrlichtDevice *device, IEventReceiver *receiver);
-
-       void init(ISimpleTextureSource *tsrc, const char *starter_img, int button_id,
-                       v2s32 UpperLeft, v2s32 LowerRight, autohide_button_bar_dir dir,
-                       float timeout);
-
-       ~AutoHideButtonBar();
-
-       /* add button to be shown */
-       void addButton(touch_gui_button_id id, const wchar_t *caption,
-                       const char *btn_image);
-
-       /* detect settings bar button events */
-       bool isButton(const SEvent &event);
-
-       /* handle released hud buttons */
-       bool isReleaseButton(int eventID);
-
-       /* step handler */
-       void step(float dtime);
-
-       /* deactivate button bar */
-       void deactivate();
-
-       /* hide the whole buttonbar */
-       void hide();
-
-       /* unhide the buttonbar */
-       void show();
-
-private:
-       ISimpleTextureSource *m_texturesource = nullptr;
-       irr::video::IVideoDriver *m_driver;
-       IGUIEnvironment *m_guienv;
-       IEventReceiver *m_receiver;
-       button_info m_starter;
-       std::vector<button_info *> m_buttons;
-
-       v2s32 m_upper_left;
-       v2s32 m_lower_right;
-
-       /* show settings bar */
-       bool m_active = false;
-
-       bool m_visible = true;
-
-       /* settings bar timeout */
-       float m_timeout = 0.0f;
-       float m_timeout_value = 3.0f;
-       bool m_initialized = false;
-       autohide_button_bar_dir m_dir = AHBB_Dir_Right_Left;
-};
-
-class TouchScreenGUI
-{
-public:
-       TouchScreenGUI(IrrlichtDevice *device, IEventReceiver *receiver);
-       ~TouchScreenGUI();
-
-       void translateEvent(const SEvent &event);
-
-       void init(ISimpleTextureSource *tsrc);
-
-       double getYawChange()
-       {
-               double res = m_camera_yaw_change;
-               m_camera_yaw_change = 0;
-               return res;
-       }
-
-       double getPitch() { return m_camera_pitch; }
-
-       /*!
-        * Returns a line which describes what the player is pointing at.
-        * The starting point and looking direction are significant,
-        * the line should be scaled to match its length to the actual distance
-        * the player can reach.
-        * The line starts at the camera and ends on the camera's far plane.
-        * The coordinates do not contain the camera offset.
-        */
-       line3d<f32> getShootline() { return m_shootline; }
-
-       void step(float dtime);
-       void resetHud();
-       void registerHudItem(int index, const rect<s32> &rect);
-       void Toggle(bool visible);
-
-       void hide();
-       void show();
-
-private:
-       IrrlichtDevice *m_device;
-       IGUIEnvironment *m_guienv;
-       IEventReceiver *m_receiver;
-       ISimpleTextureSource *m_texturesource;
-       v2u32 m_screensize;
-       std::map<int, rect<s32>> m_hud_rects;
-       std::map<int, irr::EKEY_CODE> m_hud_ids;
-       bool m_visible; // is the gui visible
-
-       /* value in degree */
-       double m_camera_yaw_change = 0.0;
-       double m_camera_pitch = 0.0;
-
-       /*!
-        * A line starting at the camera and pointing towards the
-        * selected object.
-        * The line ends on the camera's far plane.
-        * The coordinates do not contain the camera offset.
-        */
-       line3d<f32> m_shootline;
-
-       int m_move_id = -1;
-       bool m_move_has_really_moved = false;
-       s64 m_move_downtime = 0;
-       bool m_move_sent_as_mouse_event = false;
-       v2s32 m_move_downlocation = v2s32(-10000, -10000);
-
-       button_info m_buttons[after_last_element_id];
-
-       /* gui button detection */
-       touch_gui_button_id getButtonID(s32 x, s32 y);
-
-       /* gui button by eventID */
-       touch_gui_button_id getButtonID(int eventID);
-
-       /* check if a button has changed */
-       void handleChangedButton(const SEvent &event);
-
-       /* initialize a button */
-       void initButton(touch_gui_button_id id, rect<s32> button_rect,
-                       std::wstring caption, bool immediate_release,
-                       float repeat_delay = BUTTON_REPEAT_DELAY);
-
-       struct id_status
-       {
-               int id;
-               int X;
-               int Y;
-       };
-
-       /* vector to store known ids and their initial touch positions*/
-       std::vector<id_status> m_known_ids;
-
-       /* handle a button event */
-       void handleButtonEvent(touch_gui_button_id bID, int eventID, bool action);
-
-       /* handle pressed hud buttons */
-       bool isHUDButton(const SEvent &event);
-
-       /* handle released hud buttons */
-       bool isReleaseHUDButton(int eventID);
-
-       /* handle double taps */
-       bool doubleTapDetection();
-
-       /* handle release event */
-       void handleReleaseEvent(int evt_id);
-
-       /* get size of regular gui control button */
-       int getGuiButtonSize();
-
-       /* doubleclick detection variables */
-       struct key_event
-       {
-               unsigned int down_time;
-               s32 x;
-               s32 y;
-       };
-
-       /* array for saving last known position of a pointer */
-       v2s32 m_pointerpos[MAX_TOUCH_COUNT];
-
-       /* array for doubletap detection */
-       key_event m_key_events[2];
-
-       /* settings bar */
-       AutoHideButtonBar m_settingsbar;
-
-       /* rare controls bar */
-       AutoHideButtonBar m_rarecontrolsbar;
-};
-extern TouchScreenGUI *g_touchscreengui;
diff --git a/src/treegen.cpp b/src/treegen.cpp
deleted file mode 100644 (file)
index 9e11b1a..0000000
+++ /dev/null
@@ -1,872 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>,
-                         2012-2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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 "irr_v3d.h"
-#include <stack>
-#include "util/pointer.h"
-#include "util/numeric.h"
-#include "map.h"
-#include "mapblock.h"
-#include "serverenvironment.h"
-#include "nodedef.h"
-#include "treegen.h"
-#include "voxelalgorithms.h"
-
-namespace treegen
-{
-
-void make_tree(MMVManip &vmanip, v3s16 p0,
-               bool is_apple_tree, INodeDefManager *ndef, s32 seed)
-{
-       /*
-               NOTE: Tree-placing code is currently duplicated in the engine
-               and in games that have saplings; both are deprecated but not
-               replaced yet
-       */
-       MapNode treenode(ndef->getId("mapgen_tree"));
-       MapNode leavesnode(ndef->getId("mapgen_leaves"));
-       MapNode applenode(ndef->getId("mapgen_apple"));
-
-       PseudoRandom pr(seed);
-       s16 trunk_h = pr.range(4, 5);
-       v3s16 p1 = p0;
-       for (s16 ii = 0; ii < trunk_h; ii++) {
-               if (vmanip.m_area.contains(p1)) {
-                       u32 vi = vmanip.m_area.index(p1);
-                       vmanip.m_data[vi] = treenode;
-               }
-               p1.Y++;
-       }
-
-       // p1 is now the last piece of the trunk
-       p1.Y -= 1;
-
-       VoxelArea leaves_a(v3s16(-2, -1, -2), v3s16(2, 2, 2));
-       Buffer<u8> leaves_d(leaves_a.getVolume());
-       for (s32 i = 0; i < leaves_a.getVolume(); i++)
-               leaves_d[i] = 0;
-
-       // Force leaves at near the end of the trunk
-       s16 d = 1;
-       for (s16 z = -d; z <= d; z++)
-       for (s16 y = -d; y <= d; y++)
-       for (s16 x = -d; x <= d; x++) {
-               leaves_d[leaves_a.index(v3s16(x, y, z))] = 1;
-       }
-
-       // Add leaves randomly
-       for (u32 iii = 0; iii < 7; iii++) {
-               v3s16 p(
-                       pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X - d),
-                       pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y - d),
-                       pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z - d)
-               );
-
-               for (s16 z = 0; z <= d; z++)
-               for (s16 y = 0; y <= d; y++)
-               for (s16 x = 0; x <= d; x++) {
-                       leaves_d[leaves_a.index(p + v3s16(x, y, z))] = 1;
-               }
-       }
-
-       // Blit leaves to vmanip
-       for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
-       for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
-               v3s16 pmin(leaves_a.MinEdge.X, y, z);
-               u32 i = leaves_a.index(pmin);
-               u32 vi = vmanip.m_area.index(pmin + p1);
-               for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
-                       v3s16 p(x, y, z);
-                       if (vmanip.m_area.contains(p + p1) &&
-                                       (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
-                                       vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
-                               if (leaves_d[i] == 1) {
-                                       bool is_apple = pr.range(0, 99) < 10;
-                                       if (is_apple_tree && is_apple)
-                                               vmanip.m_data[vi] = applenode;
-                                       else
-                                               vmanip.m_data[vi] = leavesnode;
-                               }
-                       }
-                       vi++;
-                       i++;
-               }
-       }
-}
-
-
-// L-System tree LUA spawner
-treegen::error spawn_ltree(ServerEnvironment *env, v3s16 p0,
-               INodeDefManager *ndef, const TreeDef &tree_definition)
-{
-       ServerMap *map = &env->getServerMap();
-       std::map<v3s16, MapBlock*> modified_blocks;
-       MMVManip vmanip(map);
-       v3s16 tree_blockp = getNodeBlockPos(p0);
-       treegen::error e;
-
-       vmanip.initialEmerge(tree_blockp - v3s16(1, 1, 1), tree_blockp + v3s16(1, 3, 1));
-       e = make_ltree(vmanip, p0, ndef, tree_definition);
-       if (e != SUCCESS)
-               return e;
-
-       voxalgo::blit_back_with_light(map, &vmanip, &modified_blocks);
-
-       // Send a MEET_OTHER event
-       MapEditEvent event;
-       event.type = MEET_OTHER;
-       for (auto &modified_block : modified_blocks)
-               event.modified_blocks.insert(modified_block.first);
-       map->dispatchEvent(&event);
-       return SUCCESS;
-}
-
-
-//L-System tree generator
-treegen::error make_ltree(MMVManip &vmanip, v3s16 p0,
-               INodeDefManager *ndef, TreeDef tree_definition)
-{
-       MapNode dirtnode(ndef->getId("mapgen_dirt"));
-       s32 seed;
-       if (tree_definition.explicit_seed)
-               seed = tree_definition.seed + 14002;
-       else
-               seed = p0.X * 2 + p0.Y * 4 + p0.Z;  // use the tree position to seed PRNG
-       PseudoRandom ps(seed);
-
-       // chance of inserting abcd rules
-       double prop_a = 9;
-       double prop_b = 8;
-       double prop_c = 7;
-       double prop_d = 6;
-
-       //randomize tree growth level, minimum=2
-       s16 iterations = tree_definition.iterations;
-       if (tree_definition.iterations_random_level > 0)
-               iterations -= ps.range(0, tree_definition.iterations_random_level);
-       if (iterations < 2)
-               iterations = 2;
-
-       s16 MAX_ANGLE_OFFSET = 5;
-       double angle_in_radians = (double)tree_definition.angle * M_PI / 180;
-       double angleOffset_in_radians = (s16)(ps.range(0, 1) % MAX_ANGLE_OFFSET) * M_PI / 180;
-
-       //initialize rotation matrix, position and stacks for branches
-       core::matrix4 rotation;
-       rotation = setRotationAxisRadians(rotation, M_PI / 2, v3f(0, 0, 1));
-       v3f position;
-       position.X = p0.X;
-       position.Y = p0.Y;
-       position.Z = p0.Z;
-       std::stack <core::matrix4> stack_orientation;
-       std::stack <v3f> stack_position;
-
-       //generate axiom
-       std::string axiom = tree_definition.initial_axiom;
-       for (s16 i = 0; i < iterations; i++) {
-               std::string temp;
-               for (s16 j = 0; j < (s16)axiom.size(); j++) {
-                       char axiom_char = axiom.at(j);
-                       switch (axiom_char) {
-                       case 'A':
-                               temp += tree_definition.rules_a;
-                               break;
-                       case 'B':
-                               temp += tree_definition.rules_b;
-                               break;
-                       case 'C':
-                               temp += tree_definition.rules_c;
-                               break;
-                       case 'D':
-                               temp += tree_definition.rules_d;
-                               break;
-                       case 'a':
-                               if (prop_a >= ps.range(1, 10))
-                                       temp += tree_definition.rules_a;
-                               break;
-                       case 'b':
-                               if (prop_b >= ps.range(1, 10))
-                                       temp += tree_definition.rules_b;
-                               break;
-                       case 'c':
-                               if (prop_c >= ps.range(1, 10))
-                                       temp += tree_definition.rules_c;
-                               break;
-                       case 'd':
-                               if (prop_d >= ps.range(1, 10))
-                                       temp += tree_definition.rules_d;
-                               break;
-                       default:
-                               temp += axiom_char;
-                               break;
-                       }
-               }
-               axiom = temp;
-       }
-
-       //make sure tree is not floating in the air
-       if (tree_definition.trunk_type == "double") {
-               tree_node_placement(
-                       vmanip,
-                       v3f(position.X + 1, position.Y - 1, position.Z),
-                       dirtnode
-               );
-               tree_node_placement(
-                       vmanip,
-                       v3f(position.X, position.Y - 1, position.Z + 1),
-                       dirtnode
-               );
-               tree_node_placement(
-                       vmanip,
-                       v3f(position.X + 1, position.Y - 1, position.Z + 1),
-                       dirtnode
-               );
-       } else if (tree_definition.trunk_type == "crossed") {
-               tree_node_placement(
-                       vmanip,
-                       v3f(position.X + 1, position.Y - 1, position.Z),
-                       dirtnode
-               );
-               tree_node_placement(
-                       vmanip,
-                       v3f(position.X - 1, position.Y - 1, position.Z),
-                       dirtnode
-               );
-               tree_node_placement(
-                       vmanip,
-                       v3f(position.X, position.Y - 1, position.Z + 1),
-                       dirtnode
-               );
-               tree_node_placement(
-                       vmanip,
-                       v3f(position.X, position.Y - 1, position.Z - 1),
-                       dirtnode
-               );
-       }
-
-       /* build tree out of generated axiom
-
-       Key for Special L-System Symbols used in Axioms
-
-    G  - move forward one unit with the pen up
-    F  - move forward one unit with the pen down drawing trunks and branches
-    f  - move forward one unit with the pen down drawing leaves (100% chance)
-    T  - move forward one unit with the pen down drawing trunks only
-    R  - move forward one unit with the pen down placing fruit
-    A  - replace with rules set A
-    B  - replace with rules set B
-    C  - replace with rules set C
-    D  - replace with rules set D
-    a  - replace with rules set A, chance 90%
-    b  - replace with rules set B, chance 80%
-    c  - replace with rules set C, chance 70%
-    d  - replace with rules set D, chance 60%
-    +  - yaw the turtle right by angle degrees
-    -  - yaw the turtle left by angle degrees
-    &  - pitch the turtle down by angle degrees
-    ^  - pitch the turtle up by angle degrees
-    /  - roll the turtle to the right by angle degrees
-    *  - roll the turtle to the left by angle degrees
-    [  - save in stack current state info
-    ]  - recover from stack state info
-
-    */
-
-       s16 x,y,z;
-       for (s16 i = 0; i < (s16)axiom.size(); i++) {
-               char axiom_char = axiom.at(i);
-               core::matrix4 temp_rotation;
-               temp_rotation.makeIdentity();
-               v3f dir;
-               switch (axiom_char) {
-               case 'G':
-                       dir = v3f(1, 0, 0);
-                       dir = transposeMatrix(rotation, dir);
-                       position += dir;
-                       break;
-               case 'T':
-                       tree_trunk_placement(
-                               vmanip,
-                               v3f(position.X, position.Y, position.Z),
-                               tree_definition
-                       );
-                       if (tree_definition.trunk_type == "double" &&
-                                       !tree_definition.thin_branches) {
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X + 1, position.Y, position.Z),
-                                       tree_definition
-                               );
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X, position.Y, position.Z + 1),
-                                       tree_definition
-                               );
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X + 1, position.Y, position.Z + 1),
-                                       tree_definition
-                               );
-                       } else if (tree_definition.trunk_type == "crossed" &&
-                                       !tree_definition.thin_branches) {
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X + 1, position.Y, position.Z),
-                                       tree_definition
-                               );
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X - 1, position.Y, position.Z),
-                                       tree_definition
-                               );
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X, position.Y, position.Z + 1),
-                                       tree_definition
-                               );
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X, position.Y, position.Z - 1),
-                                       tree_definition
-                               );
-                       }
-                       dir = v3f(1, 0, 0);
-                       dir = transposeMatrix(rotation, dir);
-                       position += dir;
-                       break;
-               case 'F':
-                       tree_trunk_placement(
-                               vmanip,
-                               v3f(position.X, position.Y, position.Z),
-                               tree_definition
-                       );
-                       if ((stack_orientation.empty() &&
-                                       tree_definition.trunk_type == "double") ||
-                                       (!stack_orientation.empty() &&
-                                       tree_definition.trunk_type == "double" &&
-                                       !tree_definition.thin_branches)) {
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X +1 , position.Y, position.Z),
-                                       tree_definition
-                               );
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X, position.Y, position.Z + 1),
-                                       tree_definition
-                               );
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X + 1, position.Y, position.Z + 1),
-                                       tree_definition
-                               );
-                       } else if ((stack_orientation.empty() &&
-                                       tree_definition.trunk_type == "crossed") ||
-                                       (!stack_orientation.empty() &&
-                                       tree_definition.trunk_type == "crossed" &&
-                                       !tree_definition.thin_branches)) {
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X + 1, position.Y, position.Z),
-                                       tree_definition
-                               );
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X - 1, position.Y, position.Z),
-                                       tree_definition
-                               );
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X, position.Y, position.Z + 1),
-                                       tree_definition
-                               );
-                               tree_trunk_placement(
-                                       vmanip,
-                                       v3f(position.X, position.Y, position.Z - 1),
-                                       tree_definition
-                               );
-                       } if (!stack_orientation.empty()) {
-                               s16 size = 1;
-                               for (x = -size; x <= size; x++)
-                               for (y = -size; y <= size; y++)
-                               for (z = -size; z <= size; z++) {
-                                       if (abs(x) == size &&
-                                                       abs(y) == size &&
-                                                       abs(z) == size) {
-                                               tree_leaves_placement(
-                                                       vmanip,
-                                                       v3f(position.X + x + 1, position.Y + y,
-                                                                       position.Z + z),
-                                                       ps.next(),
-                                                       tree_definition
-                                               );
-                                               tree_leaves_placement(
-                                                       vmanip,
-                                                       v3f(position.X + x - 1, position.Y + y,
-                                                                       position.Z + z),
-                                                       ps.next(),
-                                                       tree_definition
-                                               );
-                                               tree_leaves_placement(
-                                                       vmanip,v3f(position.X + x, position.Y + y,
-                                                                       position.Z + z + 1),
-                                                       ps.next(),
-                                                       tree_definition
-                                               );
-                                               tree_leaves_placement(
-                                                       vmanip,v3f(position.X + x, position.Y + y,
-                                                                       position.Z + z - 1),
-                                                       ps.next(),
-                                                       tree_definition
-                                               );
-                                       }
-                               }
-                       }
-                       dir = v3f(1, 0, 0);
-                       dir = transposeMatrix(rotation, dir);
-                       position += dir;
-                       break;
-               case 'f':
-                       tree_single_leaves_placement(
-                               vmanip,
-                               v3f(position.X, position.Y, position.Z),
-                               ps.next(),
-                               tree_definition
-                       );
-                       dir = v3f(1, 0, 0);
-                       dir = transposeMatrix(rotation, dir);
-                       position += dir;
-                       break;
-               case 'R':
-                       tree_fruit_placement(
-                               vmanip,
-                               v3f(position.X, position.Y, position.Z),
-                               tree_definition
-                       );
-                       dir = v3f(1, 0, 0);
-                       dir = transposeMatrix(rotation, dir);
-                       position += dir;
-                       break;
-
-               // turtle orientation commands
-               case '[':
-                       stack_orientation.push(rotation);
-                       stack_position.push(position);
-                       break;
-               case ']':
-                       if (stack_orientation.empty())
-                               return UNBALANCED_BRACKETS;
-                       rotation = stack_orientation.top();
-                       stack_orientation.pop();
-                       position = stack_position.top();
-                       stack_position.pop();
-                       break;
-               case '+':
-                       temp_rotation.makeIdentity();
-                       temp_rotation = setRotationAxisRadians(temp_rotation,
-                                       angle_in_radians + angleOffset_in_radians, v3f(0, 0, 1));
-                       rotation *= temp_rotation;
-                       break;
-               case '-':
-                       temp_rotation.makeIdentity();
-                       temp_rotation = setRotationAxisRadians(temp_rotation,
-                                       angle_in_radians + angleOffset_in_radians, v3f(0, 0, -1));
-                       rotation *= temp_rotation;
-                       break;
-               case '&':
-                       temp_rotation.makeIdentity();
-                       temp_rotation = setRotationAxisRadians(temp_rotation,
-                                       angle_in_radians + angleOffset_in_radians, v3f(0, 1, 0));
-                       rotation *= temp_rotation;
-                       break;
-               case '^':
-                       temp_rotation.makeIdentity();
-                       temp_rotation = setRotationAxisRadians(temp_rotation,
-                                       angle_in_radians + angleOffset_in_radians, v3f(0, -1, 0));
-                       rotation *= temp_rotation;
-                       break;
-               case '*':
-                       temp_rotation.makeIdentity();
-                       temp_rotation = setRotationAxisRadians(temp_rotation,
-                                       angle_in_radians, v3f(1, 0, 0));
-                       rotation *= temp_rotation;
-                       break;
-               case '/':
-                       temp_rotation.makeIdentity();
-                       temp_rotation = setRotationAxisRadians(temp_rotation,
-                                       angle_in_radians, v3f(-1, 0, 0));
-                       rotation *= temp_rotation;
-                       break;
-               default:
-                       break;
-               }
-       }
-
-       return SUCCESS;
-}
-
-
-void tree_node_placement(MMVManip &vmanip, v3f p0, MapNode node)
-{
-       v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
-       if (!vmanip.m_area.contains(p1))
-               return;
-       u32 vi = vmanip.m_area.index(p1);
-       if (vmanip.m_data[vi].getContent() != CONTENT_AIR
-                       && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
-               return;
-       vmanip.m_data[vmanip.m_area.index(p1)] = node;
-}
-
-
-void tree_trunk_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
-{
-       v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
-       if (!vmanip.m_area.contains(p1))
-               return;
-       u32 vi = vmanip.m_area.index(p1);
-       content_t current_node = vmanip.m_data[vi].getContent();
-       if (current_node != CONTENT_AIR && current_node != CONTENT_IGNORE
-                       && current_node != tree_definition.leavesnode.getContent()
-                       && current_node != tree_definition.leaves2node.getContent()
-                       && current_node != tree_definition.fruitnode.getContent())
-               return;
-       vmanip.m_data[vi] = tree_definition.trunknode;
-}
-
-
-void tree_leaves_placement(MMVManip &vmanip, v3f p0,
-               PseudoRandom ps, TreeDef &tree_definition)
-{
-       MapNode leavesnode = tree_definition.leavesnode;
-       if (ps.range(1, 100) > 100 - tree_definition.leaves2_chance)
-               leavesnode = tree_definition.leaves2node;
-       v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
-       if (!vmanip.m_area.contains(p1))
-               return;
-       u32 vi = vmanip.m_area.index(p1);
-       if (vmanip.m_data[vi].getContent() != CONTENT_AIR
-                       && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
-               return;
-       if (tree_definition.fruit_chance > 0) {
-               if (ps.range(1, 100) > 100 - tree_definition.fruit_chance)
-                       vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
-               else
-                       vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
-       } else if (ps.range(1, 100) > 20) {
-               vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
-       }
-}
-
-
-void tree_single_leaves_placement(MMVManip &vmanip, v3f p0,
-               PseudoRandom ps, TreeDef &tree_definition)
-{
-       MapNode leavesnode = tree_definition.leavesnode;
-       if (ps.range(1, 100) > 100 - tree_definition.leaves2_chance)
-               leavesnode = tree_definition.leaves2node;
-       v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
-       if (!vmanip.m_area.contains(p1))
-               return;
-       u32 vi = vmanip.m_area.index(p1);
-       if (vmanip.m_data[vi].getContent() != CONTENT_AIR
-                       && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
-               return;
-       vmanip.m_data[vmanip.m_area.index(p1)] = leavesnode;
-}
-
-
-void tree_fruit_placement(MMVManip &vmanip, v3f p0, TreeDef &tree_definition)
-{
-       v3s16 p1 = v3s16(myround(p0.X), myround(p0.Y), myround(p0.Z));
-       if (!vmanip.m_area.contains(p1))
-               return;
-       u32 vi = vmanip.m_area.index(p1);
-       if (vmanip.m_data[vi].getContent() != CONTENT_AIR
-                       && vmanip.m_data[vi].getContent() != CONTENT_IGNORE)
-               return;
-       vmanip.m_data[vmanip.m_area.index(p1)] = tree_definition.fruitnode;
-}
-
-
-irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis)
-{
-       double c = cos(angle);
-       double s = sin(angle);
-       double t = 1.0 - c;
-
-       double tx  = t * axis.X;
-       double ty  = t * axis.Y;
-       double tz  = t * axis.Z;
-       double sx  = s * axis.X;
-       double sy  = s * axis.Y;
-       double sz  = s * axis.Z;
-
-       M[0] = tx * axis.X + c;
-       M[1] = tx * axis.Y + sz;
-       M[2] = tx * axis.Z - sy;
-
-       M[4] = ty * axis.X - sz;
-       M[5] = ty * axis.Y + c;
-       M[6] = ty * axis.Z + sx;
-
-       M[8]  = tz * axis.X + sy;
-       M[9]  = tz * axis.Y - sx;
-       M[10] = tz * axis.Z + c;
-       return M;
-}
-
-
-v3f transposeMatrix(irr::core::matrix4 M, v3f v)
-{
-       v3f translated;
-       double x = M[0] * v.X + M[4] * v.Y + M[8]  * v.Z +M[12];
-       double y = M[1] * v.X + M[5] * v.Y + M[9]  * v.Z +M[13];
-       double z = M[2] * v.X + M[6] * v.Y + M[10] * v.Z +M[14];
-       translated.X = x;
-       translated.Y = y;
-       translated.Z = z;
-       return translated;
-}
-
-
-void make_jungletree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 seed)
-{
-       /*
-               NOTE: Tree-placing code is currently duplicated in the engine
-               and in games that have saplings; both are deprecated but not
-               replaced yet
-       */
-       content_t c_tree   = ndef->getId("mapgen_jungletree");
-       content_t c_leaves = ndef->getId("mapgen_jungleleaves");
-       if (c_tree == CONTENT_IGNORE)
-               c_tree = ndef->getId("mapgen_tree");
-       if (c_leaves == CONTENT_IGNORE)
-               c_leaves = ndef->getId("mapgen_leaves");
-
-       MapNode treenode(c_tree);
-       MapNode leavesnode(c_leaves);
-
-       PseudoRandom pr(seed);
-       for (s16 x= -1; x <= 1; x++)
-       for (s16 z= -1; z <= 1; z++) {
-               if (pr.range(0, 2) == 0)
-                       continue;
-               v3s16 p1 = p0 + v3s16(x, 0, z);
-               v3s16 p2 = p0 + v3s16(x, -1, z);
-               u32 vi1 = vmanip.m_area.index(p1);
-               u32 vi2 = vmanip.m_area.index(p2);
-
-               if (vmanip.m_area.contains(p2) &&
-                               vmanip.m_data[vi2].getContent() == CONTENT_AIR)
-                       vmanip.m_data[vi2] = treenode;
-               else if (vmanip.m_area.contains(p1) &&
-                               vmanip.m_data[vi1].getContent() == CONTENT_AIR)
-                       vmanip.m_data[vi1] = treenode;
-       }
-       vmanip.m_data[vmanip.m_area.index(p0)] = treenode;
-
-       s16 trunk_h = pr.range(8, 12);
-       v3s16 p1 = p0;
-       for (s16 ii = 0; ii < trunk_h; ii++) {
-               if (vmanip.m_area.contains(p1)) {
-                       u32 vi = vmanip.m_area.index(p1);
-                       vmanip.m_data[vi] = treenode;
-               }
-               p1.Y++;
-       }
-
-       // p1 is now the last piece of the trunk
-       p1.Y -= 1;
-
-       VoxelArea leaves_a(v3s16(-3, -2, -3), v3s16(3, 2, 3));
-       //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++)
-               leaves_d[i] = 0;
-
-       // Force leaves at near the end of the trunk
-       s16 d = 1;
-       for (s16 z = -d; z <= d; z++)
-       for (s16 y = -d; y <= d; y++)
-       for (s16 x = -d; x <= d; x++) {
-               leaves_d[leaves_a.index(v3s16(x,y,z))] = 1;
-       }
-
-       // Add leaves randomly
-       for (u32 iii = 0; iii < 30; iii++) {
-               v3s16 p(
-                       pr.range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X - d),
-                       pr.range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y - d),
-                       pr.range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z - d)
-               );
-
-               for (s16 z = 0; z <= d; z++)
-               for (s16 y = 0; y <= d; y++)
-               for (s16 x = 0; x <= d; x++) {
-                       leaves_d[leaves_a.index(p + v3s16(x, y, z))] = 1;
-               }
-       }
-
-       // Blit leaves to vmanip
-       for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
-       for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
-               v3s16 pmin(leaves_a.MinEdge.X, y, z);
-               u32 i = leaves_a.index(pmin);
-               u32 vi = vmanip.m_area.index(pmin + p1);
-               for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
-                       v3s16 p(x, y, z);
-                       if (vmanip.m_area.contains(p + p1) &&
-                                       (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
-                                       vmanip.m_data[vi].getContent() == CONTENT_IGNORE)) {
-                               if (leaves_d[i] == 1)
-                                       vmanip.m_data[vi] = leavesnode;
-                       }
-                       vi++;
-                       i++;
-               }
-       }
-}
-
-
-void make_pine_tree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef, s32 seed)
-{
-       /*
-               NOTE: Tree-placing code is currently duplicated in the engine
-               and in games that have saplings; both are deprecated but not
-               replaced yet
-       */
-       content_t c_tree   = ndef->getId("mapgen_pine_tree");
-       content_t c_leaves = ndef->getId("mapgen_pine_needles");
-       content_t c_snow = ndef->getId("mapgen_snow");
-       if (c_tree == CONTENT_IGNORE)
-               c_tree = ndef->getId("mapgen_tree");
-       if (c_leaves == CONTENT_IGNORE)
-               c_leaves = ndef->getId("mapgen_leaves");
-       if (c_snow == CONTENT_IGNORE)
-               c_snow = CONTENT_AIR;
-
-       MapNode treenode(c_tree);
-       MapNode leavesnode(c_leaves);
-       MapNode snownode(c_snow);
-
-       PseudoRandom pr(seed);
-       u16 trunk_h = pr.range(9, 13);
-       v3s16 p1 = p0;
-       for (u16 ii = 0; ii < trunk_h; ii++) {
-               if (vmanip.m_area.contains(p1)) {
-                       u32 vi = vmanip.m_area.index(p1);
-                       vmanip.m_data[vi] = treenode;
-               }
-               p1.Y++;
-       }
-
-       // Make p1 the top node of the trunk
-       p1.Y -= 1;
-
-       VoxelArea leaves_a(v3s16(-3, -6, -3), v3s16(3, 3, 3));
-       Buffer<u8> leaves_d(leaves_a.getVolume());
-       for (s32 i = 0; i < leaves_a.getVolume(); i++)
-               leaves_d[i] = 0;
-
-       // Upper branches
-       u16 dev = 3;
-       for (s16 yy = -1; yy <= 1; yy++) {
-               for (s16 zz = -dev; zz <= dev; zz++) {
-                       u32 i = leaves_a.index(v3s16(-dev, yy, zz));
-                       u32 ia = leaves_a.index(v3s16(-dev, yy+1, zz));
-                       for (s16 xx = -dev; xx <= dev; xx++) {
-                               if (pr.range(0, 20) <= 19 - dev) {
-                                       leaves_d[i] = 1;
-                                       leaves_d[ia] = 2;
-                               }
-                               i++;
-                               ia++;
-                       }
-               }
-               dev--;
-       }
-
-       // Centre top nodes
-       leaves_d[leaves_a.index(v3s16(0, 1, 0))] = 1;
-       leaves_d[leaves_a.index(v3s16(0, 2, 0))] = 1;
-       leaves_d[leaves_a.index(v3s16(0, 3, 0))] = 2;
-
-       // Lower branches
-       s16 my = -6;
-       for (u32 iii = 0; iii < 20; iii++) {
-               s16 xi = pr.range(-3, 2);
-               s16 yy = pr.range(-6, -5);
-               s16 zi = pr.range(-3, 2);
-               if (yy > my)
-                       my = yy;
-               for (s16 zz = zi; zz <= zi + 1; zz++) {
-                       u32 i = leaves_a.index(v3s16(xi, yy, zz));
-                       u32 ia = leaves_a.index(v3s16(xi, yy + 1, zz));
-                       for (s32 xx = xi; xx <= xi + 1; xx++) {
-                               leaves_d[i] = 1;
-                               if (leaves_d[ia] == 0)
-                                       leaves_d[ia] = 2;
-                               i++;
-                               ia++;
-                       }
-               }
-       }
-
-       dev = 2;
-       for (s16 yy = my + 1; yy <= my + 2; yy++) {
-               for (s16 zz = -dev; zz <= dev; zz++) {
-                       u32 i = leaves_a.index(v3s16(-dev, yy, zz));
-                       u32 ia = leaves_a.index(v3s16(-dev, yy + 1, zz));
-                       for (s16 xx = -dev; xx <= dev; xx++) {
-                               if (pr.range(0, 20) <= 19 - dev) {
-                                       leaves_d[i] = 1;
-                                       leaves_d[ia] = 2;
-                               }
-                               i++;
-                               ia++;
-                       }
-               }
-               dev--;
-       }
-
-       // Blit leaves to vmanip
-       for (s16 z = leaves_a.MinEdge.Z; z <= leaves_a.MaxEdge.Z; z++)
-       for (s16 y = leaves_a.MinEdge.Y; y <= leaves_a.MaxEdge.Y; y++) {
-               v3s16 pmin(leaves_a.MinEdge.X, y, z);
-               u32 i = leaves_a.index(pmin);
-               u32 vi = vmanip.m_area.index(pmin + p1);
-               for (s16 x = leaves_a.MinEdge.X; x <= leaves_a.MaxEdge.X; x++) {
-                       v3s16 p(x, y, z);
-                       if (vmanip.m_area.contains(p + p1) &&
-                                       (vmanip.m_data[vi].getContent() == CONTENT_AIR ||
-                                       vmanip.m_data[vi].getContent() == CONTENT_IGNORE ||
-                                       vmanip.m_data[vi] == snownode)) {
-                               if (leaves_d[i] == 1)
-                                       vmanip.m_data[vi] = leavesnode;
-                               else if (leaves_d[i] == 2)
-                                       vmanip.m_data[vi] = snownode;
-                       }
-                       vi++;
-                       i++;
-               }
-       }
-}
-
-}; // namespace treegen
diff --git a/src/treegen.h b/src/treegen.h
deleted file mode 100644 (file)
index 8e53065..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
-Minetest
-Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>,
-                         2012-2013 RealBadAngel, Maciej Kasatkin <mk@realbadangel.pl>
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser 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.
-*/
-
-#pragma once
-
-#include <matrix4.h>
-#include "noise.h"
-
-class MMVManip;
-class INodeDefManager;
-class ServerEnvironment;
-
-
-namespace treegen {
-
-       enum error {
-               SUCCESS,
-               UNBALANCED_BRACKETS
-       };
-
-       struct TreeDef {
-               std::string initial_axiom;
-               std::string rules_a;
-               std::string rules_b;
-               std::string rules_c;
-               std::string rules_d;
-
-               MapNode trunknode;
-               MapNode leavesnode;
-               MapNode leaves2node;
-
-               int leaves2_chance;
-               int angle;
-               int iterations;
-               int iterations_random_level;
-               std::string trunk_type;
-               bool thin_branches;
-               MapNode fruitnode;
-               int fruit_chance;
-               s32 seed;
-               bool explicit_seed;
-       };
-
-       // Add default tree
-       void make_tree(MMVManip &vmanip, v3s16 p0,
-               bool is_apple_tree, INodeDefManager *ndef, s32 seed);
-       // Add jungle tree
-       void make_jungletree(MMVManip &vmanip, v3s16 p0,
-               INodeDefManager *ndef, s32 seed);
-       // Add pine tree
-       void make_pine_tree(MMVManip &vmanip, v3s16 p0,
-               INodeDefManager *ndef, s32 seed);
-
-       // Add L-Systems tree (used by engine)
-       treegen::error make_ltree(MMVManip &vmanip, v3s16 p0, INodeDefManager *ndef,
-               TreeDef tree_definition);
-       // Spawn L-systems tree from LUA
-       treegen::error spawn_ltree (ServerEnvironment *env, v3s16 p0, INodeDefManager *ndef,
-               const TreeDef &tree_definition);
-
-       // L-System tree gen helper functions
-       void tree_node_placement(MMVManip &vmanip, v3f p0,
-               MapNode node);
-       void tree_trunk_placement(MMVManip &vmanip, v3f p0,
-               TreeDef &tree_definition);
-       void tree_leaves_placement(MMVManip &vmanip, v3f p0,
-               PseudoRandom ps, TreeDef &tree_definition);
-       void tree_single_leaves_placement(MMVManip &vmanip, v3f p0,
-               PseudoRandom ps, TreeDef &tree_definition);
-       void tree_fruit_placement(MMVManip &vmanip, v3f p0,
-               TreeDef &tree_definition);
-       irr::core::matrix4 setRotationAxisRadians(irr::core::matrix4 M, double angle, v3f axis);
-
-       v3f transposeMatrix(irr::core::matrix4 M ,v3f v);
-
-}; // namespace treegen
index 4f5ac80f2a08cd15d24b5180b6b90e1f280bf416..0a5c971b9488b01c7920ee0220035241718f384c 100644 (file)
@@ -21,7 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "noise.h"
 #include "settings.h"
-#include "mapgen_v5.h"
+#include "mapgen/mapgen_v5.h"
 #include "util/sha1.h"
 #include "map_settings_manager.h"
 
index 3fea4482908cceeb7041c72f77834a18d79554ab..2e2417a3b1c56ca449163ff8f6e18c50a95cac83 100644 (file)
@@ -19,7 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "test.h"
 
-#include "mg_schematic.h"
+#include "mapgen/mg_schematic.h"
 #include "gamedef.h"
 #include "nodedef.h"
 
index 81f085017247e1fa6adea3454793b54884b3c7e1..2ad2bbfc740362da4447b9ae6293c82d416e0c6d 100644 (file)
@@ -19,10 +19,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #pragma once
 
-#include "../irrlichttypes.h"
-#include "../exceptions.h"
-#include "../threading/mutex_auto_lock.h"
-#include "../threading/semaphore.h"
+#include "irrlichttypes.h"
+#include "exceptions.h"
+#include "threading/mutex_auto_lock.h"
+#include "threading/semaphore.h"
 #include <list>
 #include <vector>
 #include <map>
index 97e18343afe4c2b09a82d078130c216cfe0d04a6..ef00e3bfeeac30e3c000f9dd852d9732a0fecba2 100644 (file)
@@ -19,8 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #pragma once
 
-#include "../irrlichttypes.h"
-#include "../irr_v3d.h"
+#include "irrlichttypes.h"
+#include "irr_v3d.h"
 
 extern const v3s16 g_6dirs[6];
 
index a48a72a8a8e8c912d8d2062c2423f2123ee9b75a..ebc81185f3918b187a84fe461af24fe84cd83f43 100644 (file)
@@ -20,9 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "numeric.h"
 
 #include "log.h"
-#include "../constants.h" // BS, MAP_BLOCKSIZE
-#include "../noise.h" // PseudoRandom, PcgRandom
-#include "../threading/mutex_auto_lock.h"
+#include "constants.h" // BS, MAP_BLOCKSIZE
+#include "noise.h" // PseudoRandom, PcgRandom
+#include "threading/mutex_auto_lock.h"
 #include <cstring>
 
 
index f09c4e9f5f95dc44a5561cc3d5c4879abdd70a1d..347e7a49ea9939fb93b5d2cf723fb93ce4edb780 100644 (file)
@@ -20,10 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #pragma once
 
 #include "basic_macros.h"
-#include "../irrlichttypes.h"
-#include "../irr_v2d.h"
-#include "../irr_v3d.h"
-#include "../irr_aabb3d.h"
+#include "irrlichttypes.h"
+#include "irr_v2d.h"
+#include "irr_v3d.h"
+#include "irr_aabb3d.h"
 
 #define rangelim(d, min, max) ((d) < (min) ? (min) : ((d) > (max) ? (max) : (d)))
 #define myfloor(x) ((x) < 0.0 ? (int)(x) - 1 : (int)(x))
index e5c5dcf4c9747a04901394544399f6605d77fbb0..c5961d56e51403c0696c8f16256cd3f736ef9792 100644 (file)
@@ -20,7 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "pointedthing.h"
 
 #include "serialize.h"
-#include "../exceptions.h"
+#include "exceptions.h"
 #include <sstream>
 
 PointedThing::PointedThing(const v3s16 &under, const v3s16 &above,
index bd95ef1f3e9f9f84f7db64b6b98842e62a041843..4c4c04234e33b88bdb52fd5bda1da562e97c74dd 100644 (file)
@@ -19,8 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #pragma once
 
-#include "../irrlichttypes.h"
-#include "../irr_v3d.h"
+#include "irrlichttypes.h"
+#include "irr_v3d.h"
 #include <iostream>
 #include <string>
 
index 59b6f5216a66e755ab0401a3ee142b1b9357b992..d29ec87399b0ada78530401e61c2b55dfeb1a124 100644 (file)
@@ -19,8 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #pragma once
 
-#include "../irrlichttypes.h"
-#include "../debug.h" // For assert()
+#include "irrlichttypes.h"
+#include "debug.h" // For assert()
 #include <cstring>
 
 template <typename T>
index a272d82eeea536b1964f66dc8480eaa659edaa8f..53c7c2addeaa3d20440f2f02181a4d2b0347ef1f 100644 (file)
@@ -21,8 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "pointer.h"
 #include "porting.h"
 #include "util/string.h"
-#include "../exceptions.h"
-#include "../irrlichttypes.h"
+#include "exceptions.h"
+#include "irrlichttypes.h"
 
 #include <sstream>
 #include <iomanip>
index f2b0dab2f3faec39bc97939edfc5a9224176ce6f..89bc0b097934dad7c042047fd5e095b7dfe283d5 100644 (file)
@@ -19,9 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #pragma once
 
-#include "../irrlichttypes_bloated.h"
-#include "../exceptions.h" // for SerializationError
-#include "../debug.h" // for assert
+#include "irrlichttypes_bloated.h"
+#include "exceptions.h" // for SerializationError
+#include "debug.h" // for assert
 
 #include "config.h"
 #if HAVE_ENDIAN_H
index bb2d2f6a7b9531cb6ad4a00b9c0e7bc671733785..3eabcbe11b1edb0aa03962deb8be4df0d52fff35 100644 (file)
@@ -23,8 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "log.h"
 
 #include "hex.h"
-#include "../porting.h"
-#include "../translation.h"
+#include "porting.h"
+#include "translation.h"
 
 #include <algorithm>
 #include <sstream>
index 5338dad034dd7f0341e41c39596c3a6052e157e3..73e9beb806f44d8effc6dd8e3a3bec3f82adf23f 100644 (file)
@@ -19,9 +19,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #pragma once
 
-#include "../irrlichttypes.h"
-#include "../threading/thread.h"
-#include "../threading/mutex_auto_lock.h"
+#include "irrlichttypes.h"
+#include "threading/thread.h"
+#include "threading/mutex_auto_lock.h"
 #include "porting.h"
 #include "log.h"
 #include "container.h"
index 6079e5cca87feed8f5ce0d9ccb8bec0dfca69831..717449c6df51f6bf929573a53d6a097da63f5907 100644 (file)
@@ -19,8 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #include "timetaker.h"
 
-#include "../porting.h"
-#include "../log.h"
+#include "porting.h"
+#include "log.h"
 #include <ostream>
 
 TimeTaker::TimeTaker(const std::string &name, u64 *result, TimePrecision prec)
index 2988e8133c9288818b93818ab2ef02adf37a6d51..bc3d4a88dbd4562709a98ff86adc5b8523e105fb 100644 (file)
@@ -19,8 +19,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #pragma once
 
-#include "../irrlichttypes.h"
-#include "../gettime.h"
+#include "irrlichttypes.h"
+#include "gettime.h"
 
 /*
        TimeTaker
index f86d7ecb3c6041245660752f60b9ff868f5cbf95..3ff4142b69a5cf2ec48d6f44c6967c5c05c73b0d 100644 (file)
@@ -2,8 +2,8 @@ src/activeobject.h
 src/ban.cpp
 src/camera.cpp
 src/camera.h
-src/cavegen.cpp
-src/cavegen.h
+src/mapgen/cavegen.cpp
+src/mapgen/cavegen.h
 src/chat.cpp
 src/chat.h
 src/chat_interface.h
@@ -51,21 +51,21 @@ src/convert_json.cpp
 src/convert_json.h
 src/craftdef.cpp
 src/craftdef.h
-src/database.cpp
-src/database-dummy.cpp
-src/database-files.cpp
-src/database-leveldb.cpp
-src/database-postgresql.cpp
-src/database-postgresql.h
-src/database-redis.cpp
-src/database-sqlite3.cpp
-src/database-sqlite3.h
+src/database/database.cpp
+src/database/database-dummy.cpp
+src/database/database-files.cpp
+src/database/database-leveldb.cpp
+src/database/database-postgresql.cpp
+src/database/database-postgresql.h
+src/database/database-redis.cpp
+src/database/database-sqlite3.cpp
+src/database/database-sqlite3.h
 src/daynightratio.h
 src/debug.cpp
 src/debug.h
 src/defaultsettings.cpp
-src/dungeongen.cpp
-src/dungeongen.h
+src/mapgen/dungeongen.cpp
+src/mapgen/dungeongen.h
 src/emerge.cpp
 src/emerge.h
 src/environment.cpp
@@ -86,32 +86,32 @@ src/genericobject.cpp
 src/genericobject.h
 src/gettext.cpp
 src/gettext.h
-src/guiChatConsole.cpp
-src/guiChatConsole.h
-src/guiEditBoxWithScrollbar.cpp
-src/guiEditBoxWithScrollbar.h
-src/guiEngine.cpp
-src/guiEngine.h
-src/guiPathSelectMenu.cpp
-src/guiPathSelectMenu.h
-src/guiFormSpecMenu.cpp
-src/guiFormSpecMenu.h
-src/guiKeyChangeMenu.cpp
-src/guiMainMenu.h
-src/guiPasswordChange.cpp
+src/gui/guiChatConsole.cpp
+src/gui/guiChatConsole.h
+src/gui/guiEditBoxWithScrollbar.cpp
+src/gui/guiEditBoxWithScrollbar.h
+src/gui/guiEngine.cpp
+src/gui/guiEngine.h
+src/gui/guiPathSelectMenu.cpp
+src/gui/guiPathSelectMenu.h
+src/gui/guiFormSpecMenu.cpp
+src/gui/guiFormSpecMenu.h
+src/gui/guiKeyChangeMenu.cpp
+src/gui/guiMainMenu.h
+src/gui/guiPasswordChange.cpp
 src/guiscalingfilter.cpp
 src/guiscalingfilter.h
-src/guiTable.cpp
-src/guiTable.h
-src/guiVolumeChange.cpp
-src/guiVolumeChange.h
+src/gui/guiTable.cpp
+src/gui/guiTable.h
+src/gui/guiVolumeChange.cpp
+src/gui/guiVolumeChange.h
 src/httpfetch.cpp
 src/hud.cpp
 src/hud.h
 src/imagefilters.cpp
 src/imagefilters.h
-src/intlGUIEditBox.cpp
-src/intlGUIEditBox.h
+src/gui/intlGUIEditBox.cpp
+src/gui/intlGUIEditBox.h
 src/inventory.cpp
 src/inventory.h
 src/inventorymanager.cpp
@@ -131,31 +131,31 @@ src/localplayer.cpp
 src/log.cpp
 src/log.h
 src/main.cpp
-src/mainmenumanager.h
+src/gui/mainmenumanager.h
 src/mapblock.cpp
 src/mapblock.h
 src/mapblock_mesh.cpp
 src/mapblock_mesh.h
 src/map.cpp
 src/map.h
-src/mapgen.cpp
-src/mapgen.h
-src/mapgen_carpathian.cpp
-src/mapgen_carpathian.h
-src/mapgen_flat.cpp
-src/mapgen_flat.h
-src/mapgen_fractal.cpp
-src/mapgen_fractal.h
-src/mapgen_singlenode.cpp
-src/mapgen_singlenode.h
-src/mapgen_v5.cpp
-src/mapgen_v5.h
-src/mapgen_v6.cpp
-src/mapgen_v6.h
-src/mapgen_v7.cpp
-src/mapgen_v7.h
-src/mapgen_valleys.cpp
-src/mapgen_valleys.h
+src/mapgen/mapgen.cpp
+src/mapgen/mapgen.h
+src/mapgen/mapgen_carpathian.cpp
+src/mapgen/mapgen_carpathian.h
+src/mapgen/mapgen_flat.cpp
+src/mapgen/mapgen_flat.h
+src/mapgen/mapgen_fractal.cpp
+src/mapgen/mapgen_fractal.h
+src/mapgen/mapgen_singlenode.cpp
+src/mapgen/mapgen_singlenode.h
+src/mapgen/mapgen_v5.cpp
+src/mapgen/mapgen_v5.h
+src/mapgen/mapgen_v6.cpp
+src/mapgen/mapgen_v6.h
+src/mapgen/mapgen_v7.cpp
+src/mapgen/mapgen_v7.h
+src/mapgen/mapgen_valleys.cpp
+src/mapgen/mapgen_valleys.h
 src/mapnode.cpp
 src/mapnode.h
 src/mapsector.cpp
@@ -166,17 +166,17 @@ src/mesh.cpp
 src/mesh.h
 src/mesh_generator_thread.cpp
 src/metadata.h
-src/mg_biome.cpp
-src/mg_biome.h
-src/mg_decoration.cpp
-src/mg_decoration.h
-src/mg_ore.cpp
-src/mg_ore.h
-src/mg_schematic.cpp
-src/mg_schematic.h
+src/mapgen/mg_biome.cpp
+src/mapgen/mg_biome.h
+src/mapgen/mg_decoration.cpp
+src/mapgen/mg_decoration.h
+src/mapgen/mg_ore.cpp
+src/mapgen/mg_ore.h
+src/mapgen/mg_schematic.cpp
+src/mapgen/mg_schematic.h
 src/minimap.cpp
 src/minimap.h
-src/modalMenu.h
+src/gui/modalMenu.h
 src/mods.cpp
 src/mods.h
 src/network/clientopcodes.cpp
@@ -341,10 +341,10 @@ src/threads.h
 src/tileanimation.cpp
 src/tool.cpp
 src/tool.h
-src/touchscreengui.cpp
+src/gui/touchscreengui.cpp
 src/translation.cpp
-src/treegen.cpp
-src/treegen.h
+src/mapgen/treegen.cpp
+src/mapgen/treegen.h
 src/unittest/test.cpp
 src/unittest/test.h
 src/unittest/test_areastore.cpp