For usages of assert() that are meant to persist in Release builds (when NDEBUG is...
[oweals/minetest.git] / src / game.cpp
index acc7a3a31fab77ab880bd20fc8848f2e14c81e23..7b6d2a3c19d6f0305f1dce3a7adaad7cbc14d5dc 100644 (file)
@@ -51,7 +51,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "nodemetadata.h"
 #include "main.h" // For g_settings
 #include "itemdef.h"
-#include "tile.h" // For TextureSource
+#include "client/tile.h" // For TextureSource
 #include "shader.h" // For ShaderSource
 #include "logoutputbuffer.h"
 #include "subgame.h"
@@ -492,7 +492,7 @@ private:
                        color(color)
                {}
        };
-       std::list<Piece> m_log;
+       std::vector<Piece> m_log;
 public:
        u32 m_log_max_size;
 
@@ -515,7 +515,7 @@ public:
        {
                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;
 
@@ -613,7 +613,7 @@ public:
                        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;
@@ -859,7 +859,7 @@ public:
                // Fog distance
                float fog_distance = 10000 * BS;
 
-               if (g_settings->getBool("enable_fog") && !*m_force_fog_off)
+               if (m_fogEnabled && !*m_force_fog_off)
                        fog_distance = *m_fog_range;
 
                services->setPixelShaderConstant("fogDistance", &fog_distance, 1);
@@ -1043,7 +1043,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,
@@ -1074,7 +1078,7 @@ static void show_deathscreen(GUIFormSpecMenu **cur_formspec,
                std::string(FORMSPEC_VERSION_STRING) +
                SIZE_TAG
                "bgcolor[#320000b4;true]"
-               "label[4.85,1.35;You died.]"
+               "label[4.85,1.35;" + gettext("You died.") + "]"
                "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]"
                ;
 
@@ -1133,7 +1137,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")) << "]";
@@ -1157,7 +1161,7 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
        LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
 
        create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device,  fs_src, txt_dst, NULL);
-
+       (*cur_formspec)->setFocus(L"btn_continue");
        (*cur_formspec)->doPause = true;
 }
 
@@ -1249,6 +1253,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 +1302,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");
@@ -1493,6 +1499,7 @@ protected:
        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);
