]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/game.cpp
Remove automatic consistent formspec size <-> font size (now has to be done manually)
[dragonfireclient.git] / src / game.cpp
index 1396e9439d20a50b9d6a236c35e2e8ae9413341a..00931a73b8aeb0509b2ddde98f6985e2156ea0ca 100644 (file)
@@ -70,6 +70,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "util/pointedthing.h"
 #include "drawscene.h"
 #include "content_cao.h"
+#include "fontengine.h"
 
 #ifdef HAVE_TOUCHSCREENGUI
 #include "touchscreengui.h"
@@ -360,12 +361,11 @@ PointedThing getPointedThing(Client *client, v3f player_position,
                for (s16 z = zstart; z <= zend; z++)
                        for (s16 x = xstart; x <= xend; x++) {
                                MapNode n;
+                               bool is_valid_position;
 
-                               try {
-                                       n = map.getNode(v3s16(x, y, z));
-                               } catch (InvalidPositionException &e) {
+                               n = map.getNodeNoEx(v3s16(x, y, z), &is_valid_position);
+                               if (!is_valid_position)
                                        continue;
-                               }
 
                                if (!isPointableNode(n, client, liquids_pointable))
                                        continue;
@@ -438,9 +438,8 @@ PointedThing getPointedThing(Client *client, v3f player_position,
 
 /* Profiler display */
 
-void update_profiler_gui(gui::IGUIStaticText *guitext_profiler,
-               gui::IGUIFont *font, u32 text_height, u32 show_profiler,
-               u32 show_profiler_max)
+void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, FontEngine *fe,
+               u32 show_profiler, u32 show_profiler_max, s32 screen_height)
 {
        if (show_profiler == 0) {
                guitext_profiler->setVisible(false);
@@ -452,14 +451,25 @@ void update_profiler_gui(gui::IGUIStaticText *guitext_profiler,
                guitext_profiler->setText(text.c_str());
                guitext_profiler->setVisible(true);
 
-               s32 w = font->getDimension(text.c_str()).Width;
+               s32 w = fe->getTextWidth(text.c_str());
 
                if (w < 400)
                        w = 400;
 
-               core::rect<s32> rect(6, 4 + (text_height + 5) * 2, 12 + w,
-                                    8 + (text_height + 5) * 2 +
-                                    font->getDimension(text.c_str()).Height);
+               unsigned text_height = fe->getTextHeight();
+
+               core::position2di upper_left, lower_right;
+
+               upper_left.X  = 6;
+               upper_left.Y  = (text_height + 5) * 2;
+               lower_right.X = 12 + w;
+               lower_right.Y = upper_left.Y + (text_height + 1) * MAX_PROFILER_TEXT_ROWS;
+
+               if (lower_right.Y > screen_height * 2 / 3)
+                       lower_right.Y = screen_height * 2 / 3;
+
+               core::rect<s32> rect(upper_left, lower_right);
+
                guitext_profiler->setRelativePosition(rect);
                guitext_profiler->setVisible(true);
        }
@@ -873,22 +883,31 @@ bool nodePlacementPrediction(Client &client,
        std::string prediction = playeritem_def.node_placement_prediction;
        INodeDefManager *nodedef = client.ndef();
        ClientMap &map = client.getEnv().getClientMap();
+       MapNode node;
+       bool is_valid_position;
+
+       node = map.getNodeNoEx(nodepos, &is_valid_position);
+       if (!is_valid_position)
+               return false;
 
-       if (prediction != "" && !nodedef->get(map.getNode(nodepos)).rightclickable) {
+       if (prediction != "" && !nodedef->get(node).rightclickable) {
                verbosestream << "Node placement prediction for "
                              << playeritem_def.name << " is "
                              << prediction << std::endl;
                v3s16 p = neighbourpos;
 
                // Place inside node itself if buildable_to
-               try {
-                       MapNode n_under = map.getNode(nodepos);
-
+               MapNode n_under = map.getNodeNoEx(nodepos, &is_valid_position);
+               if (is_valid_position)
+               {
                        if (nodedef->get(n_under).buildable_to)
                                p = nodepos;
-                       else if (!nodedef->get(map.getNode(p)).buildable_to)
-                               return false;
-               } catch (InvalidPositionException &e) {}
+                       else {
+                               node = map.getNodeNoEx(p, &is_valid_position);
+                               if (is_valid_position &&!nodedef->get(node).buildable_to)
+                                       return false;
+                       }
+               }
 
                // Find id of predicted node
                content_t id;
@@ -946,7 +965,7 @@ bool nodePlacementPrediction(Client &client,
                        else
                                pp = p + v3s16(0, -1, 0);
 
-                       if (!nodedef->get(map.getNode(pp)).walkable)
+                       if (!nodedef->get(map.getNodeNoEx(pp)).walkable)
                                return false;
                }
 
@@ -959,6 +978,7 @@ bool nodePlacementPrediction(Client &client,
                        // Dont place node when player would be inside new node
                        // NOTE: This is to be eventually implemented by a mod as client-side Lua
                        if (!nodedef->get(n).walkable ||
+                                       g_settings->getBool("enable_build_where_you_stand") ||
                                        (client.checkPrivilege("noclip") && g_settings->getBool("noclip")) ||
                                        (nodedef->get(n).walkable &&
                                         neighbourpos != player->getStandingNodePos() + v3s16(0, 1, 0) &&
@@ -1004,11 +1024,7 @@ 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]"
-#endif
 
 static void show_chat_menu(GUIFormSpecMenu **cur_formspec,
                InventoryManager *invmgr, IGameDef *gamedef,
@@ -1127,8 +1143,7 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
 /******************************************************************************/
 static void updateChat(Client &client, f32 dtime, bool show_debug,
                const v2u32 &screensize, bool show_chat, u32 show_profiler,
-               ChatBackend &chat_backend, gui::IGUIStaticText *guitext_chat,
-               gui::IGUIFont *font)
+               ChatBackend &chat_backend, gui::IGUIStaticText *guitext_chat)
 {
        // Add chat log output for errors to be shown in chat
        static LogOutputBuffer chat_log_error_buf(LMT_ERROR);
@@ -1151,9 +1166,7 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
        // Display all messages in a static text element
        unsigned int recent_chat_count = chat_backend.getRecentBuffer().getLineCount();
        std::wstring recent_chat       = chat_backend.getRecentChat();
-
-       // TODO replace by fontengine fcts
-       unsigned int line_height       = font->getDimension(L"Ay").Height + font->getKerningHeight();
+       unsigned int line_height       = g_fontengine->getLineHeight();
 
        guitext_chat->setText(recent_chat.c_str());
 
@@ -1164,7 +1177,7 @@ static void updateChat(Client &client, f32 dtime, bool show_debug,
                chat_y += line_height;
 
        // first pass to calculate height of text to be set
-       s32 width = std::min(font->getDimension(recent_chat.c_str()).Width + 10,
+       s32 width = std::min(g_fontengine->getTextWidth(recent_chat) + 10,
                             porting::getWindowSize().X - 20);
        core::rect<s32> rect(10, chat_y, width, chat_y + porting::getWindowSize().Y);
        guitext_chat->setRelativePosition(rect);
@@ -1308,7 +1321,6 @@ struct FpsControl {
  * many functions that do require objects of thse types do not modify them
  * (so they can be passed as a const qualified parameter)
  */
-
 struct CameraOrientation {
        f32 camera_yaw;    // "right/left"
        f32 camera_pitch;  // "up/down"
@@ -1393,7 +1405,6 @@ class Game
                        bool random_input,
                        InputHandler *input,
                        IrrlichtDevice *device,
-                       gui::IGUIFont *font,
                        const std::string &map_dir,
                        const std::string &playername,
                        const std::string &password,
@@ -1438,6 +1449,8 @@ class Game
        bool checkConnection();
        bool handleCallbacks();
        void processQueues();
+       void updateProfilers(const GameRunData &run_data, 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);
@@ -1474,6 +1487,8 @@ class Game
        void toggleFullViewRange(float *statustext_time);
 
        void updateCameraDirection(CameraOrientation *cam, VolatileRunFlags *flags);
+       void updateCameraOrientation(CameraOrientation *cam,
+                       const VolatileRunFlags &flags);
        void updatePlayerControl(const CameraOrientation &cam);
        void step(f32 *dtime);
        void processClientEvents(CameraOrientation *cam, float *damage_flash);
@@ -1495,8 +1510,9 @@ class Game
        void updateFrame(std::vector<aabb3f> &highlight_boxes, ProfilerGraph *graph,
                        RunStats *stats, GameRunData *runData,
                        f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam);
-       void updateGui(float *statustext_time, const RunStats &stats, f32 dtime,
-                       const VolatileRunFlags &flags, const CameraOrientation &cam);
+       void updateGui(float *statustext_time, const RunStats &stats,
+                       const GameRunData& runData, f32 dtime, const VolatileRunFlags &flags,
+                       const CameraOrientation &cam);
        void updateProfilerGraphs(ProfilerGraph *graph);
 
        // Misc
@@ -1511,8 +1527,6 @@ class Game
        Client *client;
        Server *server;
 
-       gui::IGUIFont *font;
-
        IWritableTextureSource *texture_src;
        IWritableShaderSource *shader_src;
 
@@ -1547,7 +1561,6 @@ class Game
        IrrlichtDevice *device;
        video::IVideoDriver *driver;
        scene::ISceneManager *smgr;
-       u32 text_height;
        bool *kill;
        std::wstring *error_message;
        IGameDef *gamedef;                     // Convenience (same as *client)
@@ -1574,12 +1587,30 @@ class Game
        std::wstring statustext;
 
        KeyCache keycache;
+
+       IntervalLimiter profiler_interval;
+
+       /* TODO: Add a callback function so these can be updated when a setting
+        *       changes.  At this point in time it doesn't matter (e.g. /set
+        *       is documented to change server settings only)
+        *
+        * TODO: Local caching of settings is not optimal and should at some stage
+        *       be updated to use a global settings object for getting thse values
+        *       (as opposed to the this local caching). This can be addressed in
+        *       a later release.
+        */
+       bool m_cache_doubletap_jump;
+       bool m_cache_enable_node_highlighting;
+       bool m_cache_enable_clouds;
+       bool m_cache_enable_particles;
+       bool m_cache_enable_fog;
+       f32  m_cache_mouse_sensitivity;
+       f32  m_repeat_right_click_time;
 };
 
 Game::Game() :
        client(NULL),
        server(NULL),
-       font(NULL),
        texture_src(NULL),
        shader_src(NULL),
        itemdef_manager(NULL),
@@ -1599,7 +1630,15 @@ Game::Game() :
        local_inventory(NULL),
        hud(NULL)
 {
-
+       m_cache_doubletap_jump            = g_settings->getBool("doubletap_jump");
+       m_cache_enable_node_highlighting  = g_settings->getBool("enable_node_highlighting");
+       m_cache_enable_clouds             = g_settings->getBool("enable_clouds");
+       m_cache_enable_particles          = g_settings->getBool("enable_particles");
+       m_cache_enable_fog                = g_settings->getBool("enable_fog");
+       m_cache_mouse_sensitivity         = g_settings->getFloat("mouse_sensitivity");
+       m_repeat_right_click_time         = g_settings->getFloat("repeat_rightclick_time");
+
+       m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0);
 }
 
 
@@ -1635,7 +1674,6 @@ bool Game::startup(bool *kill,
                bool random_input,
                InputHandler *input,
                IrrlichtDevice *device,
-               gui::IGUIFont *font,
                const std::string &map_dir,
                const std::string &playername,
                const std::string &password,
@@ -1648,7 +1686,6 @@ bool Game::startup(bool *kill,
 {
        // "cache"
        this->device        = device;
-       this->font          = font;
        this->kill          = kill;
        this->error_message = error_message;
        this->random_input  = random_input;
@@ -1658,7 +1695,8 @@ bool Game::startup(bool *kill,
 
        driver              = device->getVideoDriver();
        smgr                = device->getSceneManager();
-       text_height         = font->getDimension(L"Random test string").Height;
+
+       smgr->getParameters()->setAttribute(scene::OBJ_LOADER_IGNORE_MATERIAL_FILES, true);
 
        if (!init(map_dir, address, port, gamespec))
                return false;
@@ -1688,6 +1726,7 @@ void Game::run()
        flags.show_hud = true;
        flags.show_debug = g_settings->getBool("show_debug");
        flags.invert_mouse = g_settings->getBool("invert_mouse");
+       flags.first_loop_after_window_activation = true;
 
        /* Clear the profiler */
        Profiler::GraphValues dummyvalues;
@@ -1703,6 +1742,8 @@ void Game::run()
 
        std::vector<aabb3f> highlight_boxes;
 
+       set_light_table(g_settings->getFloat("display_gamma"));
+
        while (device->run() && !(*kill || g_gamecallback->shutdown_requested)) {
 
                /* Must be called immediately after a device->run() call because it
@@ -1722,7 +1763,8 @@ void Game::run()
 
                infotext = L"";
                hud->resizeHotbar();
-               addProfilerGraphs(stats, draw_times, dtime);
+
+               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);
@@ -1754,8 +1796,6 @@ void Game::shutdown()
        if (sky)
                sky->drop();
 
-       clear_particles();
-
        /* cleanup menus */
        while (g_menumgr.menuCount() > 0) {
                g_menumgr.m_stack.front()->setVisible(false);
@@ -1851,7 +1891,7 @@ bool Game::initSound()
 bool Game::createSingleplayerServer(const std::string map_dir,
                const SubgameSpec &gamespec, u16 port, std::string *address)
 {
-       showOverlayMessage("Creating server...", 0, 25);
+       showOverlayMessage("Creating server...", 0, 5);
 
        std::string bind_str = g_settings->get("bind_address");
        Address bind_addr(0, 0, 0, 0, port);
@@ -1889,7 +1929,7 @@ bool Game::createClient(const std::string &playername,
                const std::string &password, std::string *address, u16 port,
                std::wstring *error_message)
 {
-       showOverlayMessage("Creating client...", 0, 50);
+       showOverlayMessage("Creating client...", 0, 10);
 
        draw_control = new MapDrawControl;
        if (!draw_control)
@@ -1920,7 +1960,7 @@ bool Game::createClient(const std::string &playername,
        }
 
        // Update cached textures, meshes and materials
-       client->afterContentReceived(device, font);
+       client->afterContentReceived(device, g_fontengine->getFont());
 
        /* Camera
         */
@@ -1930,7 +1970,7 @@ bool Game::createClient(const std::string &playername,
 
        /* Clouds
         */
-       if (g_settings->getBool("enable_clouds")) {
+       if (m_cache_enable_clouds) {
                clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1, time(0));
                if (!clouds) {
                        *error_message = L"Memory allocation error";
@@ -1978,8 +2018,7 @@ bool Game::createClient(const std::string &playername,
        player->hurt_tilt_timer = 0;
        player->hurt_tilt_strength = 0;
 
-       hud = new Hud(driver, smgr, guienv, font, text_height, gamedef,
-                       player, local_inventory);
+       hud = new Hud(driver, smgr, guienv, gamedef, player, local_inventory);
 
        if (!hud) {
                *error_message = L"Memory error: could not create HUD";
@@ -2008,7 +2047,7 @@ bool Game::initGui(std::wstring *error_message)
        // Object infos are shown in this
        guitext_info = guienv->addStaticText(
                        L"",
-                       core::rect<s32>(0, 0, 400, text_height * 5 + 5) + v2s32(100, 200),
+                       core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + v2s32(100, 200),
                        false, true, guiroot);
 
        // Status text (displays info when showing and hiding GUI stuff, etc.)
@@ -2048,7 +2087,7 @@ bool Game::initGui(std::wstring *error_message)
 #ifdef HAVE_TOUCHSCREENGUI
 
        if (g_touchscreengui)
-               g_touchscreengui->init(tsrc, porting::getDisplayDensity());
+               g_touchscreengui->init(texture_src, porting::getDisplayDensity());
 
 #endif
 
@@ -2059,7 +2098,7 @@ bool Game::connectToServer(const std::string &playername,
                const std::string &password, std::string *address, u16 port,
                bool *connect_ok, bool *aborted)
 {
-       showOverlayMessage("Resolving address...", 0, 75);
+       showOverlayMessage("Resolving address...", 0, 15);
 
        Address connect_address(0, 0, 0, 0, port);
 
@@ -2090,9 +2129,11 @@ bool Game::connectToServer(const std::string &playername,
                return false;
        }
 
-       client = new Client(device, playername.c_str(), password, *draw_control,
-                   texture_src, shader_src, itemdef_manager, nodedef_manager, sound,
-                       eventmgr, connect_address.isIPv6());
+       client = new Client(device,
+                       playername.c_str(), password, simple_singleplayer_mode,
+                       *draw_control, texture_src, shader_src,
+                       itemdef_manager, nodedef_manager, sound, eventmgr,
+                       connect_address.isIPv6());
 
        if (!client)
                return false;
@@ -2148,7 +2189,7 @@ bool Game::connectToServer(const std::string &playername,
                        }
 
                        // Update status
-                       showOverlayMessage("Connecting to server...", dtime, 100);
+                       showOverlayMessage("Connecting to server...", dtime, 20);
                }
        } catch (con::PeerNotFoundException &e) {
                // TODO: Should something be done here? At least an info/error
@@ -2203,17 +2244,17 @@ bool Game::getServerContent(bool *aborted)
                }
 
                // Display status
-               int progress = 0;
+               int progress = 25;
 
                if (!client->itemdefReceived()) {
                        wchar_t *text = wgettext("Item definitions...");
-                       progress = 0;
-                       draw_load_screen(text, device, guienv, font, dtime, progress);
+                       progress = 25;
+                       draw_load_screen(text, device, guienv, dtime, progress);
                        delete[] text;
                } else if (!client->nodedefReceived()) {
                        wchar_t *text = wgettext("Node definitions...");
-                       progress = 25;
-                       draw_load_screen(text, device, guienv, font, dtime, progress);
+                       progress = 30;
+                       draw_load_screen(text, device, guienv, dtime, progress);
                        delete[] text;
                } else {
                        std::stringstream message;
@@ -2233,9 +2274,9 @@ bool Game::getServerContent(bool *aborted)
                                message << " ( " << cur << cur_unit << " )";
                        }
 
-                       progress = 50 + client->mediaReceiveProgress() * 50 + 0.5;
+                       progress = 30 + client->mediaReceiveProgress() * 35 + 0.5;
                        draw_load_screen(narrow_to_wide(message.str().c_str()), device,
-                                       guienv, font, dtime, progress);
+                                       guienv, dtime, progress);
                }
        }
 
@@ -2319,6 +2360,35 @@ void Game::processQueues()
 }
 
 
+void Game::updateProfilers(const GameRunData &run_data, const RunStats &stats,
+               const FpsControl &draw_times, f32 dtime)
+{
+       float profiler_print_interval =
+                       g_settings->getFloat("profiler_print_interval");
+       bool print_to_log = true;
+
+       if (profiler_print_interval == 0) {
+               print_to_log = false;
+               profiler_print_interval = 5;
+       }
+
+       if (profiler_interval.step(dtime, profiler_print_interval)) {
+               if (print_to_log) {
+                       infostream << "Profiler:" << std::endl;
+                       g_profiler->print(infostream);
+               }
+
+               update_profiler_gui(guitext_profiler, g_fontengine,
+                               run_data.profiler_current_page, run_data.profiler_max_page,
+                               driver->getScreenSize().Height);
+
+               g_profiler->clear();
+       }
+
+       addProfilerGraphs(stats, draw_times, dtime);
+}
+
+
 void Game::addProfilerGraphs(const RunStats &stats,
                const FpsControl &draw_times, f32 dtime)
 {
@@ -2421,7 +2491,7 @@ void Game::processUserInput(VolatileRunFlags *flags,
 #endif
 
        // Increase timer for double tap of "keymap_jump"
-       if (g_settings->getBool("doubletap_jump") && interact_args->jump_timer <= 0.2)
+       if (m_cache_doubletap_jump && interact_args->jump_timer <= 0.2)
                interact_args->jump_timer += dtime;
 
        processKeyboardInput(
@@ -2615,7 +2685,7 @@ void Game::toggleFreeMove(float *statustext_time)
 
 void Game::toggleFreeMoveAlt(float *statustext_time, float *jump_timer)
 {
-       if (g_settings->getBool("doubletap_jump") && *jump_timer < 0.2f)
+       if (m_cache_doubletap_jump && *jump_timer < 0.2f)
                toggleFreeMove(statustext_time);
 }
 
@@ -2665,7 +2735,8 @@ void Game::toggleHud(float *statustext_time, bool *flag)
        *flag = !*flag;
        *statustext_time = 0;
        statustext = msg[*flag];
-       client->setHighlighted(client->getHighlighted(), *flag);
+       if (g_settings->getBool("enable_node_highlighting"))
+               client->setHighlighted(client->getHighlighted(), *flag);
 }
 
 
@@ -2720,8 +2791,8 @@ void Game::toggleProfiler(float *statustext_time, u32 *profiler_current_page,
        *profiler_current_page = (*profiler_current_page + 1) % (profiler_max_page + 1);
 
        // FIXME: This updates the profiler with incomplete values
-       update_profiler_gui(guitext_profiler, font, text_height,
-                           *profiler_current_page, profiler_max_page);
+       update_profiler_gui(guitext_profiler, g_fontengine, *profiler_current_page,
+                       profiler_max_page, driver->getScreenSize().Height);
 
        if (*profiler_current_page != 0) {
                std::wstringstream sstr;
@@ -2778,11 +2849,24 @@ void Game::toggleFullViewRange(float *statustext_time)
 void Game::updateCameraDirection(CameraOrientation *cam,
                VolatileRunFlags *flags)
 {
-       // float turn_amount = 0;       // Deprecated?
+       if ((device->isWindowActive() && noMenuActive()) || random_input) {
+
+#ifndef __ANDROID__
+               if (!random_input) {
+                       // Mac OSX gets upset if this is set every frame
+                       if (device->getCursorControl()->isVisible())
+                               device->getCursorControl()->setVisible(false);
+               }
+#endif
 
-       if (!(device->isWindowActive() && noMenuActive()) || random_input) {
+               if (flags->first_loop_after_window_activation)
+                       flags->first_loop_after_window_activation = false;
+               else
+                       updateCameraOrientation(cam, *flags);
 
-       // FIXME: Clean this up
+               input->setMousePos((driver->getScreenSize().Width / 2),
+                               (driver->getScreenSize().Height / 2));
+       } else {
 
 #ifndef ANDROID
                // Mac OSX gets upset if this is set every frame
@@ -2790,63 +2874,38 @@ void Game::updateCameraDirection(CameraOrientation *cam,
                        device->getCursorControl()->setVisible(true);
 #endif
 
-               //infostream<<"window inactive"<<std::endl;
-               flags->first_loop_after_window_activation = true;
-               return;
-       }
+               if (!flags->first_loop_after_window_activation)
+                       flags->first_loop_after_window_activation = true;
 
-#ifndef __ANDROID__
-       if (!random_input) {
-               // Mac OSX gets upset if this is set every frame
-               if (device->getCursorControl()->isVisible())
-                       device->getCursorControl()->setVisible(false);
        }
-#endif
+}
 
-       if (flags->first_loop_after_window_activation) {
-               //infostream<<"window active, first loop"<<std::endl;
-               flags->first_loop_after_window_activation = false;
-       } else {
 
+void Game::updateCameraOrientation(CameraOrientation *cam,
+               const VolatileRunFlags &flags)
+{
 #ifdef HAVE_TOUCHSCREENGUI
-
-               if (g_touchscreengui) {
-                       camera_yaw   = g_touchscreengui->getYaw();
-                       camera_pitch = g_touchscreengui->getPitch();
-               } else {
+       if (g_touchscreengui) {
+               cam->camera_yaw   = g_touchscreengui->getYaw();
+               cam->camera_pitch = g_touchscreengui->getPitch();
+       } else {
 #endif
-                       s32 dx = input->getMousePos().X - (driver->getScreenSize().Width / 2);
-                       s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height / 2);
+               s32 dx = input->getMousePos().X - (driver->getScreenSize().Width / 2);
+               s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height / 2);
 
-                       if (flags->invert_mouse
-                                       || (camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT)) {
-                               dy = -dy;
-                       }
-
-                       //infostream<<"window active, pos difference "<<dx<<","<<dy<<std::endl;
+               if (flags.invert_mouse
+                               || camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT) {
+                       dy = -dy;
+               }
 
-                       float d = g_settings->getFloat("mouse_sensitivity");
-                       d = rangelim(d, 0.01, 100.0);
-                       cam->camera_yaw -= dx * d;
-                       cam->camera_pitch += dy * d;
-                       // turn_amount = v2f(dx, dy).getLength() * d; // deprecated?
+               cam->camera_yaw   -= dx * m_cache_mouse_sensitivity;
+               cam->camera_pitch += dy * m_cache_mouse_sensitivity;
 
 #ifdef HAVE_TOUCHSCREENGUI
-                       }
-#endif
-
-               if (cam->camera_pitch < -89.5)
-                       cam->camera_pitch = -89.5;
-               else if (cam->camera_pitch > 89.5)
-                       cam->camera_pitch = 89.5;
        }
+#endif
 
-       input->setMousePos(driver->getScreenSize().Width / 2,
-                       driver->getScreenSize().Height / 2);
-
-       // Deprecated? Not used anywhere else
-       // recent_turn_speed = recent_turn_speed * 0.9 + turn_amount * 0.1;
-       // std::cerr<<"recent_turn_speed = "<<recent_turn_speed<<std::endl;
+       cam->camera_pitch = rangelim(cam->camera_pitch, -89.5, 89.5);
 }
 
 
@@ -2951,44 +3010,11 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash)
 
                        delete(event.show_formspec.formspec);
                        delete(event.show_formspec.formname);
-               } else if (event.type == CE_SPAWN_PARTICLE) {
-                       video::ITexture *texture =
-                               gamedef->tsrc()->getTexture(*(event.spawn_particle.texture));
-
-                       new Particle(gamedef, smgr, player, client->getEnv(),
-                                       *event.spawn_particle.pos,
-                                       *event.spawn_particle.vel,
-                                       *event.spawn_particle.acc,
-                                       event.spawn_particle.expirationtime,
-                                       event.spawn_particle.size,
-                                       event.spawn_particle.collisiondetection,
-                                       event.spawn_particle.vertical,
-                                       texture,
-                                       v2f(0.0, 0.0),
-                                       v2f(1.0, 1.0));
-               } else if (event.type == CE_ADD_PARTICLESPAWNER) {
-                       video::ITexture *texture =
-                               gamedef->tsrc()->getTexture(*(event.add_particlespawner.texture));
-
-                       new ParticleSpawner(gamedef, smgr, player,
-                                       event.add_particlespawner.amount,
-                                       event.add_particlespawner.spawntime,
-                                       *event.add_particlespawner.minpos,
-                                       *event.add_particlespawner.maxpos,
-                                       *event.add_particlespawner.minvel,
-                                       *event.add_particlespawner.maxvel,
-                                       *event.add_particlespawner.minacc,
-                                       *event.add_particlespawner.maxacc,
-                                       event.add_particlespawner.minexptime,
-                                       event.add_particlespawner.maxexptime,
-                                       event.add_particlespawner.minsize,
-                                       event.add_particlespawner.maxsize,
-                                       event.add_particlespawner.collisiondetection,
-                                       event.add_particlespawner.vertical,
-                                       texture,
-                                       event.add_particlespawner.id);
-               } else if (event.type == CE_DELETE_PARTICLESPAWNER) {
-                       delete_particlespawner(event.delete_particlespawner.id);
+               } else if ((event.type == CE_SPAWN_PARTICLE) ||
+                               (event.type == CE_ADD_PARTICLESPAWNER) ||
+                               (event.type == CE_DELETE_PARTICLESPAWNER)) {
+                       client->getParticleManager()->handleParticleEvent(&event, gamedef,
+                                       smgr, player);
                } else if (event.type == CE_HUDADD) {
                        u32 id = event.hudadd.id;
 
@@ -3299,7 +3325,7 @@ void Game::processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
        if (pointed != runData->pointed_old) {
                infostream << "Pointing at " << pointed.dump() << std::endl;
 
-               if (g_settings->getBool("enable_node_highlighting")) {
+               if (m_cache_enable_node_highlighting) {
                        if (pointed.type == POINTEDTHING_NODE) {
                                client->setHighlighted(pointed.node_undersurface, show_hud);
                        } else {
@@ -3398,7 +3424,7 @@ void Game::handlePointingAtNode(GameRunData *runData,
        if (meta) {
                infotext = narrow_to_wide(meta->getString("infotext"));
        } else {
-               MapNode n = map.getNode(nodepos);
+               MapNode n = map.getNodeNoEx(nodepos);
 
                if (nodedef_manager->get(n).tiledef[0].name == "unknown_node.png") {
                        infotext = L"Unknown node: ";
@@ -3412,8 +3438,7 @@ void Game::handlePointingAtNode(GameRunData *runData,
        }
 
        if ((input->getRightClicked() ||
-                       runData->repeat_rightclick_timer >=
-                       g_settings->getFloat("repeat_rightclick_time")) &&
+                       runData->repeat_rightclick_timer >= m_repeat_right_click_time) &&
                        client->checkPrivilege("interact")) {
                runData->repeat_rightclick_timer = 0;
                infostream << "Ground right-clicked" << std::endl;
@@ -3456,7 +3481,7 @@ void Game::handlePointingAtNode(GameRunData *runData,
                        }
 
                        if (playeritem_def.node_placement_prediction == "" ||
-                                       nodedef_manager->get(map.getNode(nodepos)).rightclickable)
+                                       nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable)
                                client->interact(3, pointed); // Report to server
                }
        }
@@ -3525,7 +3550,7 @@ void Game::handleDigging(GameRunData *runData,
 
        LocalPlayer *player = client->getEnv().getLocalPlayer();
        ClientMap &map = client->getEnv().getClientMap();
-       MapNode n = client->getEnv().getClientMap().getNode(nodepos);
+       MapNode n = client->getEnv().getClientMap().getNodeNoEx(nodepos);
 
        // NOTE: Similar piece of code exists on the server side for
        // cheat detection.
@@ -3548,11 +3573,11 @@ void Game::handleDigging(GameRunData *runData,
        } else {
                runData->dig_time_complete = params.time;
 
-               if (g_settings->getBool("enable_particles")) {
+               if (m_cache_enable_particles) {
                        const ContentFeatures &features =
                                        client->getNodeDefManager()->get(n);
-                       addPunchingParticles(gamedef, smgr, player,
-                                       client->getEnv(), nodepos, features.tiles);
+                       client->getParticleManager()->addPunchingParticles(gamedef, smgr,
+                                       player, nodepos, features.tiles);
                }
        }
 
@@ -3590,15 +3615,16 @@ void Game::handleDigging(GameRunData *runData,
                infostream << "Digging completed" << std::endl;
                client->interact(2, pointed);
                client->setCrack(-1, v3s16(0, 0, 0));
-               MapNode wasnode = map.getNode(nodepos);
-               client->removeNode(nodepos);
+               bool is_valid_position;
+               MapNode wasnode = map.getNodeNoEx(nodepos, &is_valid_position);
+               if (is_valid_position)
+                       client->removeNode(nodepos);
 
-               if (g_settings->getBool("enable_particles")) {
+               if (m_cache_enable_particles) {
                        const ContentFeatures &features =
                                client->getNodeDefManager()->get(wasnode);
-                       addDiggingParticles
-                       (gamedef, smgr, player, client->getEnv(),
-                        nodepos, features.tiles);
+                       client->getParticleManager()->addDiggingParticles(gamedef, smgr,
+                                       player, nodepos, features.tiles);
                }
 
                runData->dig_time = 0;
@@ -3721,15 +3747,13 @@ void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
        /*
                Update particles
        */
-
-       allparticles_step(dtime);
-       allparticlespawners_step(dtime, client->getEnv());
+       client->getParticleManager()->step(dtime);
 
        /*
                Fog
        */
 
-       if (g_settings->getBool("enable_fog") && !flags.force_fog_off) {
+       if (m_cache_enable_fog && !flags.force_fog_off) {
                driver->setFog(
                                sky->getBgColor(),
                                video::EFT_FOG_LINEAR,
@@ -3759,7 +3783,7 @@ void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
 
        updateChat(*client, dtime, flags.show_debug, screensize,
                        flags.show_chat, runData->profiler_current_page,
-                       *chat_backend, guitext_chat, font);
+                       *chat_backend, guitext_chat);
 
        /*
                Inventory
@@ -3801,7 +3825,7 @@ void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
                runData->update_draw_list_last_cam_dir = camera_direction;
        }
 
-       updateGui(&runData->statustext_time, *stats, dtime, flags, cam);
+       updateGui(&runData->statustext_time, *stats, *runData, dtime, flags, cam);
 
        /*
           make sure menu is on top
@@ -3837,7 +3861,7 @@ void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
                Profiler graph
        */
        if (flags.show_profiler_graph)
-               graph->draw(10, screensize.Y - 10, driver, font);
+               graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont());
 
        /*
                Damage flash
@@ -3878,8 +3902,9 @@ void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
 }
 
 
-void Game::updateGui(float *statustext_time, const RunStats& stats,
-               f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam)
+void Game::updateGui(float *statustext_time, const RunStats &stats,
+               const GameRunData& runData, f32 dtime, const VolatileRunFlags &flags,
+               const CameraOrientation &cam)
 {
        v2u32 screensize = driver->getScreenSize();
        LocalPlayer *player = client->getEnv().getLocalPlayer();
@@ -3920,7 +3945,7 @@ void Game::updateGui(float *statustext_time, const RunStats& stats,
        if (guitext->isVisible()) {
                core::rect<s32> rect(
                                5,              5,
-                               screensize.X,   5 + text_height
+                               screensize.X,   5 + g_fontengine->getTextHeight()
                );
                guitext->setRelativePosition(rect);
        }
@@ -3934,12 +3959,25 @@ void Game::updateGui(float *statustext_time, const RunStats& stats,
                   << ") (yaw=" << (wrapDegrees_0_360(cam.camera_yaw))
                   << ") (seed = " << ((u64)client->getMapSeed())
                   << ")";
+
+               if (runData.pointed_old.type == POINTEDTHING_NODE) {
+                       ClientMap &map = client->getEnv().getClientMap();
+                       const INodeDefManager *nodedef = client->getNodeDefManager();
+                       MapNode n = map.getNodeNoEx(runData.pointed_old.node_undersurface);
+                       if (n.getContent() != CONTENT_IGNORE && nodedef->get(n).name != "unknown") {
+                               const ContentFeatures &features = nodedef->get(n);
+                               os << " (pointing_at = " << nodedef->get(n).name
+                                  << " - " << features.tiledef[0].name.c_str()
+                                  << ")";
+                       }
+               }
+
                guitext2->setText(narrow_to_wide(os.str()).c_str());
                guitext2->setVisible(true);
 
                core::rect<s32> rect(
-                               5,             5 + text_height,
-                               screensize.X,  5 + text_height * 2
+                               5,             5 + g_fontengine->getTextHeight(),
+                               screensize.X,  5 + g_fontengine->getTextHeight() * 2
                );
                guitext2->setRelativePosition(rect);
        } else {
@@ -3964,10 +4002,13 @@ void Game::updateGui(float *statustext_time, const RunStats& stats,
        guitext_status->setVisible(!statustext.empty());
 
        if (!statustext.empty()) {
-               s32 status_y = screensize.Y - 130;
+               s32 status_width  = guitext_status->getTextWidth();
+               s32 status_height = guitext_status->getTextHeight();
+               s32 status_y = screensize.Y - 150;
+               s32 status_x = (screensize.X - status_width) / 2;
                core::rect<s32> rect(
-                               10, status_y - guitext_status->getTextHeight(),
-                               10 + guitext_status->getTextWidth(), status_y
+                               status_x , status_y - status_height,
+                               status_x + status_width, status_y
                );
                guitext_status->setRelativePosition(rect);
 
@@ -4050,7 +4091,7 @@ void Game::showOverlayMessage(const char *msg, float dtime,
                int percent, bool draw_clouds)
 {
        wchar_t *text = wgettext(msg);
-       draw_load_screen(text, device, guienv, font, dtime, percent, draw_clouds);
+       draw_load_screen(text, device, guienv, dtime, percent, draw_clouds);
        delete[] text;
 }
 
@@ -4090,7 +4131,6 @@ void the_game(bool *kill,
                bool random_input,
                InputHandler *input,
                IrrlichtDevice *device,
-               gui::IGUIFont *font,
 
                const std::string &map_dir,
                const std::string &playername,
@@ -4113,7 +4153,7 @@ void the_game(bool *kill,
 
        try {
 
-               if (game.startup(kill, random_input, input, device, font, map_dir,
+               if (game.startup(kill, random_input, input, device, map_dir,
                                        playername, password, &server_address, port,
                                        &error_message, &chat_backend, gamespec,
                                        simple_singleplayer_mode)) {