diff options
Diffstat (limited to 'src/client/game.cpp')
-rw-r--r-- | src/client/game.cpp | 706 |
1 files changed, 339 insertions, 367 deletions
diff --git a/src/client/game.cpp b/src/client/game.cpp index 37680dda3..450eb4e32 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -55,7 +55,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "particles.h" #include "porting.h" #include "profiler.h" -#include "quicktune_shortcutter.h" #include "raycast.h" #include "server.h" #include "settings.h" @@ -65,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/basic_macros.h" #include "util/directiontables.h" #include "util/pointedthing.h" +#include "util/quicktune_shortcutter.h" #include "irrlicht_changes/static_text.h" #include "version.h" #include "script/scripting_client.h" @@ -184,7 +184,7 @@ struct LocalFormspecHandler : public TextDest return; } - if (m_client && m_client->moddingEnabled()) + if (m_client && m_client->modsLoaded()) m_client->getScript()->on_formspec_input(m_formname, fields); } @@ -413,6 +413,8 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting<float, 3> m_eye_position_pixel; CachedVertexShaderSetting<float, 3> m_eye_position_vertex; CachedPixelShaderSetting<float, 3> m_minimap_yaw; + CachedPixelShaderSetting<float, 3> m_camera_offset_pixel; + CachedPixelShaderSetting<float, 3> m_camera_offset_vertex; CachedPixelShaderSetting<SamplerLayer_t> m_base_texture; CachedPixelShaderSetting<SamplerLayer_t> m_normal_texture; CachedPixelShaderSetting<SamplerLayer_t> m_texture_flags; @@ -445,6 +447,8 @@ public: m_eye_position_pixel("eyePosition"), m_eye_position_vertex("eyePosition"), m_minimap_yaw("yawVec"), + m_camera_offset_pixel("cameraOffset"), + m_camera_offset_vertex("cameraOffset"), m_base_texture("baseTexture"), m_normal_texture("normalTexture"), m_texture_flags("textureFlags"), @@ -493,7 +497,7 @@ public: sunlight.b }; m_day_light.set(dnc, services); - u32 animation_timer = porting::getTimeMs() % 100000; + u32 animation_timer = porting::getTimeMs() % 1000000; float animation_timer_f = (float)animation_timer / 100000.f; m_animation_timer_vertex.set(&animation_timer_f, services); m_animation_timer_pixel.set(&animation_timer_f, services); @@ -523,6 +527,18 @@ public: m_minimap_yaw.set(minimap_yaw_array, services); } + float camera_offset_array[3]; + v3f offset = intToFloat(m_client->getCamera()->getOffset(), BS); +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) + camera_offset_array[0] = offset.X; + camera_offset_array[1] = offset.Y; + camera_offset_array[2] = offset.Z; +#else + offset.getAs3Values(camera_offset_array); +#endif + m_camera_offset_pixel.set(camera_offset_array, services); + m_camera_offset_vertex.set(camera_offset_array, services); + SamplerLayer_t base_tex = 0, normal_tex = 1, flags_tex = 2; @@ -599,7 +615,6 @@ struct GameRunData { bool dig_instantly; bool digging_blocked; bool left_punch; - bool update_wielded_item_trigger; bool reset_jump_timer; float nodig_delay_timer; float dig_time; @@ -689,8 +704,8 @@ protected: bool handleCallbacks(); void processQueues(); void updateProfilers(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 updateProfilerGraphs(ProfilerGraph *graph); // Input related void processUserInput(f32 dtime); @@ -744,15 +759,13 @@ protected: bool look_for_object, const v3s16 &camera_offset); void handlePointingAtNothing(const ItemStack &playerItem); void handlePointingAtNode(const PointedThing &pointed, - const ItemDefinition &playeritem_def, const ItemStack &playeritem, - const ToolCapabilities &playeritem_toolcap, f32 dtime); + const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime); void handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem, const v3f &player_position, bool show_debug); void handleDigging(const PointedThing &pointed, const v3s16 &nodepos, - const ToolCapabilities &playeritem_toolcap, f32 dtime); + const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime); void updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, const CameraOrientation &cam); - void updateProfilerGraphs(ProfilerGraph *graph); // Misc void limitFps(FpsControl *fps_timings, f32 *dtime); @@ -804,8 +817,9 @@ private: void updateChat(f32 dtime, const v2u32 &screensize); - bool nodePlacementPrediction(const ItemDefinition &playeritem_def, - const ItemStack &playeritem, const v3s16 &nodepos, const v3s16 &neighbourpos); + bool nodePlacement(const ItemDefinition &selected_def, const ItemStack &selected_item, + const v3s16 &nodepos, const v3s16 &neighbourpos, const PointedThing &pointed, + const NodeMetadata *meta); static const ClientEventHandler clientEventHandler[CLIENTEVENT_MAX]; InputHandler *input = nullptr; @@ -827,10 +841,6 @@ private: ChatBackend *chat_backend = nullptr; - GUIFormSpecMenu *current_formspec = nullptr; - //default: "". If other than "", empty show_formspec packets will only close the formspec when the formname matches - std::string cur_formname; - EventManager *eventmgr = nullptr; QuicktuneShortcutter *quicktune = nullptr; bool registration_confirmation_shown = false; @@ -841,7 +851,6 @@ private: Camera *camera = nullptr; Clouds *clouds = nullptr; // Free using ->Drop() Sky *sky = nullptr; // Free using ->Drop() - Inventory *local_inventory = nullptr; Hud *hud = nullptr; Minimap *mapper = nullptr; @@ -955,7 +964,6 @@ Game::~Game() delete server; // deleted first to stop all server threads delete hud; - delete local_inventory; delete camera; delete quicktune; delete eventmgr; @@ -1026,7 +1034,6 @@ bool Game::startup(bool *kill, // Reinit runData runData = GameRunData(); runData.time_from_last_punch = 10.0; - runData.update_wielded_item_trigger = true; m_game_ui->initFlags(); @@ -1089,11 +1096,13 @@ void Game::run() previous_screen_size = current_screen_size; } - /* Must be called immediately after a device->run() call because it - * uses device->getTimer()->getTime() - */ + // Calculate dtime = + // RenderingEngine::run() from this iteration + // + Sleep time until the wanted FPS are reached limitFps(&draw_times, &dtime); + // Prepare render data for next iteration + updateStats(&stats, draw_times, dtime); updateInteractTimers(dtime); @@ -1143,8 +1152,9 @@ void Game::shutdown() driver->setRenderTarget(irr::video::ERT_STEREO_BOTH_BUFFERS); } #endif - if (current_formspec) - current_formspec->quitMenu(); + auto formspec = m_game_ui->getFormspecGUI(); + if (formspec) + formspec->quitMenu(); showOverlayMessage(N_("Shutting down..."), 0, 0, false); @@ -1163,10 +1173,7 @@ void Game::shutdown() g_menumgr.deletingMenu(g_menumgr.m_stack.front()); } - if (current_formspec) { - current_formspec->drop(); - current_formspec = NULL; - } + m_game_ui->deleteFormspec(); chat_backend->addMessage(L"", L"# Disconnected."); chat_backend->addMessage(L"", L""); @@ -1355,10 +1362,8 @@ bool Game::createClient(const std::string &playername, scsf->setSky(sky); skybox = NULL; // This is used/set later on in the main run loop - local_inventory = new Inventory(itemdef_manager); - - if (!(sky && local_inventory)) { - *error_message = "Memory allocation error (sky or local inventory)"; + if (!sky) { + *error_message = "Memory allocation error sky"; errorstream << *error_message << std::endl; return false; } @@ -1390,7 +1395,7 @@ bool Game::createClient(const std::string &playername, player->hurt_tilt_timer = 0; player->hurt_tilt_strength = 0; - hud = new Hud(guienv, client, player, local_inventory); + hud = new Hud(guienv, client, player, &player->inventory); if (!hud) { *error_message = "Memory error: could not create HUD"; @@ -1545,7 +1550,7 @@ bool Game::connectToServer(const std::string &playername, } else { registration_confirmation_shown = true; (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1, - &g_menumgr, client, playername, password, *address, connection_aborted))->drop(); + &g_menumgr, client, playername, password, connection_aborted))->drop(); } } else { wait_time += dtime; @@ -1733,7 +1738,8 @@ void Game::processQueues() } -void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, f32 dtime) +void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, + f32 dtime) { float profiler_print_interval = g_settings->getFloat("profiler_print_interval"); @@ -1741,7 +1747,7 @@ void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, if (profiler_print_interval == 0) { print_to_log = false; - profiler_print_interval = 5; + profiler_print_interval = 3; } if (profiler_interval.step(dtime, profiler_print_interval)) { @@ -1754,25 +1760,14 @@ void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, g_profiler->clear(); } - addProfilerGraphs(stats, draw_times, dtime); -} - - -void Game::addProfilerGraphs(const RunStats &stats, - const FpsControl &draw_times, f32 dtime) -{ - g_profiler->graphAdd("mainloop_other", - draw_times.busy_time / 1000.0f - stats.drawtime / 1000.0f); - - if (draw_times.sleep_time != 0) - g_profiler->graphAdd("mainloop_sleep", draw_times.sleep_time / 1000.0f); - g_profiler->graphAdd("mainloop_dtime", dtime); + // Update update graphs + g_profiler->graphAdd("Time non-rendering [ms]", + draw_times.busy_time - stats.drawtime); - g_profiler->add("Elapsed time", dtime); - g_profiler->avg("FPS", 1. / dtime); + g_profiler->graphAdd("Sleep [ms]", draw_times.sleep_time); + g_profiler->graphAdd("FPS", 1.0f / dtime); } - void Game::updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime) { @@ -1853,8 +1848,9 @@ void Game::processUserInput(f32 dtime) input->step(dtime); #ifdef __ANDROID__ - if (current_formspec != NULL) - current_formspec->getAndroidUIInput(); + auto formspec = m_game_ui->getFormspecGUI(); + if (formspec) + formspec->getAndroidUIInput(); else handleAndroidChatInput(); #endif @@ -1880,6 +1876,9 @@ void Game::processKeyInput() } else if (wasKeyDown(KeyType::INVENTORY)) { openInventory(); } else if (input->cancelPressed()) { +#ifdef __ANDROID__ + m_android_chat_open = false; +#endif if (!gui_chat_console->isOpenInhibited()) { showPauseMenu(); } @@ -1888,7 +1887,7 @@ void Game::processKeyInput() } else if (wasKeyDown(KeyType::CMD)) { openConsole(0.2, L"/"); } else if (wasKeyDown(KeyType::CMD_LOCAL)) { - if (client->moddingEnabled()) + if (client->modsLoaded()) openConsole(0.2, L"."); else m_game_ui->showStatusText(wgettext("Client side scripting is disabled")); @@ -1979,7 +1978,7 @@ void Game::processItemSelection(u16 *new_playeritem) /* Item selection using mouse wheel */ - *new_playeritem = client->getPlayerItem(); + *new_playeritem = player->getWieldIndex(); s32 wheel = input->getMouseWheel(); u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1, @@ -2021,7 +2020,7 @@ void Game::dropSelectedItem(bool single_item) a->count = single_item ? 1 : 0; a->from_inv.setCurrentPlayer(); a->from_list = "main"; - a->from_i = client->getPlayerItem(); + a->from_i = client->getEnv().getLocalPlayer()->getWieldIndex(); client->inventoryAction(a); } @@ -2044,13 +2043,14 @@ void Game::openInventory() InventoryLocation inventoryloc; inventoryloc.setCurrentPlayer(); - if (!client->moddingEnabled() + if (!client->modsLoaded() || !client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) { TextDest *txt_dst = new TextDestPlayerInventory(client); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, + auto *&formspec = m_game_ui->updateFormspec(""); + GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, txt_dst, client->getFormspecPrepend()); - cur_formname = ""; - current_formspec->setFormSpec(fs_src->getForm(), inventoryloc); + + formspec->setFormSpec(fs_src->getForm(), inventoryloc); } } @@ -2079,6 +2079,7 @@ void Game::handleAndroidChatInput() if (m_android_chat_open && porting::getInputDialogState() == 0) { std::string text = porting::getInputDialogValue(); client->typeChatMessage(utf8_to_wide(text)); + m_android_chat_open = false; } } #endif @@ -2348,7 +2349,7 @@ void Game::toggleFullViewRange() void Game::checkZoomEnabled() { LocalPlayer *player = client->getEnv().getLocalPlayer(); - if (player->getZoomFOV() < 0.001f) + if (player->getZoomFOV() < 0.001f || player->getFov().fov > 0.0f) m_game_ui->showTranslatedStatusText("Zoom currently disabled by game or mod"); } @@ -2479,6 +2480,13 @@ void Game::updatePlayerControl(const CameraOrientation &cam) keypress_bits |= 1U << 4; } + // autoforward if set: simulate "up" key + if (player->getPlayerSettings().continuous_forward && + client->activeObjectsReceived() && !player->isDead()) { + control.up = true; + keypress_bits |= 1U << 0; + } + client->setPlayerControl(control); player->keyPressed = keypress_bits; @@ -2491,7 +2499,7 @@ inline void Game::step(f32 *dtime) bool can_be_and_is_paused = (simple_singleplayer_mode && g_menumgr.pausesGame()); - if (can_be_and_is_paused) { // This is for a singleplayer server + if (can_be_and_is_paused) { // This is for a singleplayer server *dtime = 0; // No time passes } else { if (server) @@ -2526,9 +2534,8 @@ void Game::handleClientEvent_None(ClientEvent *event, CameraOrientation *cam) void Game::handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam) { - if (client->moddingEnabled()) { + if (client->modsLoaded()) client->getScript()->on_damage_taken(event->player_damage.amount); - } // Damage flash and hurt tilt are not used at death if (client->getHP() > 0) { @@ -2556,7 +2563,7 @@ void Game::handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation * { // If client scripting is enabled, deathscreen is handled by CSM code in // builtin/client/init.lua - if (client->moddingEnabled()) + if (client->modsLoaded()) client->getScript()->on_death(); else showDeathFormspec(); @@ -2571,9 +2578,10 @@ void Game::handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation * void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam) { if (event->show_formspec.formspec->empty()) { - if (current_formspec && (event->show_formspec.formname->empty() - || *(event->show_formspec.formname) == cur_formname)) { - current_formspec->quitMenu(); + auto formspec = m_game_ui->getFormspecGUI(); + if (formspec && (event->show_formspec.formname->empty() + || *(event->show_formspec.formname) == m_game_ui->getFormspecName())) { + formspec->quitMenu(); } } else { FormspecFormSource *fs_src = @@ -2581,9 +2589,9 @@ void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation TextDestPlayerInventory *txt_dst = new TextDestPlayerInventory(client, *(event->show_formspec.formname)); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, + auto *&formspec = m_game_ui->updateFormspec(*(event->show_formspec.formname)); + GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, txt_dst, client->getFormspecPrepend()); - cur_formname = *(event->show_formspec.formname); } delete event->show_formspec.formspec; @@ -2595,7 +2603,7 @@ void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrienta FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec); LocalFormspecHandler *txt_dst = new LocalFormspecHandler(*event->show_formspec.formname, client); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, + GUIFormSpecMenu::create(m_game_ui->getFormspecGUI(), client, &input->joystick, fs_src, txt_dst, client->getFormspecPrepend()); delete event->show_formspec.formspec; @@ -2840,19 +2848,10 @@ void Game::updateCamera(u32 busy_time, f32 dtime) */ ItemStack playeritem; { - InventoryList *mlist = local_inventory->getList("main"); - - if (mlist && client->getPlayerItem() < mlist->getSize()) - playeritem = mlist->getItem(client->getPlayerItem()); - } - - if (playeritem.getDefinition(itemdef_manager).name.empty()) { // override the hand - InventoryList *hlist = local_inventory->getList("hand"); - if (hlist) - playeritem = hlist->getItem(0); + ItemStack selected, hand; + playeritem = player->getWieldedItem(&selected, &hand); } - ToolCapabilities playeritem_toolcap = playeritem.getToolCapabilities(itemdef_manager); @@ -2933,7 +2932,7 @@ void Game::updateSound(f32 dtime) soundmaker->step(dtime); ClientMap &map = client->getEnv().getClientMap(); - MapNode n = map.getNodeNoEx(player->getFootstepNodePos()); + MapNode n = map.getNode(player->getFootstepNodePos()); soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep; } @@ -2942,46 +2941,35 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) { LocalPlayer *player = client->getEnv().getLocalPlayer(); - ItemStack playeritem; - { - InventoryList *mlist = local_inventory->getList("main"); - - if (mlist && client->getPlayerItem() < mlist->getSize()) - playeritem = mlist->getItem(client->getPlayerItem()); - } - - const ItemDefinition &playeritem_def = - playeritem.getDefinition(itemdef_manager); - InventoryList *hlist = local_inventory->getList("hand"); - const ItemDefinition &hand_def = - hlist ? hlist->getItem(0).getDefinition(itemdef_manager) : itemdef_manager->get(""); - v3f player_position = player->getPosition(); + v3f player_eye_position = player->getEyePosition(); v3f camera_position = camera->getPosition(); v3f camera_direction = camera->getDirection(); v3s16 camera_offset = camera->getOffset(); + if (camera->getCameraMode() == CAMERA_MODE_FIRST) + player_eye_position += player->eye_offset_first; + else + player_eye_position += player->eye_offset_third; /* Calculate what block is the crosshair pointing to */ - f32 d = playeritem_def.range; // max. distance - f32 d_hand = hand_def.range; + ItemStack selected_item, hand_item; + const ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item); - if (d < 0 && d_hand >= 0) - d = d_hand; - else if (d < 0) - d = 4.0; + const ItemDefinition &selected_def = selected_item.getDefinition(itemdef_manager); + f32 d = getToolRange(selected_def, hand_item.getDefinition(itemdef_manager)); core::line3d<f32> shootline; if (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT) { - shootline = core::line3d<f32>(camera_position, - camera_position + camera_direction * BS * d); + shootline = core::line3d<f32>(player_eye_position, + player_eye_position + camera_direction * BS * d); } else { - // prevent player pointing anything in front-view - shootline = core::line3d<f32>(camera_position,camera_position); + // prevent player pointing anything in front-view + shootline = core::line3d<f32>(camera_position, camera_position); } #ifdef HAVE_TOUCHSCREENGUI @@ -2998,7 +2986,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) #endif PointedThing pointed = updatePointedThing(shootline, - playeritem_def.liquids_pointable, + selected_def.liquids_pointable, !runData.ldown_for_dig, camera_offset); @@ -3020,7 +3008,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) if (runData.digging) { if (input->getLeftReleased()) { infostream << "Left button released" - << " (stopped digging)" << std::endl; + << " (stopped digging)" << std::endl; runData.digging = false; } else if (pointed != runData.pointed_old) { if (pointed.type == POINTEDTHING_NODE @@ -3031,14 +3019,14 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) // Don't reset. } else { infostream << "Pointing away from node" - << " (stopped digging)" << std::endl; + << " (stopped digging)" << std::endl; runData.digging = false; hud->updateSelectionMesh(camera_offset); } } if (!runData.digging) { - client->interact(1, runData.pointed_old); + client->interact(INTERACT_STOP_DIGGING, runData.pointed_old); client->setCrack(-1, v3s16(0, 0, 0)); runData.dig_time = 0.0; } @@ -3062,30 +3050,19 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud, bool show_debug) else runData.repeat_rightclick_timer = 0; - if (playeritem_def.usable && input->getLeftState()) { - if (input->getLeftClicked() && (!client->moddingEnabled() - || !client->getScript()->on_item_use(playeritem, pointed))) - client->interact(4, pointed); + if (selected_def.usable && input->getLeftState()) { + if (input->getLeftClicked() && (!client->modsLoaded() + || !client->getScript()->on_item_use(selected_item, pointed))) + client->interact(INTERACT_USE, pointed); } else if (pointed.type == POINTEDTHING_NODE) { - ToolCapabilities playeritem_toolcap = - playeritem.getToolCapabilities(itemdef_manager); - if (playeritem.name.empty()) { - const ToolCapabilities *handToolcap = hlist - ? &hlist->getItem(0).getToolCapabilities(itemdef_manager) - : itemdef_manager->get("").tool_capabilities; - - if (handToolcap != nullptr) - playeritem_toolcap = *handToolcap; - } - handlePointingAtNode(pointed, playeritem_def, playeritem, - playeritem_toolcap, dtime); + handlePointingAtNode(pointed, selected_item, hand_item, dtime); } else if (pointed.type == POINTEDTHING_OBJECT) { - handlePointingAtObject(pointed, playeritem, player_position, show_debug); + handlePointingAtObject(pointed, tool_item, player_position, show_debug); } else if (input->getLeftState()) { // When button is held down in air, show continuous animation runData.left_punch = true; } else if (input->getRightClicked()) { - handlePointingAtNothing(playeritem); + handlePointingAtNothing(selected_item); } runData.pointed_old = pointed; @@ -3133,7 +3110,7 @@ PointedThing Game::updatePointedThing( } } else if (result.type == POINTEDTHING_NODE) { // Update selection boxes - MapNode n = map.getNodeNoEx(result.node_undersurface); + MapNode n = map.getNode(result.node_undersurface); std::vector<aabb3f> boxes; n.getSelectionBoxes(nodedef, &boxes, n.getNeighbors(result.node_undersurface, &map)); @@ -3160,12 +3137,12 @@ PointedThing Game::updatePointedThing( v3s16 p = floatToInt(pf, BS); // Get selection mesh light level - MapNode n = map.getNodeNoEx(p); + MapNode n = map.getNode(p); u16 node_light = getInteriorLight(n, -1, nodedef); u16 light_level = node_light; for (const v3s16 &dir : g_6dirs) { - n = map.getNodeNoEx(p + dir); + n = map.getNode(p + dir); node_light = getInteriorLight(n, -1, nodedef); if (node_light > light_level) light_level = node_light; @@ -3197,13 +3174,12 @@ void Game::handlePointingAtNothing(const ItemStack &playerItem) infostream << "Right Clicked in Air" << std::endl; PointedThing fauxPointed; fauxPointed.type = POINTEDTHING_NOTHING; - client->interact(5, fauxPointed); + client->interact(INTERACT_ACTIVATE, fauxPointed); } void Game::handlePointingAtNode(const PointedThing &pointed, - const ItemDefinition &playeritem_def, const ItemStack &playeritem, - const ToolCapabilities &playeritem_toolcap, f32 dtime) + const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime) { v3s16 nodepos = pointed.node_undersurface; v3s16 neighbourpos = pointed.node_abovesurface; @@ -3217,7 +3193,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, if (runData.nodig_delay_timer <= 0.0 && input->getLeftState() && !runData.digging_blocked && client->checkPrivilege("interact")) { - handleDigging(pointed, nodepos, playeritem_toolcap, dtime); + handleDigging(pointed, nodepos, selected_item, hand_item, dtime); } // This should be done after digging handling @@ -3227,7 +3203,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed, m_game_ui->setInfoText(unescape_translate(utf8_to_wide( meta->getString("infotext")))); } else { - MapNode n = map.getNodeNoEx(nodepos); + MapNode n = map.getNode(nodepos); if (nodedef_manager->get(n).tiledef[0].name == "unknown_node.png") { m_game_ui->setInfoText(L"Unknown node: " + @@ -3241,213 +3217,219 @@ void Game::handlePointingAtNode(const PointedThing &pointed, runData.repeat_rightclick_timer = 0; infostream << "Ground right-clicked" << std::endl; - if (meta && !meta->getString("formspec").empty() && !random_input - && !isKeyDown(KeyType::SNEAK)) { - // Report right click to server - if (nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) { - client->interact(3, pointed); - } - - infostream << "Launching custom inventory view" << std::endl; - - InventoryLocation inventoryloc; - inventoryloc.setNodeMeta(nodepos); + camera->setDigging(1); // right click animation (always shown for feedback) - NodeMetadataFormSource *fs_src = new NodeMetadataFormSource( - &client->getEnv().getClientMap(), nodepos); - TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client); + soundmaker->m_player_rightpunch_sound = SimpleSoundSpec(); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); - cur_formname.clear(); + // If the wielded item has node placement prediction, + // make that happen + // And also set the sound and send the interact + // But first check for meta formspec and rightclickable + auto &def = selected_item.getDefinition(itemdef_manager); + bool placed = nodePlacement(def, selected_item, nodepos, neighbourpos, + pointed, meta); - current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc); - } else { - // Report right click to server - - camera->setDigging(1); // right click animation (always shown for feedback) - - // If the wielded item has node placement prediction, - // make that happen - bool placed = nodePlacementPrediction(playeritem_def, playeritem, nodepos, - neighbourpos); - - if (placed) { - // Report to server - client->interact(3, pointed); - // Read the sound - soundmaker->m_player_rightpunch_sound = - playeritem_def.sound_place; - - if (client->moddingEnabled()) - client->getScript()->on_placenode(pointed, playeritem_def); - } else { - soundmaker->m_player_rightpunch_sound = - SimpleSoundSpec(); - - if (playeritem_def.node_placement_prediction.empty() || - nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) { - client->interact(3, pointed); // Report to server - } else { - soundmaker->m_player_rightpunch_sound = - playeritem_def.sound_place_failed; - } - } - } + if (placed && client->modsLoaded()) + client->getScript()->on_placenode(pointed, def); } } -bool Game::nodePlacementPrediction(const ItemDefinition &playeritem_def, - const ItemStack &playeritem, const v3s16 &nodepos, const v3s16 &neighbourpos) +bool Game::nodePlacement(const ItemDefinition &selected_def, + const ItemStack &selected_item, const v3s16 &nodepos, const v3s16 &neighbourpos, + const PointedThing &pointed, const NodeMetadata *meta) { - std::string prediction = playeritem_def.node_placement_prediction; + std::string prediction = selected_def.node_placement_prediction; const NodeDefManager *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) + node = map.getNode(nodepos, &is_valid_position); + if (!is_valid_position) { + soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; + return false; + } + + // formspec in meta + if (meta && !meta->getString("formspec").empty() && !random_input + && !isKeyDown(KeyType::SNEAK)) { + // on_rightclick callbacks are called anyway + if (nodedef_manager->get(map.getNode(nodepos)).rightclickable) + client->interact(INTERACT_PLACE, pointed); + + infostream << "Launching custom inventory view" << std::endl; + + InventoryLocation inventoryloc; + inventoryloc.setNodeMeta(nodepos); + + NodeMetadataFormSource *fs_src = new NodeMetadataFormSource( + &client->getEnv().getClientMap(), nodepos); + TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client); + + auto *&formspec = m_game_ui->updateFormspec(""); + GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, + txt_dst, client->getFormspecPrepend()); + + formspec->setFormSpec(meta->getString("formspec"), inventoryloc); return false; + } + + // on_rightclick callback + if (prediction.empty() || (nodedef->get(node).rightclickable && + !isKeyDown(KeyType::SNEAK))) { + // Report to server + client->interact(INTERACT_PLACE, pointed); + return false; + } - if (!prediction.empty() && !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 - MapNode n_under = map.getNodeNoEx(nodepos, &is_valid_position); - if (is_valid_position) - { - if (nodedef->get(n_under).buildable_to) - p = nodepos; - else { - node = map.getNodeNoEx(p, &is_valid_position); - if (is_valid_position &&!nodedef->get(node).buildable_to) - return false; + verbosestream << "Node placement prediction for " + << selected_def.name << " is " + << prediction << std::endl; + v3s16 p = neighbourpos; + + // Place inside node itself if buildable_to + MapNode n_under = map.getNode(nodepos, &is_valid_position); + if (is_valid_position) { + if (nodedef->get(n_under).buildable_to) { + p = nodepos; + } else { + node = map.getNode(p, &is_valid_position); + if (is_valid_position && !nodedef->get(node).buildable_to) { + // Report to server + client->interact(INTERACT_PLACE, pointed); + return false; } } + } - // Find id of predicted node - content_t id; - bool found = nodedef->getId(prediction, id); + // Find id of predicted node + content_t id; + bool found = nodedef->getId(prediction, id); - if (!found) { - errorstream << "Node placement prediction failed for " - << playeritem_def.name << " (places " - << prediction - << ") - Name not known" << std::endl; - return false; - } + if (!found) { + errorstream << "Node placement prediction failed for " + << selected_def.name << " (places " + << prediction + << ") - Name not known" << std::endl; + // Handle this as if prediction was empty + // Report to server + client->interact(INTERACT_PLACE, pointed); + return false; + } - const ContentFeatures &predicted_f = nodedef->get(id); + const ContentFeatures &predicted_f = nodedef->get(id); - // Predict param2 for facedir and wallmounted nodes - u8 param2 = 0; + // Predict param2 for facedir and wallmounted nodes + u8 param2 = 0; - if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || + if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { - v3s16 dir = nodepos - neighbourpos; + v3s16 dir = nodepos - neighbourpos; - if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) { - param2 = dir.Y < 0 ? 1 : 0; - } else if (abs(dir.X) > abs(dir.Z)) { - param2 = dir.X < 0 ? 3 : 2; - } else { - param2 = dir.Z < 0 ? 5 : 4; - } + if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) { + param2 = dir.Y < 0 ? 1 : 0; + } else if (abs(dir.X) > abs(dir.Z)) { + param2 = dir.X < 0 ? 3 : 2; + } else { + param2 = dir.Z < 0 ? 5 : 4; } + } - if (predicted_f.param_type_2 == CPT2_FACEDIR || + if (predicted_f.param_type_2 == CPT2_FACEDIR || predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) { - v3s16 dir = nodepos - floatToInt(client->getEnv().getLocalPlayer()->getPosition(), BS); + v3s16 dir = nodepos - floatToInt(client->getEnv().getLocalPlayer()->getPosition(), BS); - if (abs(dir.X) > abs(dir.Z)) { - param2 = dir.X < 0 ? 3 : 1; - } else { - param2 = dir.Z < 0 ? 2 : 0; - } + if (abs(dir.X) > abs(dir.Z)) { + param2 = dir.X < 0 ? 3 : 1; + } else { + param2 = dir.Z < 0 ? 2 : 0; } + } + + assert(param2 <= 5); - assert(param2 <= 5); - - //Check attachment if node is in group attached_node - if (((ItemGroupList) predicted_f.groups)["attached_node"] != 0) { - static v3s16 wallmounted_dirs[8] = { - v3s16(0, 1, 0), - v3s16(0, -1, 0), - v3s16(1, 0, 0), - v3s16(-1, 0, 0), - v3s16(0, 0, 1), - v3s16(0, 0, -1), - }; - v3s16 pp; - - if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || + //Check attachment if node is in group attached_node + if (((ItemGroupList) predicted_f.groups)["attached_node"] != 0) { + static v3s16 wallmounted_dirs[8] = { + v3s16(0, 1, 0), + v3s16(0, -1, 0), + v3s16(1, 0, 0), + v3s16(-1, 0, 0), + v3s16(0, 0, 1), + v3s16(0, 0, -1), + }; + v3s16 pp; + + if (predicted_f.param_type_2 == CPT2_WALLMOUNTED || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) - pp = p + wallmounted_dirs[param2]; - else - pp = p + v3s16(0, -1, 0); + pp = p + wallmounted_dirs[param2]; + else + pp = p + v3s16(0, -1, 0); - if (!nodedef->get(map.getNodeNoEx(pp)).walkable) - return false; + if (!nodedef->get(map.getNode(pp)).walkable) { + // Report to server + client->interact(INTERACT_PLACE, pointed); + return false; } + } - // Apply color - if ((predicted_f.param_type_2 == CPT2_COLOR + // Apply color + if ((predicted_f.param_type_2 == CPT2_COLOR || predicted_f.param_type_2 == CPT2_COLORED_FACEDIR || predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED)) { - const std::string &indexstr = playeritem.metadata.getString( - "palette_index", 0); - if (!indexstr.empty()) { - s32 index = mystoi(indexstr); - if (predicted_f.param_type_2 == CPT2_COLOR) { - param2 = index; - } else if (predicted_f.param_type_2 - == CPT2_COLORED_WALLMOUNTED) { - // param2 = pure palette index + other - param2 = (index & 0xf8) | (param2 & 0x07); - } else if (predicted_f.param_type_2 - == CPT2_COLORED_FACEDIR) { - // param2 = pure palette index + other - param2 = (index & 0xe0) | (param2 & 0x1f); - } + const std::string &indexstr = selected_item.metadata.getString( + "palette_index", 0); + if (!indexstr.empty()) { + s32 index = mystoi(indexstr); + if (predicted_f.param_type_2 == CPT2_COLOR) { + param2 = index; + } else if (predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) { + // param2 = pure palette index + other + param2 = (index & 0xf8) | (param2 & 0x07); + } else if (predicted_f.param_type_2 == CPT2_COLORED_FACEDIR) { + // param2 = pure palette index + other + param2 = (index & 0xe0) | (param2 & 0x1f); } } + } - // Add node to client map - MapNode n(id, 0, param2); + // Add node to client map + MapNode n(id, 0, param2); - try { - LocalPlayer *player = client->getEnv().getLocalPlayer(); + try { + LocalPlayer *player = client->getEnv().getLocalPlayer(); - // 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 || + // 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) && neighbourpos != player->getStandingNodePos() + v3s16(0, 2, 0))) { - - // This triggers the required mesh update too - client->addNode(p, n); - return true; - } - } catch (InvalidPositionException &e) { - errorstream << "Node placement prediction failed for " - << playeritem_def.name << " (places " - << prediction - << ") - Position not loaded" << std::endl; + // This triggers the required mesh update too + client->addNode(p, n); + // Report to server + client->interact(INTERACT_PLACE, pointed); + // A node is predicted, also play a sound + soundmaker->m_player_rightpunch_sound = selected_def.sound_place; + return true; + } else { + soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; + return false; } + } catch (InvalidPositionException &e) { + errorstream << "Node placement prediction failed for " + << selected_def.name << " (places " + << prediction + << ") - Position not loaded" << std::endl; + soundmaker->m_player_rightpunch_sound = selected_def.sound_place_failed; + return false; } - - return false; } -void Game::handlePointingAtObject(const PointedThing &pointed, const ItemStack &playeritem, - const v3f &player_position, bool show_debug) +void Game::handlePointingAtObject(const PointedThing &pointed, + const ItemStack &tool_item, const v3f &player_position, bool show_debug) { std::wstring infotext = unescape_translate( utf8_to_wide(runData.selected_object->infoText())); @@ -3483,50 +3465,39 @@ void Game::handlePointingAtObject(const PointedThing &pointed, const ItemStack & // Report direct punch v3f objpos = runData.selected_object->getPosition(); v3f dir = (objpos - player_position).normalize(); - ItemStack item = playeritem; - if (playeritem.name.empty()) { - InventoryList *hlist = local_inventory->getList("hand"); - if (hlist) { - item = hlist->getItem(0); - } - } bool disable_send = runData.selected_object->directReportPunch( - dir, &item, runData.time_from_last_punch); + dir, &tool_item, runData.time_from_last_punch); runData.time_from_last_punch = 0; if (!disable_send) - client->interact(0, pointed); + client->interact(INTERACT_START_DIGGING, pointed); } } else if (input->getRightClicked()) { infostream << "Right-clicked object" << std::endl; - client->interact(3, pointed); // place + client->interact(INTERACT_PLACE, pointed); // place } } void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, - const ToolCapabilities &playeritem_toolcap, f32 dtime) + const ItemStack &selected_item, const ItemStack &hand_item, f32 dtime) { + // See also: serverpackethandle.cpp, action == 2 LocalPlayer *player = client->getEnv().getLocalPlayer(); ClientMap &map = client->getEnv().getClientMap(); - MapNode n = client->getEnv().getClientMap().getNodeNoEx(nodepos); + MapNode n = client->getEnv().getClientMap().getNode(nodepos); // NOTE: Similar piece of code exists on the server side for // cheat detection. // Get digging parameters DigParams params = getDigParams(nodedef_manager->get(n).groups, - &playeritem_toolcap); + &selected_item.getToolCapabilities(itemdef_manager)); // If can't dig, try hand if (!params.diggable) { - InventoryList *hlist = local_inventory->getList("hand"); - const ToolCapabilities *tp = hlist - ? &hlist->getItem(0).getToolCapabilities(itemdef_manager) - : itemdef_manager->get("").tool_capabilities; - - if (tp) - params = getDigParams(nodedef_manager->get(n).groups, tp); + params = getDigParams(nodedef_manager->get(n).groups, + &hand_item.getToolCapabilities(itemdef_manager)); } if (!params.diggable) { @@ -3545,9 +3516,9 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, if (!runData.digging) { infostream << "Started digging" << std::endl; runData.dig_instantly = runData.dig_time_complete == 0; - if (client->moddingEnabled() && client->getScript()->on_punchnode(nodepos, n)) + if (client->modsLoaded() && client->getScript()->on_punchnode(nodepos, n)) return; - client->interact(0, pointed); + client->interact(INTERACT_START_DIGGING, pointed); runData.digging = true; runData.ldown_for_dig = true; } @@ -3603,10 +3574,10 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, runData.nodig_delay_timer = 0.15; bool is_valid_position; - MapNode wasnode = map.getNodeNoEx(nodepos, &is_valid_position); + MapNode wasnode = map.getNode(nodepos, &is_valid_position); if (is_valid_position) { - if (client->moddingEnabled() && - client->getScript()->on_dignode(nodepos, wasnode)) { + if (client->modsLoaded() && + client->getScript()->on_dignode(nodepos, wasnode)) { return; } @@ -3622,7 +3593,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, // implicit else: no prediction } - client->interact(2, pointed); + client->interact(INTERACT_DIGGING_COMPLETED, pointed); if (m_cache_enable_particles) { const ContentFeatures &features = @@ -3650,6 +3621,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, const CameraOrientation &cam) { + TimeTaker tt_update("Game::updateFrame()"); LocalPlayer *player = client->getEnv().getLocalPlayer(); /* @@ -3674,7 +3646,6 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, direct_brightness = time_brightness; sunlight_seen = true; } else { - ScopeProfiler sp(g_profiler, "Detecting background light", SPT_AVG); float old_brightness = sky->getBrightness(); direct_brightness = client->getEnv().getClientMap() .getBackgroundBrightness(MYMIN(runData.fog_range * 1.2, 60 * BS), @@ -3780,31 +3751,14 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, Inventory */ - if (client->getPlayerItem() != runData.new_playeritem) - client->selectPlayerItem(runData.new_playeritem); + if (player->getWieldIndex() != runData.new_playeritem) + client->setPlayerItem(runData.new_playeritem); - // Update local inventory if it has changed - if (client->getLocalInventoryUpdated()) { - //infostream<<"Updating local inventory"<<std::endl; - client->getLocalInventory(*local_inventory); - runData.update_wielded_item_trigger = true; - } - - if (runData.update_wielded_item_trigger) { + if (client->updateWieldedItem()) { // Update wielded tool - InventoryList *mlist = local_inventory->getList("main"); - - if (mlist && (client->getPlayerItem() < mlist->getSize())) { - ItemStack item = mlist->getItem(client->getPlayerItem()); - if (item.getDefinition(itemdef_manager).name.empty()) { // override the hand - InventoryList *hlist = local_inventory->getList("hand"); - if (hlist) - item = hlist->getItem(0); - } - camera->wield(item); - } - - runData.update_wielded_item_trigger = false; + ItemStack selected_item, hand_item; + ItemStack &tool_item = player->getWieldedItem(&selected_item, &hand_item); + camera->wield(tool_item); } /* @@ -3822,28 +3776,42 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, runData.update_draw_list_last_cam_dir = camera_direction; } - m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, dtime); + m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console, dtime); /* make sure menu is on top 1. Delete formspec menu reference if menu was removed 2. Else, make sure formspec menu is on top */ - if (current_formspec) { - if (current_formspec->getReferenceCount() == 1) { - current_formspec->drop(); - current_formspec = NULL; - } else if (isMenuActive()) { - guiroot->bringToFront(current_formspec); + auto formspec = m_game_ui->getFormspecGUI(); + do { // breakable. only runs for one iteration + if (!formspec) + break; + + if (formspec->getReferenceCount() == 1) { + m_game_ui->deleteFormspec(); + break; } - } + + auto &loc = formspec->getFormspecLocation(); + if (loc.type == InventoryLocation::NODEMETA) { + NodeMetadata *meta = client->getEnv().getClientMap().getNodeMetadata(loc.p); + if (!meta || meta->getString("formspec").empty()) { + formspec->quitMenu(); + break; + } + } + + if (isMenuActive()) + guiroot->bringToFront(formspec); + } while (false); /* Drawing begins */ const video::SColor &skycolor = sky->getSkyColor(); - TimeTaker tt_draw("mainloop: draw"); + TimeTaker tt_draw("Draw scene"); driver->beginScene(true, true, skycolor); bool draw_wield_tool = (m_game_ui->m_flags.show_hud && @@ -3903,7 +3871,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, driver->endScene(); stats->drawtime = tt_draw.stop(true); - g_profiler->graphAdd("mainloop_draw", stats->drawtime / 1000.0f); + g_profiler->avg("Game::updateFrame(): draw scene [ms]", stats->drawtime); + g_profiler->graphAdd("Update frame [ms]", tt_update.stop(true)); } /* Log times and stuff for visualization */ @@ -4033,8 +4002,8 @@ void Game::extendedResourceCleanup() void Game::showDeathFormspec() { - static std::string formspec = - std::string(FORMSPEC_VERSION_STRING) + + static std::string formspec_str = + std::string("formspec_version[1]") + SIZE_TAG "bgcolor[#320000b4;true]" "label[4.85,1.35;" + gettext("You died") + "]" @@ -4044,12 +4013,13 @@ void Game::showDeathFormspec() /* Create menu */ /* Note: FormspecFormSource and LocalFormspecHandler * * are deleted by guiFormSpecMenu */ - FormspecFormSource *fs_src = new FormspecFormSource(formspec); + FormspecFormSource *fs_src = new FormspecFormSource(formspec_str); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, - txt_dst, client->getFormspecPrepend()); - current_formspec->setFocus("btn_respawn"); + auto *&formspec = m_game_ui->getFormspecGUI(); + GUIFormSpecMenu::create(formspec, client, &input->joystick, + fs_src, txt_dst, client->getFormspecPrepend()); + formspec->setFocus("btn_respawn"); } #define GET_KEY_NAME(KEY) gettext(getKeySetting(#KEY).name()) @@ -4107,7 +4077,7 @@ void Game::showPauseMenu() float ypos = simple_singleplayer_mode ? 0.7f : 0.1f; std::ostringstream os; - os << FORMSPEC_VERSION_STRING << SIZE_TAG + os << "formspec_version[1]" << SIZE_TAG << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;" << strgettext("Continue") << "]"; @@ -4173,10 +4143,11 @@ void Game::showPauseMenu() FormspecFormSource *fs_src = new FormspecFormSource(os.str()); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); - GUIFormSpecMenu::create(current_formspec, client, &input->joystick, + auto *&formspec = m_game_ui->getFormspecGUI(); + GUIFormSpecMenu::create(formspec, client, &input->joystick, fs_src, txt_dst, client->getFormspecPrepend()); - current_formspec->setFocus("btn_continue"); - current_formspec->doPause = true; + formspec->setFocus("btn_continue"); + formspec->doPause = true; } /****************************************************************************/ @@ -4227,7 +4198,8 @@ void the_game(bool *kill, error_message = e.what(); errorstream << "ServerError: " << error_message << std::endl; } catch (ModError &e) { - error_message = e.what() + strgettext("\nCheck debug.txt for details."); - errorstream << "ModError: " << error_message << std::endl; + error_message = std::string("ModError: ") + e.what() + + strgettext("\nCheck debug.txt for details."); + errorstream << error_message << std::endl; } } |