]> git.lizzy.rs Git - minetest.git/blobdiff - src/game.cpp
Porting: Refactor initalizePaths()
[minetest.git] / src / game.cpp
index 7f0ac049cdaa389f09d93d4c5db4ef0945bae78d..3dcfcfaa96035b22fffce62a40ef472ff0a26bdb 100644 (file)
@@ -18,64 +18,59 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "game.h"
-#include "irrlichttypes_extrabloated.h"
-#include <IGUICheckBox.h>
-#include <IGUIEditBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-#include <IMaterialRendererServices.h>
-#include "IMeshCache.h"
+
+#include <iomanip>
+#include "camera.h"
 #include "client.h"
-#include "server.h"
-#include "guiPasswordChange.h"
-#include "guiVolumeChange.h"
-#include "guiKeyChangeMenu.h"
-#include "guiFormSpecMenu.h"
-#include "tool.h"
-#include "guiChatConsole.h"
-#include "config.h"
-#include "version.h"
+#include "client/tile.h"     // For TextureSource
+#include "clientmap.h"
 #include "clouds.h"
-#include "particles.h"
-#include "camera.h"
-#include "mapblock.h"
-#include "settings.h"
-#include "profiler.h"
-#include "mainmenumanager.h"
-#include "gettext.h"
+#include "config.h"
+#include "content_cao.h"
+#include "drawscene.h"
+#include "event_manager.h"
+#include "fontengine.h"
+#include "itemdef.h"
 #include "log.h"
 #include "filesys.h"
-// Needed for determining pointing to nodes
-#include "nodedef.h"
-#include "nodemetadata.h"
-#include "main.h" // For g_settings
-#include "itemdef.h"
-#include "tile.h" // For TextureSource
-#include "shader.h" // For ShaderSource
+#include "gettext.h"
+#include "guiChatConsole.h"
+#include "guiFormSpecMenu.h"
+#include "guiKeyChangeMenu.h"
+#include "guiPasswordChange.h"
+#include "guiVolumeChange.h"
+#include "hud.h"
 #include "logoutputbuffer.h"
-#include "subgame.h"
+#include "mainmenumanager.h"
+#include "mapblock.h"
+#include "nodedef.h"         // Needed for determining pointing to nodes
+#include "nodemetadata.h"
+#include "particles.h"
+#include "profiler.h"
 #include "quicktune_shortcutter.h"
-#include "clientmap.h"
-#include "hud.h"
+#include "server.h"
+#include "settings.h"
+#include "shader.h"          // For ShaderSource
 #include "sky.h"
+#include "subgame.h"
+#include "tool.h"
+#include "util/directiontables.h"
+#include "util/pointedthing.h"
+#include "version.h"
+
 #include "sound.h"
+
 #if USE_SOUND
-#include "sound_openal.h"
+       #include "sound_openal.h"
 #endif
-#include "event_manager.h"
-#include <iomanip>
-#include <list>
-#include "util/directiontables.h"
-#include "util/pointedthing.h"
-#include "drawscene.h"
-#include "content_cao.h"
-#include "fontengine.h"
 
 #ifdef HAVE_TOUCHSCREENGUI
-#include "touchscreengui.h"
+       #include "touchscreengui.h"
 #endif
 
+extern Settings *g_settings;
+extern Profiler *g_profiler;
+
 /*
        Text input system
 */
@@ -492,7 +487,7 @@ class ProfilerGraph
                        color(color)
                {}
        };
-       std::list<Piece> m_log;
+       std::vector<Piece> m_log;
 public:
        u32 m_log_max_size;
 
