X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fgame.cpp;h=def202fe558711cb6c19fa273dca64ca4038ad20;hb=7ea4a03c835d68a6fb58aa55aa6a6315ec80b79f;hp=d5819f78aa46a2b0f54707917cf9d3a6eb54271d;hpb=b30e8d8ec689fbb65b1cee6fb6ff79322b204d0d;p=dragonfireclient.git diff --git a/src/game.cpp b/src/game.cpp index d5819f78a..def202fe5 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -40,7 +40,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiPasswordChange.h" #include "guiVolumeChange.h" #include "hud.h" -#include "logoutputbuffer.h" #include "mainmenumanager.h" #include "mapblock.h" #include "nodedef.h" // Needed for determining pointing to nodes @@ -56,8 +55,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "tool.h" #include "util/directiontables.h" #include "util/pointedthing.h" +#include "irrlicht_changes/static_text.h" #include "version.h" #include "minimap.h" +#include "mapblock_mesh.h" #include "sound.h" @@ -176,19 +177,6 @@ struct LocalFormspecHandler : public TextDest { } } - if (m_formname == "MT_CHAT_MENU") { - assert(m_client != 0); - - if ((fields.find("btn_send") != fields.end()) || - (fields.find("quit") != fields.end())) { - StringMap::const_iterator it = fields.find("f_text"); - if (it != fields.end()) - m_client->typeChatMessage(utf8_to_wide(it->second)); - - return; - } - } - if (m_formname == "MT_DEATH_SCREEN") { assert(m_client != 0); @@ -286,23 +274,70 @@ inline bool isPointableNode(const MapNode &n, (liquids_pointable && features.isLiquid()); } +static inline void getNeighborConnectingFace(v3s16 p, INodeDefManager *nodedef, + ClientMap *map, MapNode n, u8 bitmask, u8 *neighbors) +{ + MapNode n2 = map->getNodeNoEx(p); + if (nodedef->nodeboxConnects(n, n2, bitmask)) + *neighbors |= bitmask; +} + +static inline u8 getNeighbors(v3s16 p, INodeDefManager *nodedef, ClientMap *map, MapNode n) +{ + u8 neighbors = 0; + const ContentFeatures &f = nodedef->get(n); + // locate possible neighboring nodes to connect to + if (f.drawtype == NDT_NODEBOX && f.node_box.type == NODEBOX_CONNECTED) { + v3s16 p2 = p; + + p2.Y++; + getNeighborConnectingFace(p2, nodedef, map, n, 1, &neighbors); + + p2 = p; + p2.Y--; + getNeighborConnectingFace(p2, nodedef, map, n, 2, &neighbors); + + p2 = p; + p2.Z--; + getNeighborConnectingFace(p2, nodedef, map, n, 4, &neighbors); + + p2 = p; + p2.X--; + getNeighborConnectingFace(p2, nodedef, map, n, 8, &neighbors); + + p2 = p; + p2.Z++; + getNeighborConnectingFace(p2, nodedef, map, n, 16, &neighbors); + + p2 = p; + p2.X++; + getNeighborConnectingFace(p2, nodedef, map, n, 32, &neighbors); + } + + return neighbors; +} + /* Find what the player is pointing at */ -PointedThing getPointedThing(Client *client, v3f player_position, - v3f camera_direction, v3f camera_position, core::line3d shootline, - f32 d, bool liquids_pointable, bool look_for_object, v3s16 camera_offset, - std::vector &hilightboxes, ClientActiveObject *&selected_object) +PointedThing getPointedThing(Client *client, Hud *hud, const v3f &player_position, + const v3f &camera_direction, const v3f &camera_position, + core::line3d shootline, f32 d, bool liquids_pointable, + bool look_for_object, const v3s16 &camera_offset, + ClientActiveObject *&selected_object) { PointedThing result; - hilightboxes.clear(); + std::vector *selectionboxes = hud->getSelectionBoxes(); + selectionboxes->clear(); + static const bool show_entity_selectionbox = g_settings->getBool("show_entity_selectionbox"); + selected_object = NULL; INodeDefManager *nodedef = client->getNodeDefManager(); ClientMap &map = client->getEnv().getClientMap(); - f32 mindistance = BS * 1001; + f32 min_distance = BS * 1001; // First try to find a pointed at active object if (look_for_object) { @@ -310,19 +345,20 @@ PointedThing getPointedThing(Client *client, v3f player_position, camera_position, shootline); if (selected_object != NULL) { - if (selected_object->doShowSelectionBox()) { + if (show_entity_selectionbox && + selected_object->doShowSelectionBox()) { aabb3f *selection_box = selected_object->getSelectionBox(); // Box should exist because object was // returned in the first place assert(selection_box); v3f pos = selected_object->getPosition(); - hilightboxes.push_back(aabb3f( - selection_box->MinEdge + pos - intToFloat(camera_offset, BS), - selection_box->MaxEdge + pos - intToFloat(camera_offset, BS))); + selectionboxes->push_back(aabb3f( + selection_box->MinEdge, selection_box->MaxEdge)); + hud->setSelectionPos(pos, camera_offset); } - mindistance = (selected_object->getPosition() - camera_position).getLength(); + min_distance = (selected_object->getPosition() - camera_position).getLength(); result.type = POINTEDTHING_OBJECT; result.object_id = selected_object->getId(); @@ -331,14 +367,13 @@ PointedThing getPointedThing(Client *client, v3f player_position, // That didn't work, try to find a pointed at node - v3s16 pos_i = floatToInt(player_position, BS); /*infostream<<"pos_i=("< 0 ? a : 1); @@ -355,82 +390,139 @@ PointedThing getPointedThing(Client *client, v3f player_position, if (xend == 32767) xend = 32766; - for (s16 y = ystart; y <= yend; y++) - for (s16 z = zstart; z <= zend; z++) + v3s16 pointed_pos(0, 0, 0); + + for (s16 y = ystart; y <= yend; y++) { + for (s16 z = zstart; z <= zend; z++) { for (s16 x = xstart; x <= xend; x++) { MapNode n; bool is_valid_position; + v3s16 p(x, y, z); - n = map.getNodeNoEx(v3s16(x, y, z), &is_valid_position); - if (!is_valid_position) + n = map.getNodeNoEx(p, &is_valid_position); + if (!is_valid_position) { continue; - - if (!isPointableNode(n, client, liquids_pointable)) + } + if (!isPointableNode(n, client, liquids_pointable)) { continue; + } - std::vector boxes = n.getSelectionBoxes(nodedef); + std::vector boxes; + n.getSelectionBoxes(nodedef, &boxes, getNeighbors(p, nodedef, &map, n)); v3s16 np(x, y, z); v3f npf = intToFloat(np, BS); - for (std::vector::const_iterator i = boxes.begin(); - i != boxes.end(); i++) { + i != boxes.end(); ++i) { aabb3f box = *i; box.MinEdge += npf; box.MaxEdge += npf; - for (u16 j = 0; j < 6; j++) { - v3s16 facedir = g_6dirs[j]; - aabb3f facebox = box; - - f32 d = 0.001 * BS; - - if (facedir.X > 0) - facebox.MinEdge.X = facebox.MaxEdge.X - d; - else if (facedir.X < 0) - facebox.MaxEdge.X = facebox.MinEdge.X + d; - else if (facedir.Y > 0) - facebox.MinEdge.Y = facebox.MaxEdge.Y - d; - else if (facedir.Y < 0) - facebox.MaxEdge.Y = facebox.MinEdge.Y + d; - else if (facedir.Z > 0) - facebox.MinEdge.Z = facebox.MaxEdge.Z - d; - else if (facedir.Z < 0) - facebox.MaxEdge.Z = facebox.MinEdge.Z + d; - - v3f centerpoint = facebox.getCenter(); - f32 distance = (centerpoint - camera_position).getLength(); - - if (distance >= mindistance) - continue; - - if (!facebox.intersectsWithLine(shootline)) - continue; - - v3s16 np_above = np + facedir; - - result.type = POINTEDTHING_NODE; - result.node_undersurface = np; - result.node_abovesurface = np_above; - mindistance = distance; - - hilightboxes.clear(); - - if (!g_settings->getBool("enable_node_highlighting")) { - for (std::vector::const_iterator - i2 = boxes.begin(); - i2 != boxes.end(); i2++) { - aabb3f box = *i2; - box.MinEdge += npf + v3f(-d, -d, -d) - intToFloat(camera_offset, BS); - box.MaxEdge += npf + v3f(d, d, d) - intToFloat(camera_offset, BS); - hilightboxes.push_back(box); - } - } + v3f centerpoint = box.getCenter(); + f32 distance = (centerpoint - camera_position).getLength(); + if (distance >= min_distance) { + continue; + } + if (!box.intersectsWithLine(shootline)) { + continue; } + result.type = POINTEDTHING_NODE; + min_distance = distance; + pointed_pos = np; + } + } + } + } + + if (result.type == POINTEDTHING_NODE) { + f32 d = 0.001 * BS; + MapNode n = map.getNodeNoEx(pointed_pos); + v3f npf = intToFloat(pointed_pos, BS); + std::vector boxes; + n.getSelectionBoxes(nodedef, &boxes, getNeighbors(pointed_pos, nodedef, &map, n)); + f32 face_min_distance = 1000 * BS; + for (std::vector::const_iterator + i = boxes.begin(); + i != boxes.end(); ++i) { + aabb3f box = *i; + box.MinEdge += npf; + box.MaxEdge += npf; + for (u16 j = 0; j < 6; j++) { + v3s16 facedir = g_6dirs[j]; + aabb3f facebox = box; + if (facedir.X > 0) { + facebox.MinEdge.X = facebox.MaxEdge.X - d; + } else if (facedir.X < 0) { + facebox.MaxEdge.X = facebox.MinEdge.X + d; + } else if (facedir.Y > 0) { + facebox.MinEdge.Y = facebox.MaxEdge.Y - d; + } else if (facedir.Y < 0) { + facebox.MaxEdge.Y = facebox.MinEdge.Y + d; + } else if (facedir.Z > 0) { + facebox.MinEdge.Z = facebox.MaxEdge.Z - d; + } else if (facedir.Z < 0) { + facebox.MaxEdge.Z = facebox.MinEdge.Z + d; } - } // for coords + v3f centerpoint = facebox.getCenter(); + f32 distance = (centerpoint - camera_position).getLength(); + if (distance >= face_min_distance) + continue; + if (!facebox.intersectsWithLine(shootline)) + continue; + result.node_abovesurface = pointed_pos + facedir; + face_min_distance = distance; + } + } + selectionboxes->clear(); + for (std::vector::const_iterator + i = boxes.begin(); + i != boxes.end(); ++i) { + aabb3f box = *i; + box.MinEdge += v3f(-d, -d, -d); + box.MaxEdge += v3f(d, d, d); + selectionboxes->push_back(box); + } + hud->setSelectionPos(intToFloat(pointed_pos, BS), camera_offset); + result.node_undersurface = pointed_pos; + } + // Update selection mesh light level and vertex colors + if (selectionboxes->size() > 0) { + v3f pf = hud->getSelectionPos(); + v3s16 p = floatToInt(pf, BS); + + // Get selection mesh light level + MapNode n = map.getNodeNoEx(p); + u16 node_light = getInteriorLight(n, -1, nodedef); + u16 light_level = node_light; + + for (u8 i = 0; i < 6; i++) { + n = map.getNodeNoEx(p + g_6dirs[i]); + node_light = getInteriorLight(n, -1, nodedef); + if (node_light > light_level) + light_level = node_light; + } + + video::SColor c = MapBlock_LightColor(255, light_level, 0); + u8 day = c.getRed(); + u8 night = c.getGreen(); + u32 daynight_ratio = client->getEnv().getDayNightRatio(); + finalColorBlend(c, day, night, daynight_ratio); + + // Modify final color a bit with time + u32 timer = porting::getTimeMs() % 5000; + float timerf = (float)(irr::core::PI * ((timer / 2500.0) - 0.5)); + float sin_r = 0.08 * sin(timerf); + float sin_g = 0.08 * sin(timerf + irr::core::PI * 0.5); + float sin_b = 0.08 * sin(timerf + irr::core::PI); + c.setRed(core::clamp(core::round32(c.getRed() * (0.8 + sin_r)), 0, 255)); + c.setGreen(core::clamp(core::round32(c.getGreen() * (0.8 + sin_g)), 0, 255)); + c.setBlue(core::clamp(core::round32(c.getBlue() * (0.8 + sin_b)), 0, 255)); + + // Set mesh final color + hud->setSelectionMeshColor(c); + } return result; } @@ -446,7 +538,7 @@ void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, FontEngine *fe, std::ostringstream os(std::ios_base::binary); g_profiler->printPage(os, show_profiler, show_profiler_max); std::wstring text = utf8_to_wide(os.str()); - guitext_profiler->setText(text.c_str()); + setStaticText(guitext_profiler, text.c_str()); guitext_profiler->setVisible(true); s32 w = fe->getTextWidth(text.c_str()); @@ -490,7 +582,7 @@ class ProfilerGraph color(color) {} }; - std::vector m_log; + std::deque m_log; public: u32 m_log_max_size; @@ -513,12 +605,12 @@ class ProfilerGraph { std::map m_meta; - for (std::vector::const_iterator k = m_log.begin(); - k != m_log.end(); k++) { + for (std::deque::const_iterator k = m_log.begin(); + k != m_log.end(); ++k) { const Piece &piece = *k; for (Profiler::GraphValues::const_iterator i = piece.values.begin(); - i != piece.values.end(); i++) { + i != piece.values.end(); ++i) { const std::string &id = i->first; const float &value = i->second; std::map::iterator j = @@ -550,7 +642,7 @@ class ProfilerGraph u32 next_color_i = 0; for (std::map::iterator i = m_meta.begin(); - i != m_meta.end(); i++) { + i != m_meta.end(); ++i) { Meta &meta = i->second; video::SColor color(255, 200, 200, 200); @@ -566,7 +658,7 @@ class ProfilerGraph s32 meta_i = 0; for (std::map::const_iterator i = m_meta.begin(); - i != m_meta.end(); i++) { + i != m_meta.end(); ++i) { const std::string &id = i->first; const Meta &meta = i->second; s32 x = x_left; @@ -601,8 +693,8 @@ class ProfilerGraph float lastscaledvalue = 0.0; bool lastscaledvalue_exists = false; - for (std::vector::const_iterator j = m_log.begin(); - j != m_log.end(); j++) { + for (std::deque::const_iterator j = m_log.begin(); + j != m_log.end(); ++j) { const Piece &piece = *j; float value = 0; bool value_exists = false; @@ -871,18 +963,21 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter services->setPixelShaderConstant("yawVec", (irr::f32 *)&minimap_yaw_vec, 3); // Uniform sampler layers - int layer0 = 0; - int layer1 = 1; - int layer2 = 2; // before 1.8 there isn't a "integer interface", only float #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) + f32 layer0 = 0; + f32 layer1 = 1; + f32 layer2 = 2; services->setPixelShaderConstant("baseTexture" , (irr::f32 *)&layer0, 1); services->setPixelShaderConstant("normalTexture" , (irr::f32 *)&layer1, 1); - services->setPixelShaderConstant("useNormalmap" , (irr::f32 *)&layer2, 1); + services->setPixelShaderConstant("textureFlags" , (irr::f32 *)&layer2, 1); #else + s32 layer0 = 0; + s32 layer1 = 1; + s32 layer2 = 2; services->setPixelShaderConstant("baseTexture" , (irr::s32 *)&layer0, 1); services->setPixelShaderConstant("normalTexture" , (irr::s32 *)&layer1, 1); - services->setPixelShaderConstant("useNormalmap" , (irr::s32 *)&layer2, 1); + services->setPixelShaderConstant("textureFlags" , (irr::s32 *)&layer2, 1); #endif } }; @@ -1040,27 +1135,6 @@ static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec, #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, - IWritableTextureSource *tsrc, IrrlichtDevice *device, - Client *client, std::string text) -{ - std::string formspec = - FORMSPEC_VERSION_STRING - SIZE_TAG - "field[3,2.35;6,0.5;f_text;;" + text + "]" - "button_exit[4,3;3,0.5;btn_send;" + wide_to_utf8(wstrgettext("Proceed")) + "]" - ; - - /* Create menu */ - /* Note: FormspecFormSource and LocalFormspecHandler - * are deleted by guiFormSpecMenu */ - FormspecFormSource *fs_src = new FormspecFormSource(formspec); - LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_CHAT_MENU", client); - - create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL); -} - static void show_deathscreen(GUIFormSpecMenu **cur_formspec, InventoryManager *invmgr, IGameDef *gamedef, IWritableTextureSource *tsrc, IrrlichtDevice *device, Client *client) @@ -1089,32 +1163,32 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, bool singleplayermode) { #ifdef __ANDROID__ - std::string control_text = wide_to_utf8(wstrgettext("Default Controls:\n" - "No menu visible:\n" - "- single tap: button activate\n" - "- double tap: place/use\n" - "- slide finger: look around\n" - "Menu/Inventory visible:\n" - "- double tap (outside):\n" - " -->close\n" - "- touch stack, touch slot:\n" - " --> move stack\n" - "- touch&drag, tap 2nd finger\n" - " --> place single item to slot\n" - )); + std::string control_text = strgettext("Default Controls:\n" + "No menu visible:\n" + "- single tap: button activate\n" + "- double tap: place/use\n" + "- slide finger: look around\n" + "Menu/Inventory visible:\n" + "- double tap (outside):\n" + " -->close\n" + "- touch stack, touch slot:\n" + " --> move stack\n" + "- touch&drag, tap 2nd finger\n" + " --> place single item to slot\n" + ); #else - std::string control_text = wide_to_utf8(wstrgettext("Default Controls:\n" - "- WASD: move\n" - "- Space: jump/climb\n" - "- Shift: sneak/go down\n" - "- Q: drop item\n" - "- I: inventory\n" - "- Mouse: turn/look\n" - "- Mouse left: dig/punch\n" - "- Mouse right: place/use\n" - "- Mouse wheel: select item\n" - "- T: chat\n" - )); + std::string control_text = strgettext("Default Controls:\n" + "- WASD: move\n" + "- Space: jump/climb\n" + "- Shift: sneak/go down\n" + "- Q: drop item\n" + "- I: inventory\n" + "- Mouse: turn/look\n" + "- Mouse left: dig/punch\n" + "- Mouse right: place/use\n" + "- Mouse wheel: select item\n" + "- T: chat\n" + ); #endif float ypos = singleplayermode ? 0.5 : 0.1; @@ -1122,23 +1196,23 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, os << FORMSPEC_VERSION_STRING << SIZE_TAG << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;" - << wide_to_utf8(wstrgettext("Continue")) << "]"; + << strgettext("Continue") << "]"; if (!singleplayermode) { os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;" - << wide_to_utf8(wstrgettext("Change Password")) << "]"; + << strgettext("Change Password") << "]"; } #ifndef __ANDROID__ os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;" - << wide_to_utf8(wstrgettext("Sound Volume")) << "]"; + << strgettext("Sound Volume") << "]"; os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;" - << wide_to_utf8(wstrgettext("Change Keys")) << "]"; + << strgettext("Change Keys") << "]"; #endif os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;" - << wide_to_utf8(wstrgettext("Exit to Menu")) << "]"; + << strgettext("Exit to Menu") << "]"; os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;" - << wide_to_utf8(wstrgettext("Exit to OS")) << "]" + << strgettext("Exit to OS") << "]" << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]" << "textarea[0.4,0.25;3.5,6;;" << PROJECT_NAME_C "\n" << g_build_info << "\n" @@ -1163,11 +1237,15 @@ static void updateChat(Client &client, f32 dtime, bool show_debug, 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); + static LogOutputBuffer chat_log_error_buf(g_logger, LL_ERROR); // Get new messages from error log buffer while (!chat_log_error_buf.empty()) { - chat_backend.addMessage(L"", utf8_to_wide(chat_log_error_buf.get())); + std::wstring error_message = utf8_to_wide(chat_log_error_buf.get()); + if (!g_settings->getBool("disable_escape_sequences")) { + error_message = L"\x1b(c@red)" + error_message + L"\x1b(c@white)"; + } + chat_backend.addMessage(L"", error_message); } // Get new messages from client @@ -1182,10 +1260,10 @@ 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(); + EnrichedString recent_chat = chat_backend.getRecentChat(); unsigned int line_height = g_fontengine->getLineHeight(); - guitext_chat->setText(recent_chat.c_str()); + setStaticText(guitext_chat, recent_chat); // Update gui element size and position s32 chat_y = 5 + line_height; @@ -1194,7 +1272,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(g_fontengine->getTextWidth(recent_chat) + 10, + s32 width = std::min(g_fontengine->getTextWidth(recent_chat.c_str()) + 10, porting::getWindowSize().X - 20); core::rect rect(10, chat_y, width, chat_y + porting::getWindowSize().Y); guitext_chat->setRelativePosition(rect); @@ -1224,7 +1302,11 @@ static void updateChat(Client &client, f32 dtime, bool show_debug, */ struct KeyCache { - KeyCache() { populate(); } + KeyCache() + { + handler = NULL; + populate(); + } enum { // Player movement @@ -1235,6 +1317,7 @@ struct KeyCache { KEYMAP_ID_JUMP, KEYMAP_ID_SPECIAL1, KEYMAP_ID_SNEAK, + KEYMAP_ID_AUTORUN, // Other KEYMAP_ID_DROP, @@ -1275,6 +1358,7 @@ struct KeyCache { void populate(); KeyPress key[KEYMAP_INTERNAL_ENUM_COUNT]; + InputHandler *handler; }; void KeyCache::populate() @@ -1287,6 +1371,8 @@ void KeyCache::populate() key[KEYMAP_ID_SPECIAL1] = getKeySetting("keymap_special1"); key[KEYMAP_ID_SNEAK] = getKeySetting("keymap_sneak"); + key[KEYMAP_ID_AUTORUN] = getKeySetting("keymap_autorun"); + key[KEYMAP_ID_DROP] = getKeySetting("keymap_drop"); key[KEYMAP_ID_INVENTORY] = getKeySetting("keymap_inventory"); key[KEYMAP_ID_CHAT] = getKeySetting("keymap_chat"); @@ -1323,6 +1409,19 @@ void KeyCache::populate() key[KEYMAP_ID_QUICKTUNE_DEC] = getKeySetting("keymap_quicktune_dec"); key[KEYMAP_ID_DEBUG_STACKS] = getKeySetting("keymap_print_debug_stacks"); + + if (handler) { + // First clear all keys, then re-add the ones we listen for + handler->dontListenForKeys(); + for (size_t i = 0; i < KEYMAP_INTERNAL_ENUM_COUNT; i++) { + handler->listenForKey(key[i]); + } + handler->listenForKey(EscapeKey); + handler->listenForKey(CancelKey); + for (size_t i = 0; i < 10; i++) { + handler->listenForKey(NumberKey[i]); + } + } } @@ -1417,8 +1516,7 @@ struct VolatileRunFlags { * hides most of the stuff in this class (nothing in this class is required * by any other file) but exposes the public methods/data only. */ -class Game -{ +class Game { public: Game(); ~Game(); @@ -1434,6 +1532,7 @@ class Game std::string *address, u16 port, std::string &error_message, + bool *reconnect, ChatBackend *chat_backend, const SubgameSpec &gamespec, // Used for local game bool simple_singleplayer_mode); @@ -1488,16 +1587,17 @@ class Game void dropSelectedItem(); void openInventory(); - void openConsole(); + void openConsole(float height, const wchar_t *line=NULL); void toggleFreeMove(float *statustext_time); void toggleFreeMoveAlt(float *statustext_time, float *jump_timer); void toggleFast(float *statustext_time); void toggleNoClip(float *statustext_time); void toggleCinematic(float *statustext_time); + void toggleAutorun(float *statustext_time); void toggleChat(float *statustext_time, bool *flag); void toggleHud(float *statustext_time, bool *flag); - void toggleMinimap(float *statustext_time, bool *flag1, bool *flag2, + void toggleMinimap(float *statustext_time, bool *flag, bool show_hud, bool shift_pressed); void toggleFog(float *statustext_time, bool *flag); void toggleDebug(float *statustext_time, bool *show_debug, @@ -1519,9 +1619,9 @@ class Game void updateCamera(VolatileRunFlags *flags, u32 busy_time, f32 dtime, float time_from_last_punch); void updateSound(f32 dtime); - void processPlayerInteraction(std::vector &highlight_boxes, - GameRunData *runData, f32 dtime, bool show_hud, + void processPlayerInteraction(GameRunData *runData, f32 dtime, bool show_hud, bool show_debug); + void handlePointingAtNothing(GameRunData *runData, const ItemStack &playerItem); void handlePointingAtNode(GameRunData *runData, const PointedThing &pointed, const ItemDefinition &playeritem_def, const ToolCapabilities &playeritem_toolcap, f32 dtime); @@ -1531,8 +1631,7 @@ class Game void handleDigging(GameRunData *runData, const PointedThing &pointed, const v3s16 &nodepos, const ToolCapabilities &playeritem_toolcap, f32 dtime); - void updateFrame(std::vector &highlight_boxes, ProfilerGraph *graph, - RunStats *stats, GameRunData *runData, + void updateFrame(ProfilerGraph *graph, RunStats *stats, GameRunData *runData, f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam); void updateGui(float *statustext_time, const RunStats &stats, const GameRunData& runData, f32 dtime, const VolatileRunFlags &flags, @@ -1545,6 +1644,13 @@ class Game void showOverlayMessage(const wchar_t *msg, float dtime, int percent, bool draw_clouds = true); + static void settingChangedCallback(const std::string &setting_name, void *data); + void readSettings(); + +#ifdef __ANDROID__ + void handleAndroidChatInput(); +#endif + private: InputHandler *input; @@ -1588,6 +1694,7 @@ class Game scene::ISceneManager *smgr; bool *kill; std::string *error_message; + bool *reconnect_requested; IGameDef *gamedef; // Convenience (same as *client) scene::ISceneNode *skybox; @@ -1615,17 +1722,13 @@ class Game 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; @@ -1634,8 +1737,8 @@ class Game #ifdef __ANDROID__ bool m_cache_hold_aux1; + bool m_android_chat_open; #endif - }; Game::Game() : @@ -1661,15 +1764,20 @@ Game::Game() : hud(NULL), mapper(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); + g_settings->registerChangedCallback("doubletap_jump", + &settingChangedCallback, this); + g_settings->registerChangedCallback("enable_clouds", + &settingChangedCallback, this); + g_settings->registerChangedCallback("enable_particles", + &settingChangedCallback, this); + g_settings->registerChangedCallback("enable_fog", + &settingChangedCallback, this); + g_settings->registerChangedCallback("mouse_sensitivity", + &settingChangedCallback, this); + g_settings->registerChangedCallback("repeat_rightclick_time", + &settingChangedCallback, this); + + readSettings(); #ifdef __ANDROID__ m_cache_hold_aux1 = false; // This is initialised properly later @@ -1704,6 +1812,19 @@ Game::~Game() delete draw_control; extendedResourceCleanup(); + + g_settings->deregisterChangedCallback("doubletap_jump", + &settingChangedCallback, this); + g_settings->deregisterChangedCallback("enable_clouds", + &settingChangedCallback, this); + g_settings->deregisterChangedCallback("enable_particles", + &settingChangedCallback, this); + g_settings->deregisterChangedCallback("enable_fog", + &settingChangedCallback, this); + g_settings->deregisterChangedCallback("mouse_sensitivity", + &settingChangedCallback, this); + g_settings->deregisterChangedCallback("repeat_rightclick_time", + &settingChangedCallback, this); } bool Game::startup(bool *kill, @@ -1716,19 +1837,24 @@ bool Game::startup(bool *kill, std::string *address, // can change if simple_singleplayer_mode u16 port, std::string &error_message, + bool *reconnect, ChatBackend *chat_backend, const SubgameSpec &gamespec, bool simple_singleplayer_mode) { // "cache" - this->device = device; - this->kill = kill; - this->error_message = &error_message; - this->random_input = random_input; - this->input = input; - this->chat_backend = chat_backend; + this->device = device; + this->kill = kill; + this->error_message = &error_message; + this->reconnect_requested = reconnect; + this->random_input = random_input; + this->input = input; + this->chat_backend = chat_backend; this->simple_singleplayer_mode = simple_singleplayer_mode; + keycache.handler = input; + keycache.populate(); + driver = device->getVideoDriver(); smgr = device->getSceneManager(); @@ -1778,8 +1904,6 @@ void Game::run() &runData.fog_range, client)); - std::vector highlight_boxes; - set_light_table(g_settings->getFloat("display_gamma")); #ifdef __ANDROID__ @@ -1787,7 +1911,9 @@ void Game::run() && client->checkPrivilege("fast"); #endif - while (device->run() && !(*kill || g_gamecallback->shutdown_requested)) { + while (device->run() + && !(*kill || g_gamecallback->shutdown_requested + || (server && server->getShutdownRequested()))) { /* Must be called immediately after a device->run() call because it * uses device->getTimer()->getTime() @@ -1827,17 +1953,23 @@ void Game::run() updateCamera(&flags, draw_times.busy_time, dtime, runData.time_from_last_punch); updateSound(dtime); - processPlayerInteraction(highlight_boxes, &runData, dtime, - flags.show_hud, flags.show_debug); - updateFrame(highlight_boxes, &graph, &stats, &runData, dtime, - flags, cam_view); + processPlayerInteraction(&runData, dtime, flags.show_hud, + flags.show_debug); + updateFrame(&graph, &stats, &runData, dtime, flags, cam_view); updateProfilerGraphs(&graph); + + // Update if minimap has been disabled by the server + flags.show_minimap &= !client->isMinimapDisabledByServer(); } } void Game::shutdown() { + if (g_settings->get("3d_mode") == "pageflip") { + driver->setRenderTarget(irr::video::ERT_STEREO_BOTH_BUFFERS); + } + showOverlayMessage(wgettext("Shutting down..."), 0, 0, false); if (clouds) @@ -2019,6 +2151,7 @@ bool Game::createClient(const std::string &playername, camera = new Camera(smgr, *draw_control, gamedef); if (!camera || !camera->successfullyCreated(*error_message)) return false; + client->setCamera(camera); /* Clouds */ @@ -2086,43 +2219,45 @@ bool Game::createClient(const std::string &playername, bool Game::initGui() { // First line of debug text - guitext = guienv->addStaticText( + guitext = addStaticText(guienv, utf8_to_wide(PROJECT_NAME_C).c_str(), core::rect(0, 0, 0, 0), false, false, guiroot); // Second line of debug text - guitext2 = guienv->addStaticText( + guitext2 = addStaticText(guienv, L"", core::rect(0, 0, 0, 0), false, false, guiroot); // At the middle of the screen // Object infos are shown in this - guitext_info = guienv->addStaticText( + guitext_info = addStaticText(guienv, L"", core::rect(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.) - guitext_status = guienv->addStaticText( + guitext_status = addStaticText(guienv, L"", core::rect(0, 0, 0, 0), false, false, guiroot); guitext_status->setVisible(false); // Chat text - guitext_chat = guienv->addStaticText( + guitext_chat = addStaticText( + guienv, L"", core::rect(0, 0, 0, 0), //false, false); // Disable word wrap as of now false, true, guiroot); + // Remove stale "recent" chat messages from previous connections chat_backend->clearRecentChat(); // Chat backend and console gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(), - -1, chat_backend, client); + -1, chat_backend, client, &g_menumgr); if (!gui_chat_console) { *error_message = "Could not allocate memory for chat console"; errorstream << *error_message << std::endl; @@ -2130,7 +2265,7 @@ bool Game::initGui() } // Profiler text (size is updated when text is updated) - guitext_profiler = guienv->addStaticText( + guitext_profiler = addStaticText(guienv, L"", core::rect(0, 0, 0, 0), false, false, guiroot); @@ -2141,7 +2276,7 @@ bool Game::initGui() #ifdef HAVE_TOUCHSCREENGUI if (g_touchscreengui) - g_touchscreengui->init(texture_src, porting::getDisplayDensity()); + g_touchscreengui->init(texture_src); #endif @@ -2239,6 +2374,7 @@ bool Game::connectToServer(const std::string &playername, if (client->accessDenied()) { *error_message = "Access denied. Reason: " + client->accessDeniedReason(); + *reconnect_requested = client->reconnectRequested(); errorstream << *error_message << std::endl; break; } @@ -2376,6 +2512,7 @@ inline bool Game::checkConnection() if (client->accessDenied()) { *error_message = "Access denied. Reason: " + client->accessDeniedReason(); + *reconnect_requested = client->reconnectRequested(); errorstream << *error_message << std::endl; return false; } @@ -2535,7 +2672,17 @@ void Game::processUserInput(VolatileRunFlags *flags, || noMenuActive() == false || guienv->hasFocus(gui_chat_console)) { input->clear(); +#ifdef HAVE_TOUCHSCREENGUI + g_touchscreengui->hide(); +#endif + } +#ifdef HAVE_TOUCHSCREENGUI + else if (g_touchscreengui) { + /* on touchscreengui step may generate own input events which ain't + * what we want in case we just did clear them */ + g_touchscreengui->step(dtime); } +#endif if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen()) { gui_chat_console->closeConsoleAtOnce(); @@ -2544,18 +2691,11 @@ void Game::processUserInput(VolatileRunFlags *flags, // Input handler step() (used by the random input generator) input->step(dtime); -#ifdef HAVE_TOUCHSCREENGUI - - if (g_touchscreengui) { - g_touchscreengui->step(dtime); - } - -#endif #ifdef __ANDROID__ - - if (current_formspec != 0) + if (current_formspec != NULL) current_formspec->getAndroidUIInput(); - + else + handleAndroidChatInput(); #endif // Increase timer for double tap of "keymap_jump" @@ -2586,19 +2726,21 @@ void Game::processKeyboardInput(VolatileRunFlags *flags, if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DROP])) { dropSelectedItem(); + } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_AUTORUN])) { + toggleAutorun(statustext_time); } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_INVENTORY])) { openInventory(); } else if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) { - show_pause_menu(¤t_formspec, client, gamedef, texture_src, device, - simple_singleplayer_mode); + if (!gui_chat_console->isOpenInhibited()) { + show_pause_menu(¤t_formspec, client, gamedef, + texture_src, device, simple_singleplayer_mode); + } } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CHAT])) { - show_chat_menu(¤t_formspec, client, gamedef, texture_src, device, - client, ""); + openConsole(0.2, L""); } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CMD])) { - show_chat_menu(¤t_formspec, client, gamedef, texture_src, device, - client, "/"); + openConsole(0.2, L"/"); } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CONSOLE])) { - openConsole(); + openConsole(1); } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_FREEMOVE])) { toggleFreeMove(statustext_time); } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP])) { @@ -2615,7 +2757,7 @@ void Game::processKeyboardInput(VolatileRunFlags *flags, } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_HUD])) { toggleHud(statustext_time, &flags->show_hud); } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_MINIMAP])) { - toggleMinimap(statustext_time, &flags->show_minimap, &flags->show_hud, + toggleMinimap(statustext_time, &flags->show_minimap, flags->show_hud, input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK])); } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_CHAT])) { toggleChat(statustext_time, &flags->show_chat); @@ -2633,19 +2775,19 @@ void Game::processKeyboardInput(VolatileRunFlags *flags, decreaseViewRange(statustext_time); } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_RANGESELECT])) { toggleFullViewRange(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_NEXT])) + } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_NEXT])) { quicktune->next(); - else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_PREV])) + } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_PREV])) { quicktune->prev(); - else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_INC])) + } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_INC])) { quicktune->inc(); - else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_DEC])) + } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_DEC])) { quicktune->dec(); - else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DEBUG_STACKS])) { + } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DEBUG_STACKS])) { // Print debug stacks dstream << "-----------------------------------------" << std::endl; - dstream << DTIME << "Printing debug stacks:" << std::endl; + dstream << "Printing debug stacks:" << std::endl; dstream << "-----------------------------------------" << std::endl; debug_stacks_print(); @@ -2741,15 +2883,32 @@ void Game::openInventory() } -void Game::openConsole() +void Game::openConsole(float height, const wchar_t *line) { - if (!gui_chat_console->isOpenInhibited()) { - // Open up to over half of the screen - gui_chat_console->openConsole(0.6); - guienv->setFocus(gui_chat_console); +#ifdef __ANDROID__ + porting::showInputDialog(gettext("ok"), "", "", 2); + m_android_chat_open = true; +#else + if (gui_chat_console->isOpenInhibited()) + return; + gui_chat_console->openConsole(height); + if (line) { + gui_chat_console->setCloseOnEnter(true); + gui_chat_console->replaceAndAddToHistory(line); } +#endif } +#ifdef __ANDROID__ +void Game::handleAndroidChatInput() +{ + if (m_android_chat_open && porting::getInputDialogState() == 0) { + std::string text = porting::getInputDialogValue(); + client->typeChatMessage(utf8_to_wide(text)); + } +} +#endif + void Game::toggleFreeMove(float *statustext_time) { @@ -2815,6 +2974,16 @@ void Game::toggleCinematic(float *statustext_time) statustext = msg[cinematic]; } +// Add WoW-style autorun by toggling continuous forward. +void Game::toggleAutorun(float *statustext_time) +{ + static const wchar_t *msg[] = { L"autorun disabled", L"autorun enabled" }; + bool autorun_enabled = !g_settings->getBool("continuous_forward"); + g_settings->set("continuous_forward", bool_to_cstr(autorun_enabled)); + + *statustext_time = 0; + statustext = msg[autorun_enabled ? 1 : 0]; +} void Game::toggleChat(float *statustext_time, bool *flag) { @@ -2833,47 +3002,56 @@ void Game::toggleHud(float *statustext_time, bool *flag) *flag = !*flag; *statustext_time = 0; statustext = msg[*flag]; - if (g_settings->getBool("enable_node_highlighting")) - client->setHighlighted(client->getHighlighted(), *flag); } -void Game::toggleMinimap(float *statustext_time, bool *flag, bool *show_hud, bool shift_pressed) +void Game::toggleMinimap(float *statustext_time, bool *flag, + bool show_hud, bool shift_pressed) { - if (*show_hud && g_settings->getBool("enable_minimap")) { - if (shift_pressed) { - mapper->toggleMinimapShape(); - return; - } - MinimapMode mode = mapper->getMinimapMode(); - mode = (MinimapMode)((int)(mode) + 1); - *flag = true; - switch (mode) { - case MINIMAP_MODE_SURFACEx1: - statustext = L"Minimap in surface mode, Zoom x1"; - break; - case MINIMAP_MODE_SURFACEx2: - statustext = L"Minimap in surface mode, Zoom x2"; - break; - case MINIMAP_MODE_SURFACEx4: - statustext = L"Minimap in surface mode, Zoom x4"; - break; - case MINIMAP_MODE_RADARx1: - statustext = L"Minimap in radar mode, Zoom x1"; - break; - case MINIMAP_MODE_RADARx2: - statustext = L"Minimap in radar mode, Zoom x2"; - break; - case MINIMAP_MODE_RADARx4: - statustext = L"Minimap in radar mode, Zoom x4"; - break; - default: - mode = MINIMAP_MODE_OFF; - *flag = false; - statustext = L"Minimap hidden"; - } - *statustext_time = 0; - mapper->setMinimapMode(mode); + if (!show_hud || !g_settings->getBool("enable_minimap")) + return; + + if (shift_pressed) { + mapper->toggleMinimapShape(); + return; + } + + u32 hud_flags = client->getEnv().getLocalPlayer()->hud_flags; + + MinimapMode mode = MINIMAP_MODE_OFF; + if (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) { + mode = mapper->getMinimapMode(); + mode = (MinimapMode)((int)mode + 1); + } + + *flag = true; + switch (mode) { + case MINIMAP_MODE_SURFACEx1: + statustext = L"Minimap in surface mode, Zoom x1"; + break; + case MINIMAP_MODE_SURFACEx2: + statustext = L"Minimap in surface mode, Zoom x2"; + break; + case MINIMAP_MODE_SURFACEx4: + statustext = L"Minimap in surface mode, Zoom x4"; + break; + case MINIMAP_MODE_RADARx1: + statustext = L"Minimap in radar mode, Zoom x1"; + break; + case MINIMAP_MODE_RADARx2: + statustext = L"Minimap in radar mode, Zoom x2"; + break; + case MINIMAP_MODE_RADARx4: + statustext = L"Minimap in radar mode, Zoom x4"; + break; + default: + mode = MINIMAP_MODE_OFF; + *flag = false; + statustext = (hud_flags & HUD_FLAG_MINIMAP_VISIBLE) ? + L"Minimap hidden" : L"Minimap disabled by server"; } + + *statustext_time = 0; + mapper->setMinimapMode(mode); } void Game::toggleFog(float *statustext_time, bool *flag) @@ -2944,10 +3122,10 @@ void Game::toggleProfiler(float *statustext_time, u32 *profiler_current_page, void Game::increaseViewRange(float *statustext_time) { - s16 range = g_settings->getS16("viewing_range_nodes_min"); + s16 range = g_settings->getS16("viewing_range"); s16 range_new = range + 10; - g_settings->set("viewing_range_nodes_min", itos(range_new)); - statustext = utf8_to_wide("Minimum viewing range changed to " + g_settings->set("viewing_range", itos(range_new)); + statustext = utf8_to_wide("Viewing range changed to " + itos(range_new)); *statustext_time = 0; } @@ -2955,14 +3133,14 @@ void Game::increaseViewRange(float *statustext_time) void Game::decreaseViewRange(float *statustext_time) { - s16 range = g_settings->getS16("viewing_range_nodes_min"); + s16 range = g_settings->getS16("viewing_range"); s16 range_new = range - 10; - if (range_new < 0) - range_new = range; + if (range_new < 20) + range_new = 20; - g_settings->set("viewing_range_nodes_min", itos(range_new)); - statustext = utf8_to_wide("Minimum viewing range changed to " + g_settings->set("viewing_range", itos(range_new)); + statustext = utf8_to_wide("Viewing range changed to " + itos(range_new)); *statustext_time = 0; } @@ -3409,8 +3587,8 @@ void Game::updateSound(f32 dtime) } -void Game::processPlayerInteraction(std::vector &highlight_boxes, - GameRunData *runData, f32 dtime, bool show_hud, bool show_debug) +void Game::processPlayerInteraction(GameRunData *runData, + f32 dtime, bool show_hud, bool show_debug) { LocalPlayer *player = client->getEnv().getLocalPlayer(); @@ -3468,25 +3646,17 @@ void Game::processPlayerInteraction(std::vector &highlight_boxes, PointedThing pointed = getPointedThing( // input - client, player_position, camera_direction, + client, hud, player_position, camera_direction, camera_position, shootline, d, playeritem_def.liquids_pointable, !runData->ldown_for_dig, camera_offset, // output - highlight_boxes, runData->selected_object); if (pointed != runData->pointed_old) { infostream << "Pointing at " << pointed.dump() << std::endl; - - if (m_cache_enable_node_highlighting) { - if (pointed.type == POINTEDTHING_NODE) { - client->setHighlighted(pointed.node_undersurface, show_hud); - } else { - client->setHighlighted(pointed.node_undersurface, false); - } - } + hud->updateSelectionMesh(camera_offset); } /* @@ -3510,6 +3680,7 @@ void Game::processPlayerInteraction(std::vector &highlight_boxes, infostream << "Pointing away from node" << " (stopped digging)" << std::endl; runData->digging = false; + hud->updateSelectionMesh(camera_offset); } } @@ -3547,6 +3718,8 @@ void Game::processPlayerInteraction(std::vector &highlight_boxes, } else if (input->getLeftState()) { // When button is held down in air, show continuous animation runData->left_punch = true; + } else if (input->getRightClicked()) { + handlePointingAtNothing(runData, playeritem); } runData->pointed_old = pointed; @@ -3562,6 +3735,15 @@ void Game::processPlayerInteraction(std::vector &highlight_boxes, } +void Game::handlePointingAtNothing(GameRunData *runData, const ItemStack &playerItem) +{ + infostream << "Right Clicked in Air" << std::endl; + PointedThing fauxPointed; + fauxPointed.type = POINTEDTHING_NOTHING; + client->interact(5, fauxPointed); +} + + void Game::handlePointingAtNode(GameRunData *runData, const PointedThing &pointed, const ItemDefinition &playeritem_def, const ToolCapabilities &playeritem_toolcap, f32 dtime) @@ -3577,7 +3759,7 @@ void Game::handlePointingAtNode(GameRunData *runData, NodeMetadata *meta = map.getNodeMetadata(nodepos); if (meta) { - infotext = utf8_to_wide(meta->getString("infotext")); + infotext = unescape_enriched(utf8_to_wide(meta->getString("infotext"))); } else { MapNode n = map.getNodeNoEx(nodepos); @@ -3633,11 +3815,15 @@ void Game::handlePointingAtNode(GameRunData *runData, } else { soundmaker->m_player_rightpunch_sound = SimpleSoundSpec(); - } - if (playeritem_def.node_placement_prediction == "" || - nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable) - client->interact(3, pointed); // Report to server + if (playeritem_def.node_placement_prediction == "" || + 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; + } + } } } } @@ -3649,10 +3835,15 @@ void Game::handlePointingAtObject(GameRunData *runData, const v3f &player_position, bool show_debug) { - infotext = utf8_to_wide(runData->selected_object->infoText()); + infotext = unescape_enriched( + utf8_to_wide(runData->selected_object->infoText())); - if (infotext == L"" && show_debug) { - infotext = utf8_to_wide(runData->selected_object->debugInfoText()); + if (show_debug) { + if (infotext != L"") { + infotext += L"\n"; + } + infotext += unescape_enriched(utf8_to_wide( + runData->selected_object->debugInfoText())); } if (input->getLeftState()) { @@ -3816,9 +4007,9 @@ void Game::handleDigging(GameRunData *runData, } -void Game::updateFrame(std::vector &highlight_boxes, - ProfilerGraph *graph, RunStats *stats, GameRunData *runData, - f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam) +void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, + GameRunData *runData, f32 dtime, const VolatileRunFlags &flags, + const CameraOrientation &cam) { LocalPlayer *player = client->getEnv().getLocalPlayer(); @@ -3829,12 +4020,7 @@ void Game::updateFrame(std::vector &highlight_boxes, if (draw_control->range_all) { runData->fog_range = 100000 * BS; } else { - runData->fog_range = draw_control->wanted_range * BS - + 0.0 * MAP_BLOCKSIZE * BS; - runData->fog_range = MYMIN( - runData->fog_range, - (draw_control->farthest_drawn + 20) * BS); - runData->fog_range *= 0.9; + runData->fog_range = 0.9 * draw_control->wanted_range * BS; } /* @@ -4010,7 +4196,7 @@ void Game::updateFrame(std::vector &highlight_boxes, } draw_scene(driver, smgr, *camera, *client, player, *hud, *mapper, - guienv, highlight_boxes, screensize, skycolor, flags.show_hud, + guienv, screensize, skycolor, flags.show_hud, flags.show_minimap); /* @@ -4117,12 +4303,12 @@ void Game::updateGui(float *statustext_time, const RunStats &stats, << ", v_range = " << draw_control->wanted_range << std::setprecision(3) << ", RTT = " << client->getRTT(); - guitext->setText(utf8_to_wide(os.str()).c_str()); + setStaticText(guitext, utf8_to_wide(os.str()).c_str()); guitext->setVisible(true); } else if (flags.show_hud || flags.show_chat) { std::ostringstream os(std::ios_base::binary); os << PROJECT_NAME_C " " << g_version_hash; - guitext->setText(utf8_to_wide(os.str()).c_str()); + setStaticText(guitext, utf8_to_wide(os.str()).c_str()); guitext->setVisible(true); } else { guitext->setVisible(false); @@ -4159,7 +4345,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats, } } - guitext2->setText(utf8_to_wide(os.str()).c_str()); + setStaticText(guitext2, utf8_to_wide(os.str()).c_str()); guitext2->setVisible(true); core::rect rect( @@ -4171,7 +4357,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats, guitext2->setVisible(false); } - guitext_info->setText(infotext.c_str()); + setStaticText(guitext_info, infotext.c_str()); guitext_info->setVisible(flags.show_hud && g_menumgr.menuCount() == 0); float statustext_time_max = 1.5; @@ -4185,7 +4371,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats, } } - guitext_status->setText(statustext.c_str()); + setStaticText(guitext_status, statustext.c_str()); guitext_status->setVisible(!statustext.empty()); if (!statustext.empty()) { @@ -4282,6 +4468,22 @@ void Game::showOverlayMessage(const wchar_t *msg, float dtime, delete[] msg; } +void Game::settingChangedCallback(const std::string &setting_name, void *data) +{ + ((Game *)data)->readSettings(); +} + +void Game::readSettings() +{ + m_cache_doubletap_jump = g_settings->getBool("doubletap_jump"); + 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); +} /****************************************************************************/ /**************************************************************************** @@ -4330,6 +4532,7 @@ void the_game(bool *kill, std::string &error_message, ChatBackend &chat_backend, + bool *reconnect_requested, const SubgameSpec &gamespec, // Used for local game bool simple_singleplayer_mode) { @@ -4344,21 +4547,17 @@ void the_game(bool *kill, try { if (game.startup(kill, random_input, input, device, map_dir, - playername, password, &server_address, port, - error_message, &chat_backend, gamespec, + playername, password, &server_address, port, error_message, + reconnect_requested, &chat_backend, gamespec, simple_singleplayer_mode)) { game.run(); game.shutdown(); } } catch (SerializationError &e) { - error_message = strgettext("A serialization error occurred:") - + "\n" + e.what(); - if (!simple_singleplayer_mode) { - error_message += "\n\n" - + strgettext("The server is probably running a different version of") - + " " PROJECT_NAME_C "."; - } + error_message = std::string("A serialization error occurred:\n") + + e.what() + "\n\nThe server is probably " + " running a different version of " PROJECT_NAME_C "."; errorstream << error_message << std::endl; } catch (ServerError &e) { error_message = e.what(); @@ -4368,4 +4567,3 @@ void the_game(bool *kill, errorstream << "ModError: " << error_message << std::endl; } } -