@@ -1733,6 +1740,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 +1796,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);
@@ -1806,9 +1824,7 @@ void Game::run()
 
 void Game::shutdown()
 {
-       wchar_t *msg = wgettext("Shutting down...");
-       showOverlayMessage(msg, 0, 0, false);
-       delete [] msg;
+       showOverlayMessage(wgettext("Shutting down..."), 0, 0, false);
 
        if (clouds)
                clouds->drop();
@@ -1857,9 +1873,7 @@ bool Game::init(
                u16 port,
                const SubgameSpec &gamespec)
 {
-       wchar_t *msg = wgettext("Loading...");
-       showOverlayMessage(msg, 0, 0);
-       delete [] msg;
+       showOverlayMessage(wgettext("Loading..."), 0, 0);
 
        texture_src = createTextureSource(device);
        shader_src = createShaderSource(device);
@@ -1916,9 +1930,7 @@ bool Game::initSound()
 bool Game::createSingleplayerServer(const std::string map_dir,
                const SubgameSpec &gamespec, u16 port, std::string *address)
 {
-       wchar_t *msg = wgettext("Creating server...");
-       showOverlayMessage(msg, 0, 5);
-       delete [] msg;
+       showOverlayMessage(wgettext("Creating server..."), 0, 5);
 
        std::string bind_str = g_settings->get("bind_address");
        Address bind_addr(0, 0, 0, 0, port);
@@ -1955,9 +1967,7 @@ bool Game::createClient(const std::string &playername,
                const std::string &password, std::string *address, u16 port,
                std::wstring *error_message)
 {
-       wchar_t *msg = wgettext("Creating client...");
-       showOverlayMessage(msg, 0, 10);
-       delete [] msg;
+       showOverlayMessage(wgettext("Creating client..."), 0, 10);
 
        draw_control = new MapDrawControl;
        if (!draw_control)
@@ -2130,9 +2140,7 @@ bool Game::connectToServer(const std::string &playername,
        *aborted = false;
        bool local_server_mode = false;
 
-       wchar_t *msg = wgettext("Resolving address...");
-       showOverlayMessage(msg, 0, 15);
-       delete [] msg;
+       showOverlayMessage(wgettext("Resolving address..."), 0, 15);
 
        Address connect_address(0, 0, 0, 0, port);
 
@@ -2223,9 +2231,7 @@ bool Game::connectToServer(const std::string &playername,
                        }
 
                        // Update status
-                       wchar_t *msg = wgettext("Connecting to server...");
-                       showOverlayMessage(msg, dtime, 20);
-                       delete [] msg;
+                       showOverlayMessage(wgettext("Connecting to server..."), dtime, 20);
                }
        } catch (con::PeerNotFoundException &e) {
                // TODO: Should something be done here? At least an info/error
@@ -2283,12 +2289,12 @@ bool Game::getServerContent(bool *aborted)
                int progress = 25;
 
                if (!client->itemdefReceived()) {
-                       wchar_t *text = wgettext("Item definitions...");
+                       const wchar_t *text = wgettext("Item definitions...");
                        progress = 25;
                        draw_load_screen(text, device, guienv, dtime, progress);
                        delete[] text;
                } else if (!client->nodedefReceived()) {
-                       wchar_t *text = wgettext("Node definitions...");
+                       const wchar_t *text = wgettext("Node definitions...");
                        progress = 30;
                        draw_load_screen(text, device, guienv, dtime, progress);
                        delete[] text;
@@ -2311,7 +2317,7 @@ bool Game::getServerContent(bool *aborted)
                        }
 
                        progress = 30 + client->mediaReceiveProgress() * 35 + 0.5;
-                       draw_load_screen(narrow_to_wide(message.str().c_str()), device,
+                       draw_load_screen(narrow_to_wide(message.str()), device,
                                        guienv, dtime, progress);
                }
        }
@@ -2576,6 +2582,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])) {
@@ -2681,6 +2689,15 @@ void Game::dropSelectedItem()
 
 void Game::openInventory()
 {
+       /*
+        * Don't permit to open inventory is CAO or player doesn't exists.
+        * This prevent showing an empty inventory at player load
+        */
+
+       LocalPlayer *player = client->getEnv().getLocalPlayer();
+       if (player == NULL || player->getCAO() == NULL)
+               return;
+
        infostream << "the_game: " << "Launching inventory" << std::endl;
 
        PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client);
@@ -2753,6 +2770,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)
 {
@@ -3085,7 +3112,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;
@@ -3230,10 +3257,13 @@ void Game::updateCamera(VolatileRunFlags *flags, u32 busy_time,
        v3s16 old_camera_offset = camera->getOffset();
 
        if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CAMERA_MODE])) {
-               camera->toggleCameraMode();
                GenericCAO *playercao = player->getCAO();
 
-               assert(playercao != NULL);
+               // If playercao not loaded, don't change camera
+               if (playercao == NULL)
+                       return;
+
+               camera->toggleCameraMode();
 
                playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
        }
@@ -3938,6 +3968,28 @@ void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
 }
 
 
+inline static const char *yawToDirectionString(int yaw)
+{
+       // NOTE: TODO: This can be done mathematically without the else/else-if
+       // cascade.
+
+       const char *player_direction;
+
+       yaw = wrapDegrees_0_360(yaw);
+
+       if (yaw >= 45 && yaw < 135)
+               player_direction = "West [-X]";
+       else if (yaw >= 135 && yaw < 225)
+               player_direction = "South [-Z]";
+       else if (yaw >= 225 && yaw < 315)
+               player_direction = "East [+X]";
+       else
+               player_direction = "North [+Z]";
+
+       return player_direction;
+}
+
+
 void Game::updateGui(float *statustext_time, const RunStats &stats,
                const GameRunData& runData, f32 dtime, const VolatileRunFlags &flags,
                const CameraOrientation &cam)
@@ -3993,6 +4045,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats,
                   << ", " << (player_position.Y / BS)
                   << ", " << (player_position.Z / BS)
                   << ") (yaw=" << (wrapDegrees_0_360(cam.camera_yaw))
+                  << " " << yawToDirectionString(cam.camera_yaw)
                   << ") (seed = " << ((u64)client->getMapSeed())
                   << ")";
 
@@ -4122,11 +4175,14 @@ 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)
 {
        draw_load_screen(msg, device, guienv, dtime, percent, draw_clouds);
+       delete[] msg;
 }
 
 
@@ -4206,8 +4262,6 @@ void the_game(bool *kill,
                errorstream << "ServerError: " << e.what() << std::endl;
        } catch (ModError &e) {
                errorstream << "ModError: " << e.what() << std::endl;
-               wchar_t *check_msg = wgettext("\nCheck debug.txt for details.");
-               error_message = narrow_to_wide(e.what()) + check_msg;
-               delete [] check_msg;
+               error_message = narrow_to_wide(e.what()) + wstrgettext("\nCheck debug.txt for details.");
        }
 }