@@ -515,7 +510,7 @@ class ProfilerGraph
        {
                std::map<std::string, Meta> m_meta;
 
-               for (std::list<Piece>::const_iterator k = m_log.begin();
+               for (std::vector<Piece>::const_iterator k = m_log.begin();
                                k != m_log.end(); k++) {
                        const Piece &piece = *k;
 
@@ -565,16 +560,6 @@ class ProfilerGraph
                s32 graphh = 50;
                s32 textx = x_left + m_log_max_size + 15;
                s32 textx2 = textx + 200 - 15;
-
-               // Draw background
-               /*{
-                       u32 num_graphs = m_meta.size();
-                       core::rect<s32> rect(x_left, y_bottom - num_graphs*graphh,
-                                       textx2, y_bottom);
-                       video::SColor bgcolor(120,0,0,0);
-                       driver->draw2DRectangle(bgcolor, rect, NULL);
-               }*/
-
                s32 meta_i = 0;
 
                for (std::map<std::string, Meta>::const_iterator i = m_meta.begin();
@@ -613,7 +598,7 @@ class ProfilerGraph
                        float lastscaledvalue = 0.0;
                        bool lastscaledvalue_exists = false;
 
-                       for (std::list<Piece>::const_iterator j = m_log.begin();
+                       for (std::vector<Piece>::const_iterator j = m_log.begin();
                                        j != m_log.end(); j++) {
                                const Piece &piece = *j;
                                float value = 0;
@@ -1043,7 +1028,11 @@ static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec,
        }
 }
 
+#ifdef __ANDROID__
 #define SIZE_TAG "size[11,5.5]"
+#else
+#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop
+#endif
 
 static void show_chat_menu(GUIFormSpecMenu **cur_formspec,
                InventoryManager *invmgr, IGameDef *gamedef,
@@ -1133,7 +1122,7 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
                os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;"
                   << wide_to_narrow(wstrgettext("Change Password")) << "]";
        }
-       
+
 #ifndef __ANDROID__
        os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
                        << wide_to_narrow(wstrgettext("Sound Volume")) << "]";
@@ -1145,8 +1134,8 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
        os              << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
                        << wide_to_narrow(wstrgettext("Exit to OS"))   << "]"
                        << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
-                       << "textarea[0.4,0.25;3.5,6;;" << "Minetest\n"
-                       << minetest_build_info << "\n"
+                       << "textarea[0.4,0.25;3.5,6;;" << PROJECT_NAME "\n"
+                       << g_build_info << "\n"
                        << "path_user = " << wrap_rows(porting::path_user, 20)
                        << "\n;]";
 
@@ -1249,6 +1238,7 @@ struct KeyCache {
                KEYMAP_ID_FREEMOVE,
                KEYMAP_ID_FASTMOVE,
                KEYMAP_ID_NOCLIP,
+               KEYMAP_ID_CINEMATIC,
                KEYMAP_ID_SCREENSHOT,
                KEYMAP_ID_TOGGLE_HUD,
                KEYMAP_ID_TOGGLE_CHAT,
@@ -1297,6 +1287,7 @@ void KeyCache::populate()
        key[KEYMAP_ID_FREEMOVE]     = getKeySetting("keymap_freemove");
        key[KEYMAP_ID_FASTMOVE]     = getKeySetting("keymap_fastmove");
        key[KEYMAP_ID_NOCLIP]       = getKeySetting("keymap_noclip");
+       key[KEYMAP_ID_CINEMATIC]    = getKeySetting("keymap_cinematic");
        key[KEYMAP_ID_SCREENSHOT]   = getKeySetting("keymap_screenshot");
        key[KEYMAP_ID_TOGGLE_HUD]   = getKeySetting("keymap_toggle_hud");
        key[KEYMAP_ID_TOGGLE_CHAT]  = getKeySetting("keymap_toggle_chat");
@@ -1432,7 +1423,7 @@ class Game
                        // If address is "", local server is used and address is updated
                        std::string *address,
                        u16 port,
-                       std::wstring *error_message,
+                       std::string &error_message,
                        ChatBackend *chat_backend,
                        const SubgameSpec &gamespec,    // Used for local game
                        bool simple_singleplayer_mode);
@@ -1454,9 +1445,8 @@ class Game
 
        // Client creation
        bool createClient(const std::string &playername,
-                       const std::string &password, std::string *address, u16 port,
-                       std::wstring *error_message);
-       bool initGui(std::wstring *error_message);
+                       const std::string &password, std::string *address, u16 port);
+       bool initGui();
 
        // Client connection
        bool connectToServer(const std::string &playername,
@@ -1466,17 +1456,17 @@ class Game
 
        // Main loop
 
-       void updateInteractTimers(GameRunData *args, f32 dtime);
+       void updateInteractTimers(GameRunData *runData, f32 dtime);
        bool checkConnection();
        bool handleCallbacks();
        void processQueues();
-       void updateProfilers(const GameRunData &run_data, const RunStats &stats,
+       void updateProfilers(const GameRunData &runData, const RunStats &stats,
                        const FpsControl &draw_times, f32 dtime);
        void addProfilerGraphs(const RunStats &stats, const FpsControl &draw_times,
                        f32 dtime);
        void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime);
 
-       void processUserInput(VolatileRunFlags *flags, GameRunData *interact_args,
+       void processUserInput(VolatileRunFlags *flags, GameRunData *runData,
                        f32 dtime);
        void processKeyboardInput(VolatileRunFlags *flags,
                        float *statustext_time,
@@ -1493,6 +1483,7 @@ class Game
        void toggleFreeMoveAlt(float *statustext_time, float *jump_timer);
        void toggleFast(float *statustext_time);
        void toggleNoClip(float *statustext_time);
+       void toggleCinematic(float *statustext_time);
 
        void toggleChat(float *statustext_time, bool *flag);
        void toggleHud(float *statustext_time, bool *flag);
@@ -1583,7 +1574,7 @@ class Game
        video::IVideoDriver *driver;
        scene::ISceneManager *smgr;
        bool *kill;
-       std::wstring *error_message;
+       std::string *error_message;
        IGameDef *gamedef;                     // Convenience (same as *client)
        scene::ISceneNode *skybox;
 
@@ -1700,7 +1691,7 @@ bool Game::startup(bool *kill,
                const std::string &password,
                std::string *address,     // can change if simple_singleplayer_mode
                u16 port,
-               std::wstring *error_message,
+               std::string &error_message,
                ChatBackend *chat_backend,
                const SubgameSpec &gamespec,
                bool simple_singleplayer_mode)
@@ -1708,7 +1699,7 @@ bool Game::startup(bool *kill,
        // "cache"
        this->device        = device;
        this->kill          = kill;
-       this->error_message = error_message;
+       this->error_message = &error_message;
        this->random_input  = random_input;
        this->input         = input;
        this->chat_backend  = chat_backend;
@@ -1722,7 +1713,7 @@ bool Game::startup(bool *kill,
        if (!init(map_dir, address, port, gamespec))
                return false;
 
-       if (!createClient(playername, password, address, port, error_message))
+       if (!createClient(playername, password, address, port))
                return false;
 
        return true;
@@ -1733,6 +1724,7 @@ void Game::run()
 {
        ProfilerGraph graph;
        RunStats stats              = { 0 };
+       CameraOrientation cam_view_target  = { 0 };
        CameraOrientation cam_view  = { 0 };
        GameRunData runData         = { 0 };
        FpsControl draw_times       = { 0 };
@@ -1788,7 +1780,17 @@ void Game::run()
                updateProfilers(runData, stats, draw_times, dtime);
                processUserInput(&flags, &runData, dtime);
                // Update camera before player movement to avoid camera lag of one frame
-               updateCameraDirection(&cam_view, &flags);
+               updateCameraDirection(&cam_view_target, &flags);
+               float cam_smoothing = 0;
+               if (g_settings->getBool("cinematic"))
+                       cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing");
+               else
+                       cam_smoothing = 1 - g_settings->getFloat("camera_smoothing");
+               cam_smoothing = rangelim(cam_smoothing, 0.01f, 1.0f);
+               cam_view.camera_yaw += (cam_view_target.camera_yaw -
+                               cam_view.camera_yaw) * cam_smoothing;
+               cam_view.camera_pitch += (cam_view_target.camera_pitch -
+                               cam_view.camera_pitch) * cam_smoothing;
                updatePlayerControl(cam_view);
                step(&dtime);
                processClientEvents(&cam_view, &runData.damage_flash);
@@ -1844,10 +1846,11 @@ void Game::shutdown()
 }
 
 
-
+/****************************************************************************/
 /****************************************************************************
  Startup
  ****************************************************************************/
+/****************************************************************************/
 
 bool Game::init(
                const std::string &map_dir,
@@ -1930,10 +1933,10 @@ bool Game::createSingleplayerServer(const std::string map_dir,
        }
 
        if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
-               *error_message = L"Unable to listen on " +
-                               narrow_to_wide(bind_addr.serializeString()) +
-                               L" because IPv6 is disabled";
-               errorstream << wide_to_narrow(*error_message) << std::endl;
+               *error_message = "Unable to listen on " +
+                               bind_addr.serializeString() +
+                               " because IPv6 is disabled";
+               errorstream << *error_message << std::endl;
                return false;
        }
 
@@ -1946,8 +1949,7 @@ bool Game::createSingleplayerServer(const std::string map_dir,
 }
 
 bool Game::createClient(const std::string &playername,
-               const std::string &password, std::string *address, u16 port,
-               std::wstring *error_message)
+               const std::string &password, std::string *address, u16 port)
 {
        showOverlayMessage(wgettext("Creating client..."), 0, 10);
 
@@ -1962,25 +1964,25 @@ bool Game::createClient(const std::string &playername,
                return false;
 
        if (!could_connect) {
-               if (*error_message == L"" && !connect_aborted) {
+               if (error_message->empty() && !connect_aborted) {
                        // Should not happen if error messages are set properly
-                       *error_message = L"Connection failed for unknown reason";
-                       errorstream << wide_to_narrow(*error_message) << std::endl;
+                       *error_message = "Connection failed for unknown reason";
+                       errorstream << *error_message << std::endl;
                }
                return false;
        }
 
        if (!getServerContent(&connect_aborted)) {
-               if (*error_message == L"" && !connect_aborted) {
+               if (error_message->empty() && !connect_aborted) {
                        // Should not happen if error messages are set properly
-                       *error_message = L"Connection failed for unknown reason";
-                       errorstream << wide_to_narrow(*error_message) << std::endl;
+                       *error_message = "Connection failed for unknown reason";
+                       errorstream << *error_message << std::endl;
                }
                return false;
        }
 
        // Update cached textures, meshes and materials
-       client->afterContentReceived(device, g_fontengine->getFont());
+       client->afterContentReceived(device);
 
        /* Camera
         */
@@ -1993,9 +1995,8 @@ bool Game::createClient(const std::string &playername,
        if (m_cache_enable_clouds) {
                clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1, time(0));
                if (!clouds) {
-                       *error_message = L"Memory allocation error";
-                       *error_message += narrow_to_wide(" (clouds)");
-                       errorstream << wide_to_narrow(*error_message) << std::endl;
+                       *error_message = "Memory allocation error (clouds)";
+                       errorstream << *error_message << std::endl;
                        return false;
                }
        }
@@ -2008,9 +2009,8 @@ bool Game::createClient(const std::string &playername,
        local_inventory = new Inventory(itemdef_manager);
 
        if (!(sky && local_inventory)) {
-               *error_message = L"Memory allocation error";
-               *error_message += narrow_to_wide(" (sky or local inventory)");
-               errorstream << wide_to_narrow(*error_message) << std::endl;
+               *error_message = "Memory allocation error (sky or local inventory)";
+               errorstream << *error_message << std::endl;
                return false;
        }
 
@@ -2024,14 +2024,15 @@ bool Game::createClient(const std::string &playername,
                crack_animation_length = 5;
        }
 
-       if (!initGui(error_message))
+       if (!initGui())
                return false;
 
        /* Set window caption
         */
-       core::stringw str = L"Minetest [";
+       std::wstring str = narrow_to_wide(PROJECT_NAME);
+       str += L" [";
        str += driver->getName();
-       str += "]";
+       str += L"]";
        device->setWindowCaption(str.c_str());
 
        LocalPlayer *player = client->getEnv().getLocalPlayer();
@@ -2041,19 +2042,19 @@ bool Game::createClient(const std::string &playername,
        hud = new Hud(driver, smgr, guienv, gamedef, player, local_inventory);
 
        if (!hud) {
-               *error_message = L"Memory error: could not create HUD";
-               errorstream << wide_to_narrow(*error_message) << std::endl;
+               *error_message = "Memory error: could not create HUD";
+               errorstream << *error_message << std::endl;
                return false;
        }
 
        return true;
 }
 
-bool Game::initGui(std::wstring *error_message)
+bool Game::initGui()
 {
        // First line of debug text
        guitext = guienv->addStaticText(
-                       L"Minetest",
+                       narrow_to_wide(PROJECT_NAME).c_str(),
                        core::rect<s32>(0, 0, 0, 0),
                        false, false, guiroot);
 
@@ -2090,8 +2091,8 @@ bool Game::initGui(std::wstring *error_message)
        gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
                        -1, chat_backend, client);
        if (!gui_chat_console) {
-               *error_message = L"Could not allocate memory for chat console";
-               errorstream << wide_to_narrow(*error_message) << std::endl;
+               *error_message = "Could not allocate memory for chat console";
+               errorstream << *error_message << std::endl;
                return false;
        }
 
@@ -2141,16 +2142,16 @@ bool Game::connectToServer(const std::string &playername,
                        local_server_mode = true;
                }
        } catch (ResolveError &e) {
-               *error_message = L"Couldn't resolve address: " + narrow_to_wide(e.what());
-               errorstream << wide_to_narrow(*error_message) << std::endl;
+               *error_message = std::string("Couldn't resolve address: ") + e.what();
+               errorstream << *error_message << std::endl;
                return false;
        }
 
        if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
-               *error_message = L"Unable to connect to " +
-                               narrow_to_wide(connect_address.serializeString()) +
-                               L" because IPv6 is disabled";
-               errorstream << wide_to_narrow(*error_message) << std::endl;
+               *error_message = "Unable to connect to " +
+                               connect_address.serializeString() +
+                               " because IPv6 is disabled";
+               errorstream << *error_message << std::endl;
                return false;
        }
 
@@ -2180,7 +2181,10 @@ bool Game::connectToServer(const std::string &playername,
                input->clear();
 
                FpsControl fps_control = { 0 };
-               f32 dtime; // in seconds
+               f32 dtime;
+               f32 wait_time = 0; // in seconds
+
+               fps_control.last_time = device->getTimer()->getTime();
 
                while (device->run()) {
 
@@ -2200,9 +2204,9 @@ bool Game::connectToServer(const std::string &playername,
 
                        // Break conditions
                        if (client->accessDenied()) {
-                               *error_message = L"Access denied. Reason: "
+                               *error_message = "Access denied. Reason: "
                                                + client->accessDeniedReason();
-                               errorstream << wide_to_narrow(*error_message) << std::endl;
+                               errorstream << *error_message << std::endl;
                                break;
                        }
 
@@ -2212,6 +2216,14 @@ bool Game::connectToServer(const std::string &playername,
                                break;
                        }
 
+                       wait_time += dtime;
+                       // Only time out if we aren't waiting for the server we started
+                       if ((*address != "") && (wait_time > 10)) {
+                               *error_message = "Connection timed out.";
+                               errorstream << *error_message << std::endl;
+                               break;
+                       }
+
                        // Update status
                        showOverlayMessage(wgettext("Connecting to server..."), dtime, 20);
                }
@@ -2231,6 +2243,8 @@ bool Game::getServerContent(bool *aborted)
        FpsControl fps_control = { 0 };
        f32 dtime; // in seconds
 
+       fps_control.last_time = device->getTimer()->getTime();
+
        while (device->run()) {
 
                limitFps(&fps_control, &dtime);
@@ -2248,16 +2262,12 @@ bool Game::getServerContent(bool *aborted)
                }
 
                // Error conditions
-               if (client->accessDenied()) {
-                       *error_message = L"Access denied. Reason: "
-                                       + client->accessDeniedReason();
-                       errorstream << wide_to_narrow(*error_message) << std::endl;
+               if (!checkConnection())
                        return false;
-               }
 
                if (client->getState() < LC_Init) {
-                       *error_message = L"Client disconnected";
-                       errorstream << wide_to_narrow(*error_message) << std::endl;
+                       *error_message = "Client disconnected";
+                       errorstream << *error_message << std::endl;
                        return false;
                }
 
@@ -2308,20 +2318,21 @@ bool Game::getServerContent(bool *aborted)
 }
 
 
-
+/****************************************************************************/
 /****************************************************************************
  Run
  ****************************************************************************/
+/****************************************************************************/
 
-inline void Game::updateInteractTimers(GameRunData *args, f32 dtime)
+inline void Game::updateInteractTimers(GameRunData *runData, f32 dtime)
 {
-       if (args->nodig_delay_timer >= 0)
-               args->nodig_delay_timer -= dtime;
+       if (runData->nodig_delay_timer >= 0)
+               runData->nodig_delay_timer -= dtime;
 
-       if (args->object_hit_delay_timer >= 0)
-               args->object_hit_delay_timer -= dtime;
+       if (runData->object_hit_delay_timer >= 0)
+               runData->object_hit_delay_timer -= dtime;
 
-       args->time_from_last_punch += dtime;
+       runData->time_from_last_punch += dtime;
 }
 
 
@@ -2330,9 +2341,9 @@ inline void Game::updateInteractTimers(GameRunData *args, f32 dtime)
 inline bool Game::checkConnection()
 {
        if (client->accessDenied()) {
-               *error_message = L"Access denied. Reason: "
+               *error_message = "Access denied. Reason: "
                                + client->accessDeniedReason();
-               errorstream << wide_to_narrow(*error_message) << std::endl;
+               errorstream << *error_message << std::endl;
                return false;
        }
 
@@ -2384,7 +2395,7 @@ void Game::processQueues()
 }
 
 
-void Game::updateProfilers(const GameRunData &run_data, const RunStats &stats,
+void Game::updateProfilers(const GameRunData &runData, const RunStats &stats,
                const FpsControl &draw_times, f32 dtime)
 {
        float profiler_print_interval =
@@ -2403,7 +2414,7 @@ void Game::updateProfilers(const GameRunData &run_data, const RunStats &stats,
                }
 
                update_profiler_gui(guitext_profiler, g_fontengine,
-                               run_data.profiler_current_page, run_data.profiler_max_page,
+                               runData.profiler_current_page, runData.profiler_max_page,
                                driver->getScreenSize().Height);
 
                g_profiler->clear();
@@ -2484,7 +2495,7 @@ void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
  ****************************************************************************/
 
 void Game::processUserInput(VolatileRunFlags *flags,
-               GameRunData *interact_args, f32 dtime)
+               GameRunData *runData, f32 dtime)
 {
        // Reset input if window not active or some menu is active
        if (device->isWindowActive() == false
@@ -2515,18 +2526,18 @@ void Game::processUserInput(VolatileRunFlags *flags,
 #endif
 
        // Increase timer for double tap of "keymap_jump"
-       if (m_cache_doubletap_jump && interact_args->jump_timer <= 0.2)
-               interact_args->jump_timer += dtime;
+       if (m_cache_doubletap_jump && runData->jump_timer <= 0.2)
+               runData->jump_timer += dtime;
 
        processKeyboardInput(
                        flags,
-                       &interact_args->statustext_time,
-                       &interact_args->jump_timer,
-                       &interact_args->reset_jump_timer,
-                       &interact_args->profiler_current_page,
-                       interact_args->profiler_max_page);
+                       &runData->statustext_time,
+                       &runData->jump_timer,
+                       &runData->reset_jump_timer,
+                       &runData->profiler_current_page,
+                       runData->profiler_max_page);
 
-       processItemSelection(&interact_args->new_playeritem);
+       processItemSelection(&runData->new_playeritem);
 }
 
 
@@ -2564,6 +2575,8 @@ void Game::processKeyboardInput(VolatileRunFlags *flags,
                toggleFast(statustext_time);
        } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_NOCLIP])) {
                toggleNoClip(statustext_time);
+       } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CINEMATIC])) {
+               toggleCinematic(statustext_time);
        } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_SCREENSHOT])) {
                client->makeScreenshot(device);
        } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_HUD])) {
@@ -2750,6 +2763,16 @@ void Game::toggleNoClip(float *statustext_time)
                statustext += L" (note: no 'noclip' privilege)";
 }
 
+void Game::toggleCinematic(float *statustext_time)
+{
+       static const wchar_t *msg[] = { L"cinematic disabled", L"cinematic enabled" };
+       bool cinematic = !g_settings->getBool("cinematic");
+       g_settings->set("cinematic", bool_to_cstr(cinematic));
+
+       *statustext_time = 0;
+       statustext = msg[cinematic];
+}
+
 
 void Game::toggleChat(float *statustext_time, bool *flag)
 {
@@ -2959,19 +2982,37 @@ void Game::updatePlayerControl(const CameraOrientation &cam)
                cam.camera_pitch,
                cam.camera_yaw
        );
+
+       u32 keypress_bits =
+                       ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD])  & 0x1) << 0) |
+                       ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]) & 0x1) << 1) |
+                       ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT])     & 0x1) << 2) |
+                       ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT])    & 0x1) << 3) |
+                       ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP])     & 0x1) << 4) |
+                       ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]) & 0x1) << 5) |
+                       ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK])    & 0x1) << 6) |
+                       ( (u32)(input->getLeftState()                                        & 0x1) << 7) |
+                       ( (u32)(input->getRightState()                                       & 0x1) << 8
+               );
+
+#ifdef ANDROID
+       /* For Android, invert the meaning of holding down the fast button (i.e.
+        * holding down the fast button -- if there is one -- means walk), unless
+        * performing an action, sneaking or jumping.
+        */
+       const u32 autofast_exludebits =
+                         (1U << 4) | (1U << 6)     // jump, sneak
+                       | (1U << 7) | (1U << 8);    // left state, right state
+
+       if ((keypress_bits & autofast_exludebits) == 0) {
+               control.aux1 = control.aux1 ^ true;
+               keypress_bits ^= ((u32)(1U << 5));
+       }
+#endif
+
        client->setPlayerControl(control);
        LocalPlayer *player = client->getEnv().getLocalPlayer();
-       player->keyPressed =
-               ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD])  & 0x1) << 0) |
-               ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]) & 0x1) << 1) |
-               ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT])     & 0x1) << 2) |
-               ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT])    & 0x1) << 3) |
-               ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP])     & 0x1) << 4) |
-               ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]) & 0x1) << 5) |
-               ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK])    & 0x1) << 6) |
-               ( (u32)(input->getLeftState()                                        & 0x1) << 7) |
-               ( (u32)(input->getRightState()                                       & 0x1) << 8
-       );
+       player->keyPressed = keypress_bits;
 
        //tt.stop();
 }
@@ -3082,7 +3123,7 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash)
 
                        u32 new_id = player->addHud(e);
                        //if this isn't true our huds aren't consistent
-                       assert(new_id == id);
+                       sanity_check(new_id == id);
 
                        delete event.hudadd.pos;
                        delete event.hudadd.name;
@@ -3174,12 +3215,12 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash)
                                        event.set_sky.params->size() == 6) {
                                sky->setFallbackBgColor(*event.set_sky.bgcolor);
                                skybox = smgr->addSkyBoxSceneNode(
-                                                texture_src->getTexture((*event.set_sky.params)[0]),
-                                                texture_src->getTexture((*event.set_sky.params)[1]),
-                                                texture_src->getTexture((*event.set_sky.params)[2]),
-                                                texture_src->getTexture((*event.set_sky.params)[3]),
-                                                texture_src->getTexture((*event.set_sky.params)[4]),
-                                                texture_src->getTexture((*event.set_sky.params)[5]));
+                                                texture_src->getTextureForMesh((*event.set_sky.params)[0]),
+                                                texture_src->getTextureForMesh((*event.set_sky.params)[1]),
+                                                texture_src->getTextureForMesh((*event.set_sky.params)[2]),
+                                                texture_src->getTextureForMesh((*event.set_sky.params)[3]),
+                                                texture_src->getTextureForMesh((*event.set_sky.params)[4]),
+                                                texture_src->getTextureForMesh((*event.set_sky.params)[5]));
                        }
                        // Handle everything else as plain color
                        else {
@@ -3977,7 +4018,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
 
                std::ostringstream os(std::ios_base::binary);
                os << std::fixed
-                  << "Minetest " << minetest_version_hash
+                  << PROJECT_NAME " " << g_version_hash
                   << " FPS = " << fps
                   << " (R: range_all=" << draw_control->range_all << ")"
                   << std::setprecision(0)
@@ -3993,7 +4034,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
                guitext->setVisible(true);
        } else if (flags.show_hud || flags.show_chat) {
                std::ostringstream os(std::ios_base::binary);
-               os << "Minetest " << minetest_version_hash;
+               os << PROJECT_NAME " " << g_version_hash;
                guitext->setText(narrow_to_wide(os.str()).c_str());
                guitext->setVisible(true);
        } else {
@@ -4109,7 +4150,6 @@ inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime)
        // not using getRealTime is necessary for wine
        device->getTimer()->tick(); // Maker sure device time is up-to-date
        u32 time = device->getTimer()->getTime();
-
        u32 last_time = fps_timings->last_time;
 
        if (time > last_time)  // Make sure time hasn't overflowed
@@ -4145,7 +4185,9 @@ inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime)
        fps_timings->last_time = time;
 }
 
-
+// Note: This will free (using delete[])! \p msg. If you want to use it later,
+// pass a copy of it to this function
+// Note: \p msg must be allocated using new (not malloc())
 void Game::showOverlayMessage(const wchar_t *msg, float dtime,
                int percent, bool draw_clouds)
 {
@@ -4154,9 +4196,11 @@ void Game::showOverlayMessage(const wchar_t *msg, float dtime,
 }
 
 
+/****************************************************************************/
 /****************************************************************************
  Shutdown / cleanup
  ****************************************************************************/
+/****************************************************************************/
 
 void Game::extendedResourceCleanup()
 {
@@ -4180,10 +4224,11 @@ void Game::extendedResourceCleanup()
 }
 
 
-
+/****************************************************************************/
 /****************************************************************************
  extern function for launching the game
  ****************************************************************************/
+/****************************************************************************/
 
 void the_game(bool *kill,
                bool random_input,
@@ -4196,7 +4241,7 @@ void the_game(bool *kill,
                const std::string &address,         // If empty local server is created
                u16 port,
 
-               std::wstring &error_message,
+               std::string &error_message,
                ChatBackend &chat_backend,
                const SubgameSpec &gamespec,        // Used for local game
                bool simple_singleplayer_mode)
@@ -4212,25 +4257,24 @@ void the_game(bool *kill,
        try {
 
                if (game.startup(kill, random_input, input, device, map_dir,
-                                       playername, password, &server_address, port,
-                                       &error_message, &chat_backend, gamespec,
-                                       simple_singleplayer_mode)) {
-
+                               playername, password, &server_address, port,
+                               error_message, &chat_backend, gamespec,
+                               simple_singleplayer_mode)) {
                        game.run();
                        game.shutdown();
                }
 
        } catch (SerializationError &e) {
-               error_message = L"A serialization error occurred:\n"
-                               + narrow_to_wide(e.what()) + L"\n\nThe server is probably "
-                               L" running a different version of Minetest.";
-               errorstream << wide_to_narrow(error_message) << std::endl;
+               error_message = std::string("A serialization error occurred:\n")
+                               + e.what() + "\n\nThe server is probably "
+                               " running a different version of " PROJECT_NAME ".";
+               errorstream << error_message << std::endl;
        } catch (ServerError &e) {
-               error_message = narrow_to_wide(e.what());
-               errorstream << "ServerError: " << e.what() << std::endl;
+               error_message = e.what();
+               errorstream << "ServerError: " << error_message << std::endl;
        } catch (ModError &e) {
-               errorstream << "ModError: " << e.what() << std::endl;
-               error_message = narrow_to_wide(e.what()) + wstrgettext("\nCheck debug.txt for details.");
+               error_message = e.what() + strgettext("\nCheck debug.txt for details.");
+               errorstream << "ModError: " << error_message << std::endl;
        }
 }