X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fgame.cpp;h=10ec5d59471a66542903759d934deb7c046e1037;hb=454483f26610959526d97c1d318e0d699d78747d;hp=25b4048a38599bdd6452da6e7f14a14e3b683bf7;hpb=ff924ef0dcd90eb227f772e15a1a3b01f7cc0745;p=oweals%2Fminetest.git diff --git a/src/game.cpp b/src/game.cpp index 25b4048a3..10ec5d594 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -23,6 +23,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "camera.h" #include "client.h" #include "client/tile.h" // For TextureSource +#include "client/keys.h" +#include "client/joystick_controller.h" #include "clientmap.h" #include "clouds.h" #include "config.h" @@ -39,8 +41,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "guiKeyChangeMenu.h" #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,7 +56,11 @@ 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 "script/clientscripting.h" #include "sound.h" @@ -84,14 +88,14 @@ struct TextDestNodeMetadata : public TextDest { // This is deprecated I guess? -celeron55 void gotText(std::wstring text) { - std::string ntext = wide_to_narrow(text); + std::string ntext = wide_to_utf8(text); infostream << "Submitting 'text' field of node at (" << m_p.X << "," << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl; - std::map fields; + StringMap fields; fields["text"] = ntext; m_client->sendNodemetaFields(m_p, "", fields); } - void gotText(std::map fields) + void gotText(const StringMap &fields) { m_client->sendNodemetaFields(m_p, "", fields); } @@ -111,7 +115,7 @@ struct TextDestPlayerInventory : public TextDest { m_client = client; m_formname = formname; } - void gotText(std::map fields) + void gotText(const StringMap &fields) { m_client->sendInventoryFields(m_formname, fields); } @@ -121,6 +125,7 @@ struct TextDestPlayerInventory : public TextDest { struct LocalFormspecHandler : public TextDest { LocalFormspecHandler(); + LocalFormspecHandler(std::string formname) : m_client(0) { @@ -138,7 +143,7 @@ struct LocalFormspecHandler : public TextDest { errorstream << "LocalFormspecHandler::gotText old style message received" << std::endl; } - void gotText(std::map fields) + void gotText(const StringMap &fields) { if (m_formname == "MT_PAUSE_MENU") { if (fields.find("btn_sound") != fields.end()) { @@ -175,49 +180,8 @@ 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())) { - if (fields.find("f_text") != fields.end()) { - m_client->typeChatMessage(narrow_to_wide(fields["f_text"])); - } - - return; - } - } - - if (m_formname == "MT_DEATH_SCREEN") { - assert(m_client != 0); - - if ((fields.find("btn_respawn") != fields.end())) { - m_client->sendRespawn(); - return; - } - - if (fields.find("quit") != fields.end()) { - m_client->sendRespawn(); - return; - } - } - - // don't show error message for unhandled cursor keys - if ((fields.find("key_up") != fields.end()) || - (fields.find("key_down") != fields.end()) || - (fields.find("key_left") != fields.end()) || - (fields.find("key_right") != fields.end())) { - return; - } - - errorstream << "LocalFormspecHandler::gotText unhandled >" << m_formname << "< event" << std::endl; - int i = 0; - - for (std::map::iterator iter = fields.begin(); - iter != fields.end(); iter++) { - errorstream << "\t" << i << ": " << iter->first << "=" << iter->second << std::endl; - i++; - } + // Don't disable this part when modding is disabled, it's used in builtin + m_client->getScript()->on_formspec_input(m_formname, fields); } Client *m_client; @@ -272,165 +236,6 @@ public: Client *m_client; }; -/* - Check if a node is pointable -*/ -inline bool isPointableNode(const MapNode &n, - Client *client, bool liquids_pointable) -{ - const ContentFeatures &features = client->getNodeDefManager()->get(n); - return features.pointable || - (liquids_pointable && features.isLiquid()); -} - -/* - 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 result; - - hilightboxes.clear(); - selected_object = NULL; - - INodeDefManager *nodedef = client->getNodeDefManager(); - ClientMap &map = client->getEnv().getClientMap(); - - f32 mindistance = BS * 1001; - - // First try to find a pointed at active object - if (look_for_object) { - selected_object = client->getSelectedActiveObject(d * BS, - camera_position, shootline); - - if (selected_object != NULL) { - if (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))); - } - - mindistance = (selected_object->getPosition() - camera_position).getLength(); - - result.type = POINTEDTHING_OBJECT; - result.object_id = selected_object->getId(); - } - } - - // That didn't work, try to find a pointed at node - - - v3s16 pos_i = floatToInt(player_position, BS); - - /*infostream<<"pos_i=("< 0 ? a : 1); - s16 zend = pos_i.Z + (camera_direction.Z > 0 ? a : 1); - s16 xend = pos_i.X + (camera_direction.X > 0 ? a : 1); - - // Prevent signed number overflow - if (yend == 32767) - yend = 32766; - - if (zend == 32767) - zend = 32766; - - if (xend == 32767) - xend = 32766; - - 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; - - n = map.getNodeNoEx(v3s16(x, y, z), &is_valid_position); - if (!is_valid_position) - continue; - - if (!isPointableNode(n, client, liquids_pointable)) - continue; - - std::vector boxes = n.getSelectionBoxes(nodedef); - - v3s16 np(x, y, z); - v3f npf = intToFloat(np, 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; - - 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); - } - } - } - } - } // for coords - - return result; -} - /* Profiler display */ void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, FontEngine *fe, @@ -442,8 +247,8 @@ 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 = narrow_to_wide(os.str()); - guitext_profiler->setText(text.c_str()); + std::wstring text = utf8_to_wide(os.str()); + setStaticText(guitext_profiler, text.c_str()); guitext_profiler->setVisible(true); s32 w = fe->getTextWidth(text.c_str()); @@ -487,7 +292,7 @@ private: color(color) {} }; - std::vector m_log; + std::deque m_log; public: u32 m_log_max_size; @@ -508,18 +313,19 @@ public: void draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver, gui::IGUIFont *font) const { + // Do *not* use UNORDERED_MAP here as the order needs + // to be the same for each call to prevent flickering 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 = - m_meta.find(id); + std::map::iterator j = m_meta.find(id); if (j == m_meta.end()) { m_meta[id] = Meta(value); @@ -547,7 +353,7 @@ public: 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); @@ -563,7 +369,7 @@ public: 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; @@ -579,16 +385,16 @@ public: s32 texth = 15; char buf[10]; snprintf(buf, 10, "%.3g", show_max); - font->draw(narrow_to_wide(buf).c_str(), + font->draw(utf8_to_wide(buf).c_str(), core::rect(textx, y - graphh, textx2, y - graphh + texth), meta.color); snprintf(buf, 10, "%.3g", show_min); - font->draw(narrow_to_wide(buf).c_str(), + font->draw(utf8_to_wide(buf).c_str(), core::rect(textx, y - texth, textx2, y), meta.color); - font->draw(narrow_to_wide(id).c_str(), + font->draw(utf8_to_wide(id).c_str(), core::rect(textx, y - graphh / 2 - texth / 2, textx2, y - graphh / 2 + texth / 2), meta.color); @@ -598,8 +404,8 @@ public: 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; @@ -773,7 +579,7 @@ public: return; m_fetched.insert(name); - std::string base = porting::path_share + DIR_DELIM + "testsounds"; + std::string base = porting::path_share + DIR_DELIM + "sounds"; dst_paths.insert(base + DIR_DELIM + name + ".ogg"); dst_paths.insert(base + DIR_DELIM + name + ".0.ogg"); dst_paths.insert(base + DIR_DELIM + name + ".1.ogg"); @@ -788,40 +594,73 @@ public: } }; + +// before 1.8 there isn't a "integer interface", only float +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) +typedef f32 SamplerLayer_t; +#else +typedef s32 SamplerLayer_t; +#endif + + class GameGlobalShaderConstantSetter : public IShaderConstantSetter { Sky *m_sky; bool *m_force_fog_off; f32 *m_fog_range; + bool m_fog_enabled; + CachedPixelShaderSetting m_sky_bg_color; + CachedPixelShaderSetting m_fog_distance; + CachedVertexShaderSetting m_animation_timer_vertex; + CachedPixelShaderSetting m_animation_timer_pixel; + CachedPixelShaderSetting m_day_light; + CachedPixelShaderSetting m_eye_position_pixel; + CachedVertexShaderSetting m_eye_position_vertex; + CachedPixelShaderSetting m_minimap_yaw; + CachedPixelShaderSetting m_base_texture; + CachedPixelShaderSetting m_normal_texture; + CachedPixelShaderSetting m_texture_flags; Client *m_client; - bool m_fogEnabled; public: void onSettingsChange(const std::string &name) { if (name == "enable_fog") - m_fogEnabled = g_settings->getBool("enable_fog"); + m_fog_enabled = g_settings->getBool("enable_fog"); } - static void SettingsCallback(const std::string name, void *userdata) + static void settingsCallback(const std::string &name, void *userdata) { reinterpret_cast(userdata)->onSettingsChange(name); } + void setSky(Sky *sky) { m_sky = sky; } + GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off, f32 *fog_range, Client *client) : m_sky(sky), m_force_fog_off(force_fog_off), m_fog_range(fog_range), + m_sky_bg_color("skyBgColor"), + m_fog_distance("fogDistance"), + m_animation_timer_vertex("animationTimer"), + m_animation_timer_pixel("animationTimer"), + m_day_light("dayLight"), + m_eye_position_pixel("eyePosition"), + m_eye_position_vertex("eyePosition"), + m_minimap_yaw("yawVec"), + m_base_texture("baseTexture"), + m_normal_texture("normalTexture"), + m_texture_flags("textureFlags"), m_client(client) { - g_settings->registerChangedCallback("enable_fog", SettingsCallback, this); - m_fogEnabled = g_settings->getBool("enable_fog"); + g_settings->registerChangedCallback("enable_fog", settingsCallback, this); + m_fog_enabled = g_settings->getBool("enable_fog"); } ~GameGlobalShaderConstantSetter() { - g_settings->deregisterChangedCallback("enable_fog", SettingsCallback, this); + g_settings->deregisterChangedCallback("enable_fog", settingsCallback, this); } virtual void onSetConstants(video::IMaterialRendererServices *services, @@ -839,48 +678,98 @@ public: bgcolorf.b, bgcolorf.a, }; - services->setPixelShaderConstant("skyBgColor", bgcolorfa, 4); + m_sky_bg_color.set(bgcolorfa, services); // Fog distance float fog_distance = 10000 * BS; - if (m_fogEnabled && !*m_force_fog_off) + if (m_fog_enabled && !*m_force_fog_off) fog_distance = *m_fog_range; - services->setPixelShaderConstant("fogDistance", &fog_distance, 1); + m_fog_distance.set(&fog_distance, services); - // Day-night ratio - u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); - float daynight_ratio_f = (float)daynight_ratio / 1000.0; - services->setPixelShaderConstant("dayNightRatio", &daynight_ratio_f, 1); + u32 daynight_ratio = (float)m_client->getEnv().getDayNightRatio(); + video::SColorf sunlight; + get_sunlight_color(&sunlight, daynight_ratio); + float dnc[3] = { + sunlight.r, + sunlight.g, + sunlight.b }; + m_day_light.set(dnc, services); u32 animation_timer = porting::getTimeMs() % 100000; - float animation_timer_f = (float)animation_timer / 100000.0; - services->setPixelShaderConstant("animationTimer", &animation_timer_f, 1); - services->setVertexShaderConstant("animationTimer", &animation_timer_f, 1); + 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); - LocalPlayer *player = m_client->getEnv().getLocalPlayer(); - v3f eye_position = player->getEyePosition(); - services->setPixelShaderConstant("eyePosition", (irr::f32 *)&eye_position, 3); - services->setVertexShaderConstant("eyePosition", (irr::f32 *)&eye_position, 3); - - // Uniform sampler layers - int layer0 = 0; - int layer1 = 1; - int layer2 = 2; - // before 1.8 there isn't a "integer interface", only float + float eye_position_array[3]; + v3f epos = m_client->getEnv().getLocalPlayer()->getEyePosition(); #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) - services->setPixelShaderConstant("baseTexture" , (irr::f32 *)&layer0, 1); - services->setPixelShaderConstant("normalTexture" , (irr::f32 *)&layer1, 1); - services->setPixelShaderConstant("useNormalmap" , (irr::f32 *)&layer2, 1); + eye_position_array[0] = epos.X; + eye_position_array[1] = epos.Y; + eye_position_array[2] = epos.Z; #else - services->setPixelShaderConstant("baseTexture" , (irr::s32 *)&layer0, 1); - services->setPixelShaderConstant("normalTexture" , (irr::s32 *)&layer1, 1); - services->setPixelShaderConstant("useNormalmap" , (irr::s32 *)&layer2, 1); + epos.getAs3Values(eye_position_array); #endif + m_eye_position_pixel.set(eye_position_array, services); + m_eye_position_vertex.set(eye_position_array, services); + + float minimap_yaw_array[3]; + v3f minimap_yaw = m_client->getMapper()->getYawVec(); +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) + minimap_yaw_array[0] = minimap_yaw.X; + minimap_yaw_array[1] = minimap_yaw.Y; + minimap_yaw_array[2] = minimap_yaw.Z; +#else + minimap_yaw.getAs3Values(minimap_yaw_array); +#endif + m_minimap_yaw.set(minimap_yaw_array, services); + + SamplerLayer_t base_tex = 0, + normal_tex = 1, + flags_tex = 2; + m_base_texture.set(&base_tex, services); + m_normal_texture.set(&normal_tex, services); + m_texture_flags.set(&flags_tex, services); + } +}; + + +class GameGlobalShaderConstantSetterFactory : public IShaderConstantSetterFactory +{ + Sky *m_sky; + bool *m_force_fog_off; + f32 *m_fog_range; + Client *m_client; + std::vector created_nosky; +public: + GameGlobalShaderConstantSetterFactory(bool *force_fog_off, + f32 *fog_range, Client *client) : + m_sky(NULL), + m_force_fog_off(force_fog_off), + m_fog_range(fog_range), + m_client(client) + {} + + void setSky(Sky *sky) { + m_sky = sky; + for (size_t i = 0; i < created_nosky.size(); ++i) { + created_nosky[i]->setSky(m_sky); + } + created_nosky.clear(); + } + + virtual IShaderConstantSetter* create() + { + GameGlobalShaderConstantSetter *scs = new GameGlobalShaderConstantSetter( + m_sky, m_force_fog_off, m_fog_range, m_client); + if (!m_sky) + created_nosky.push_back(scs); + return scs; } }; + bool nodePlacementPrediction(Client &client, const ItemDefinition &playeritem_def, v3s16 nodepos, v3s16 neighbourpos) { @@ -928,7 +817,8 @@ bool nodePlacementPrediction(Client &client, // Predict param2 for facedir and wallmounted nodes u8 param2 = 0; - if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED) { + if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED || + nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED) { v3s16 dir = nodepos - neighbourpos; if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) { @@ -940,7 +830,8 @@ bool nodePlacementPrediction(Client &client, } } - if (nodedef->get(id).param_type_2 == CPT2_FACEDIR) { + if (nodedef->get(id).param_type_2 == CPT2_FACEDIR || + nodedef->get(id).param_type_2 == CPT2_COLORED_FACEDIR) { v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS); if (abs(dir.X) > abs(dir.Z)) { @@ -964,7 +855,8 @@ bool nodePlacementPrediction(Client &client, }; v3s16 pp; - if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED) + if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED || + nodedef->get(id).param_type_2 == CPT2_COLORED_WALLMOUNTED) pp = p + wallmounted_dirs[param2]; else pp = p + v3s16(0, -1, 0); @@ -1004,14 +896,14 @@ bool nodePlacementPrediction(Client &client, } static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec, - InventoryManager *invmgr, IGameDef *gamedef, - IWritableTextureSource *tsrc, IrrlichtDevice *device, - IFormSource *fs_src, TextDest *txt_dest, Client *client) + Client *client, IrrlichtDevice *device, JoystickController *joystick, + IFormSource *fs_src, TextDest *txt_dest) { if (*cur_formspec == 0) { - *cur_formspec = new GUIFormSpecMenu(device, guiroot, -1, &g_menumgr, - invmgr, gamedef, tsrc, fs_src, txt_dest, client); + *cur_formspec = new GUIFormSpecMenu(device, joystick, + guiroot, -1, &g_menumgr, client, client->getTextureSource(), + fs_src, txt_dest); (*cur_formspec)->doPause = false; /* @@ -1026,6 +918,7 @@ static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec, (*cur_formspec)->setFormSource(fs_src); (*cur_formspec)->setTextDest(txt_dest); } + } #ifdef __ANDROID__ @@ -1034,81 +927,39 @@ 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_narrow(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) -{ - std::string formspec = - std::string(FORMSPEC_VERSION_STRING) + - SIZE_TAG - "bgcolor[#320000b4;true]" - "label[4.85,1.35;" + gettext("You died.") + "]" - "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]" - ; - - /* Create menu */ - /* Note: FormspecFormSource and LocalFormspecHandler - * are deleted by guiFormSpecMenu */ - FormspecFormSource *fs_src = new FormspecFormSource(formspec); - LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client); - - create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL); -} - /******************************************************************************/ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, - InventoryManager *invmgr, IGameDef *gamedef, + Client *client, IWritableTextureSource *tsrc, IrrlichtDevice *device, - bool singleplayermode) + JoystickController *joystick, bool singleplayermode) { #ifdef __ANDROID__ - std::string control_text = wide_to_narrow(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_narrow(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; @@ -1116,25 +967,25 @@ 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_narrow(wstrgettext("Continue")) << "]"; + << strgettext("Continue") << "]"; if (!singleplayermode) { os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;" - << wide_to_narrow(wstrgettext("Change Password")) << "]"; + << strgettext("Change Password") << "]"; } #ifndef __ANDROID__ os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;" - << wide_to_narrow(wstrgettext("Sound Volume")) << "]"; + << strgettext("Sound Volume") << "]"; os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;" - << wide_to_narrow(wstrgettext("Change Keys")) << "]"; + << strgettext("Change Keys") << "]"; #endif os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;" - << wide_to_narrow(wstrgettext("Exit to Menu")) << "]"; + << strgettext("Exit to Menu") << "]"; os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;" - << wide_to_narrow(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 "\n" + << "textarea[0.4,0.25;3.5,6;;" << PROJECT_NAME_C "\n" << g_build_info << "\n" << "path_user = " << wrap_rows(porting::path_user, 20) << "\n;]"; @@ -1145,8 +996,9 @@ static void show_pause_menu(GUIFormSpecMenu **cur_formspec, FormspecFormSource *fs_src = new FormspecFormSource(os.str()); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); - create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL); - (*cur_formspec)->setFocus(L"btn_continue"); + create_formspec_menu(cur_formspec, client, device, joystick, fs_src, txt_dst); + std::string con("btn_continue"); + (*cur_formspec)->setFocus(con); (*cur_formspec)->doPause = true; } @@ -1156,11 +1008,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"", narrow_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 @@ -1175,19 +1031,19 @@ 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; + s32 chat_y = 5; if (show_debug) - chat_y += line_height; + chat_y += 2 * 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); @@ -1217,103 +1073,89 @@ static void updateChat(Client &client, f32 dtime, bool show_debug, */ struct KeyCache { - KeyCache() { populate(); } - - enum { - // Player movement - KEYMAP_ID_FORWARD, - KEYMAP_ID_BACKWARD, - KEYMAP_ID_LEFT, - KEYMAP_ID_RIGHT, - KEYMAP_ID_JUMP, - KEYMAP_ID_SPECIAL1, - KEYMAP_ID_SNEAK, - - // Other - KEYMAP_ID_DROP, - KEYMAP_ID_INVENTORY, - KEYMAP_ID_CHAT, - KEYMAP_ID_CMD, - KEYMAP_ID_CONSOLE, - KEYMAP_ID_FREEMOVE, - KEYMAP_ID_FASTMOVE, - KEYMAP_ID_NOCLIP, - KEYMAP_ID_CINEMATIC, - KEYMAP_ID_SCREENSHOT, - KEYMAP_ID_TOGGLE_HUD, - KEYMAP_ID_TOGGLE_CHAT, - KEYMAP_ID_TOGGLE_FORCE_FOG_OFF, - KEYMAP_ID_TOGGLE_UPDATE_CAMERA, - KEYMAP_ID_TOGGLE_DEBUG, - KEYMAP_ID_TOGGLE_PROFILER, - KEYMAP_ID_CAMERA_MODE, - KEYMAP_ID_INCREASE_VIEWING_RANGE, - KEYMAP_ID_DECREASE_VIEWING_RANGE, - KEYMAP_ID_RANGESELECT, - - KEYMAP_ID_QUICKTUNE_NEXT, - KEYMAP_ID_QUICKTUNE_PREV, - KEYMAP_ID_QUICKTUNE_INC, - KEYMAP_ID_QUICKTUNE_DEC, - - KEYMAP_ID_DEBUG_STACKS, - - // Fake keycode for array size and internal checks - KEYMAP_INTERNAL_ENUM_COUNT - - - }; + KeyCache() + { + handler = NULL; + populate(); + populate_nonchanging(); + } void populate(); - KeyPress key[KEYMAP_INTERNAL_ENUM_COUNT]; + // Keys that are not settings dependent + void populate_nonchanging(); + + KeyPress key[KeyType::INTERNAL_ENUM_COUNT]; + InputHandler *handler; }; +void KeyCache::populate_nonchanging() +{ + key[KeyType::ESC] = EscapeKey; +} + void KeyCache::populate() { - key[KEYMAP_ID_FORWARD] = getKeySetting("keymap_forward"); - key[KEYMAP_ID_BACKWARD] = getKeySetting("keymap_backward"); - key[KEYMAP_ID_LEFT] = getKeySetting("keymap_left"); - key[KEYMAP_ID_RIGHT] = getKeySetting("keymap_right"); - key[KEYMAP_ID_JUMP] = getKeySetting("keymap_jump"); - key[KEYMAP_ID_SPECIAL1] = getKeySetting("keymap_special1"); - key[KEYMAP_ID_SNEAK] = getKeySetting("keymap_sneak"); - - key[KEYMAP_ID_DROP] = getKeySetting("keymap_drop"); - key[KEYMAP_ID_INVENTORY] = getKeySetting("keymap_inventory"); - key[KEYMAP_ID_CHAT] = getKeySetting("keymap_chat"); - key[KEYMAP_ID_CMD] = getKeySetting("keymap_cmd"); - key[KEYMAP_ID_CONSOLE] = getKeySetting("keymap_console"); - key[KEYMAP_ID_FREEMOVE] = getKeySetting("keymap_freemove"); - key[KEYMAP_ID_FASTMOVE] = getKeySetting("keymap_fastmove"); - key[KEYMAP_ID_NOCLIP] = getKeySetting("keymap_noclip"); - key[KEYMAP_ID_CINEMATIC] = getKeySetting("keymap_cinematic"); - key[KEYMAP_ID_SCREENSHOT] = getKeySetting("keymap_screenshot"); - key[KEYMAP_ID_TOGGLE_HUD] = getKeySetting("keymap_toggle_hud"); - key[KEYMAP_ID_TOGGLE_CHAT] = getKeySetting("keymap_toggle_chat"); - key[KEYMAP_ID_TOGGLE_FORCE_FOG_OFF] + key[KeyType::FORWARD] = getKeySetting("keymap_forward"); + key[KeyType::BACKWARD] = getKeySetting("keymap_backward"); + key[KeyType::LEFT] = getKeySetting("keymap_left"); + key[KeyType::RIGHT] = getKeySetting("keymap_right"); + key[KeyType::JUMP] = getKeySetting("keymap_jump"); + key[KeyType::SPECIAL1] = getKeySetting("keymap_special1"); + key[KeyType::SNEAK] = getKeySetting("keymap_sneak"); + + key[KeyType::AUTORUN] = getKeySetting("keymap_autorun"); + + key[KeyType::DROP] = getKeySetting("keymap_drop"); + key[KeyType::INVENTORY] = getKeySetting("keymap_inventory"); + key[KeyType::CHAT] = getKeySetting("keymap_chat"); + key[KeyType::CMD] = getKeySetting("keymap_cmd"); + key[KeyType::CONSOLE] = getKeySetting("keymap_console"); + key[KeyType::MINIMAP] = getKeySetting("keymap_minimap"); + key[KeyType::FREEMOVE] = getKeySetting("keymap_freemove"); + key[KeyType::FASTMOVE] = getKeySetting("keymap_fastmove"); + key[KeyType::NOCLIP] = getKeySetting("keymap_noclip"); + key[KeyType::CINEMATIC] = getKeySetting("keymap_cinematic"); + key[KeyType::SCREENSHOT] = getKeySetting("keymap_screenshot"); + key[KeyType::TOGGLE_HUD] = getKeySetting("keymap_toggle_hud"); + key[KeyType::TOGGLE_CHAT] = getKeySetting("keymap_toggle_chat"); + key[KeyType::TOGGLE_FORCE_FOG_OFF] = getKeySetting("keymap_toggle_force_fog_off"); - key[KEYMAP_ID_TOGGLE_UPDATE_CAMERA] + key[KeyType::TOGGLE_UPDATE_CAMERA] = getKeySetting("keymap_toggle_update_camera"); - key[KEYMAP_ID_TOGGLE_DEBUG] + key[KeyType::TOGGLE_DEBUG] = getKeySetting("keymap_toggle_debug"); - key[KEYMAP_ID_TOGGLE_PROFILER] + key[KeyType::TOGGLE_PROFILER] = getKeySetting("keymap_toggle_profiler"); - key[KEYMAP_ID_CAMERA_MODE] + key[KeyType::CAMERA_MODE] = getKeySetting("keymap_camera_mode"); - key[KEYMAP_ID_INCREASE_VIEWING_RANGE] + key[KeyType::INCREASE_VIEWING_RANGE] = getKeySetting("keymap_increase_viewing_range_min"); - key[KEYMAP_ID_DECREASE_VIEWING_RANGE] + key[KeyType::DECREASE_VIEWING_RANGE] = getKeySetting("keymap_decrease_viewing_range_min"); - key[KEYMAP_ID_RANGESELECT] + key[KeyType::RANGESELECT] = getKeySetting("keymap_rangeselect"); + key[KeyType::ZOOM] = getKeySetting("keymap_zoom"); - key[KEYMAP_ID_QUICKTUNE_NEXT] = getKeySetting("keymap_quicktune_next"); - key[KEYMAP_ID_QUICKTUNE_PREV] = getKeySetting("keymap_quicktune_prev"); - key[KEYMAP_ID_QUICKTUNE_INC] = getKeySetting("keymap_quicktune_inc"); - key[KEYMAP_ID_QUICKTUNE_DEC] = getKeySetting("keymap_quicktune_dec"); + key[KeyType::QUICKTUNE_NEXT] = getKeySetting("keymap_quicktune_next"); + key[KeyType::QUICKTUNE_PREV] = getKeySetting("keymap_quicktune_prev"); + key[KeyType::QUICKTUNE_INC] = getKeySetting("keymap_quicktune_inc"); + key[KeyType::QUICKTUNE_DEC] = getKeySetting("keymap_quicktune_dec"); - key[KEYMAP_ID_DEBUG_STACKS] = getKeySetting("keymap_print_debug_stacks"); + key[KeyType::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 < KeyType::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]); + } + } } @@ -1389,6 +1231,7 @@ struct VolatileRunFlags { bool invert_mouse; bool show_chat; bool show_hud; + bool show_minimap; bool force_fog_off; bool show_debug; bool show_profiler_graph; @@ -1407,8 +1250,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(); @@ -1424,6 +1266,7 @@ public: 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); @@ -1466,9 +1309,10 @@ protected: f32 dtime); void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime); + // Input related void processUserInput(VolatileRunFlags *flags, GameRunData *runData, f32 dtime); - void processKeyboardInput(VolatileRunFlags *flags, + void processKeyInput(VolatileRunFlags *flags, float *statustext_time, float *jump_timer, bool *reset_jump_timer, @@ -1478,18 +1322,21 @@ protected: void dropSelectedItem(); void openInventory(); - void openConsole(); + void openConsole(float scale, 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 *flag, bool show_hud, + bool shift_pressed); void toggleFog(float *statustext_time, bool *flag); void toggleDebug(float *statustext_time, bool *show_debug, - bool *show_profiler_graph); + bool *show_profiler_graph, bool *show_wireframe); void toggleUpdateCamera(float *statustext_time, bool *flag); void toggleProfiler(float *statustext_time, u32 *profiler_current_page, u32 profiler_max_page); @@ -1498,18 +1345,36 @@ protected: void decreaseViewRange(float *statustext_time); void toggleFullViewRange(float *statustext_time); - void updateCameraDirection(CameraOrientation *cam, VolatileRunFlags *flags); + void updateCameraDirection(CameraOrientation *cam, VolatileRunFlags *flags, + float dtime); void updateCameraOrientation(CameraOrientation *cam, - const VolatileRunFlags &flags); + const VolatileRunFlags &flags, float dtime); void updatePlayerControl(const CameraOrientation &cam); void step(f32 *dtime); void processClientEvents(CameraOrientation *cam, float *damage_flash); 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); + /*! + * Returns the object or node the player is pointing at. + * Also updates the selected thing in the Hud. + * + * @param[in] shootline the shootline, starting from + * the camera position. This also gives the maximal distance + * of the search. + * @param[in] liquids_pointable if false, liquids are ignored + * @param[in] look_for_object if false, objects are ignored + * @param[in] camera_offset offset of the camera + * @param[out] selected_object the selected object or + * NULL if not found + */ + PointedThing updatePointedThing( + const core::line3d &shootline, bool liquids_pointable, + bool look_for_object, const v3s16 &camera_offset, + ClientActiveObject *&selected_object); + void handlePointingAtNothing(GameRunData *runData, const ItemStack &playerItem); void handlePointingAtNode(GameRunData *runData, const PointedThing &pointed, const ItemDefinition &playeritem_def, const ToolCapabilities &playeritem_toolcap, f32 dtime); @@ -1519,8 +1384,7 @@ protected: 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, @@ -1533,6 +1397,48 @@ protected: 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(); + + inline bool getLeftClicked() + { + return input->getLeftClicked() || + input->joystick.getWasKeyDown(KeyType::MOUSE_L); + } + inline bool getRightClicked() + { + return input->getRightClicked() || + input->joystick.getWasKeyDown(KeyType::MOUSE_R); + } + inline bool isLeftPressed() + { + return input->getLeftState() || + input->joystick.isKeyDown(KeyType::MOUSE_L); + } + inline bool isRightPressed() + { + return input->getRightState() || + input->joystick.isKeyDown(KeyType::MOUSE_R); + } + inline bool getLeftReleased() + { + return input->getLeftReleased() || + input->joystick.wasKeyReleased(KeyType::MOUSE_L); + } + + inline bool isKeyDown(GameKeyType k) + { + return input->isKeyDown(keycache.key[k]) || input->joystick.isKeyDown(k); + } + inline bool wasKeyDown(GameKeyType k) + { + return input->wasKeyDown(keycache.key[k]) || input->joystick.wasKeyDown(k); + } + +#ifdef __ANDROID__ + void handleAndroidChatInput(); +#endif + private: InputHandler *input; @@ -1554,6 +1460,8 @@ private: ChatBackend *chat_backend; GUIFormSpecMenu *current_formspec; + //default: "". If other than "", empty show_formspec packets will only close the formspec when the formname matches + std::string cur_formname; EventManager *eventmgr; QuicktuneShortcutter *quicktune; @@ -1565,6 +1473,10 @@ private: Sky *sky; // Free using ->Drop() Inventory *local_inventory; Hud *hud; + Mapper *mapper; + + GameRunData runData; + VolatileRunFlags flags; /* 'cache' This class does take ownership/responsibily for cleaning up etc of any of @@ -1575,7 +1487,7 @@ private: scene::ISceneManager *smgr; bool *kill; std::string *error_message; - IGameDef *gamedef; // Convenience (same as *client) + bool *reconnect_requested; scene::ISceneNode *skybox; bool random_input; @@ -1602,22 +1514,29 @@ private: 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_joysticks; bool m_cache_enable_particles; bool m_cache_enable_fog; + bool m_cache_enable_noclip; + bool m_cache_enable_free_move; f32 m_cache_mouse_sensitivity; + f32 m_cache_joystick_frustum_sensitivity; f32 m_repeat_right_click_time; + f32 m_cache_cam_smoothing; + f32 m_cache_fog_start; + +#ifdef __ANDROID__ + bool m_cache_hold_aux1; + bool m_android_chat_open; +#endif }; Game::Game() : @@ -1632,6 +1551,7 @@ Game::Game() : soundmaker(NULL), chat_backend(NULL), current_formspec(NULL), + cur_formname(""), eventmgr(NULL), quicktune(NULL), gui_chat_console(NULL), @@ -1640,17 +1560,42 @@ Game::Game() : clouds(NULL), sky(NULL), local_inventory(NULL), - hud(NULL) + 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"); + g_settings->registerChangedCallback("doubletap_jump", + &settingChangedCallback, this); + g_settings->registerChangedCallback("enable_clouds", + &settingChangedCallback, this); + g_settings->registerChangedCallback("doubletap_joysticks", + &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("joystick_frustum_sensitivity", + &settingChangedCallback, this); + g_settings->registerChangedCallback("repeat_rightclick_time", + &settingChangedCallback, this); + g_settings->registerChangedCallback("noclip", + &settingChangedCallback, this); + g_settings->registerChangedCallback("free_move", + &settingChangedCallback, this); + g_settings->registerChangedCallback("cinematic", + &settingChangedCallback, this); + g_settings->registerChangedCallback("cinematic_camera_smoothing", + &settingChangedCallback, this); + g_settings->registerChangedCallback("camera_smoothing", + &settingChangedCallback, this); + + readSettings(); + +#ifdef __ANDROID__ + m_cache_hold_aux1 = false; // This is initialised properly later +#endif - m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0); } @@ -1680,6 +1625,29 @@ 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); + g_settings->deregisterChangedCallback("noclip", + &settingChangedCallback, this); + g_settings->deregisterChangedCallback("free_move", + &settingChangedCallback, this); + g_settings->deregisterChangedCallback("cinematic", + &settingChangedCallback, this); + g_settings->deregisterChangedCallback("cinematic_camera_smoothing", + &settingChangedCallback, this); + g_settings->deregisterChangedCallback("camera_smoothing", + &settingChangedCallback, this); } bool Game::startup(bool *kill, @@ -1692,24 +1660,41 @@ 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(); smgr->getParameters()->setAttribute(scene::OBJ_LOADER_IGNORE_MATERIAL_FILES, true); + memset(&runData, 0, sizeof(runData)); + runData.time_from_last_punch = 10.0; + runData.profiler_max_page = 3; + runData.update_wielded_item_trigger = true; + + memset(&flags, 0, sizeof(flags)); + flags.show_chat = true; + flags.show_hud = true; + flags.show_debug = g_settings->getBool("show_debug"); + flags.invert_mouse = g_settings->getBool("invert_mouse"); + flags.first_loop_after_window_activation = true; + if (!init(map_dir, address, port, gamespec)) return false; @@ -1726,38 +1711,25 @@ void Game::run() RunStats stats = { 0 }; CameraOrientation cam_view_target = { 0 }; CameraOrientation cam_view = { 0 }; - GameRunData runData = { 0 }; FpsControl draw_times = { 0 }; - VolatileRunFlags flags = { 0 }; f32 dtime; // in seconds - runData.time_from_last_punch = 10.0; - runData.profiler_max_page = 3; - runData.update_wielded_item_trigger = true; - - flags.show_chat = true; - flags.show_hud = true; - flags.show_debug = g_settings->getBool("show_debug"); - flags.invert_mouse = g_settings->getBool("invert_mouse"); - flags.first_loop_after_window_activation = true; - /* Clear the profiler */ Profiler::GraphValues dummyvalues; g_profiler->graphGet(dummyvalues); draw_times.last_time = device->getTimer()->getTime(); - shader_src->addGlobalConstantSetter(new GameGlobalShaderConstantSetter( - sky, - &flags.force_fog_off, - &runData.fog_range, - client)); - - std::vector highlight_boxes; - set_light_table(g_settings->getFloat("display_gamma")); - while (device->run() && !(*kill || g_gamecallback->shutdown_requested)) { +#ifdef __ANDROID__ + m_cache_hold_aux1 = g_settings->getBool("fast_move") + && client->checkPrivilege("fast"); +#endif + + 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() @@ -1780,34 +1752,36 @@ void Game::run() updateProfilers(runData, stats, draw_times, dtime); processUserInput(&flags, &runData, dtime); // Update camera before player movement to avoid camera lag of one frame - updateCameraDirection(&cam_view_target, &flags); - float cam_smoothing = 0; - if (g_settings->getBool("cinematic")) - cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing"); - else - cam_smoothing = 1 - g_settings->getFloat("camera_smoothing"); - cam_smoothing = rangelim(cam_smoothing, 0.01f, 1.0f); + updateCameraDirection(&cam_view_target, &flags, dtime); cam_view.camera_yaw += (cam_view_target.camera_yaw - - cam_view.camera_yaw) * cam_smoothing; + cam_view.camera_yaw) * m_cache_cam_smoothing; cam_view.camera_pitch += (cam_view_target.camera_pitch - - cam_view.camera_pitch) * cam_smoothing; + cam_view.camera_pitch) * m_cache_cam_smoothing; updatePlayerControl(cam_view); step(&dtime); - processClientEvents(&cam_view, &runData.damage_flash); + processClientEvents(&cam_view_target, &runData.damage_flash); 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 IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR <= 8 + if (g_settings->get("3d_mode") == "pageflip") { + driver->setRenderTarget(irr::video::ERT_STEREO_BOTH_BUFFERS); + } +#endif + showOverlayMessage(wgettext("Shutting down..."), 0, 0, false); if (clouds) @@ -1981,14 +1955,19 @@ bool Game::createClient(const std::string &playername, return false; } + GameGlobalShaderConstantSetterFactory *scsf = new GameGlobalShaderConstantSetterFactory( + &flags.force_fog_off, &runData.fog_range, client); + shader_src->addShaderConstantSetterFactory(scsf); + // Update cached textures, meshes and materials client->afterContentReceived(device); /* Camera */ - camera = new Camera(smgr, *draw_control, gamedef); + camera = new Camera(smgr, *draw_control, client); if (!camera || !camera->successfullyCreated(*error_message)) return false; + client->setCamera(camera); /* Clouds */ @@ -2004,6 +1983,7 @@ bool Game::createClient(const std::string &playername, /* Skybox */ sky = new Sky(smgr->getRootSceneNode(), smgr, -1, texture_src); + scsf->setSky(sky); skybox = NULL; // This is used/set later on in the main run loop local_inventory = new Inventory(itemdef_manager); @@ -2029,7 +2009,9 @@ bool Game::createClient(const std::string &playername, /* Set window caption */ - std::wstring str = narrow_to_wide(PROJECT_NAME); + std::wstring str = utf8_to_wide(PROJECT_NAME_C); + str += L" "; + str += utf8_to_wide(g_version_hash); str += L" ["; str += driver->getName(); str += L"]"; @@ -2039,7 +2021,7 @@ bool Game::createClient(const std::string &playername, player->hurt_tilt_timer = 0; player->hurt_tilt_strength = 0; - hud = new Hud(driver, smgr, guienv, gamedef, player, local_inventory); + hud = new Hud(driver, smgr, guienv, client, player, local_inventory); if (!hud) { *error_message = "Memory error: could not create HUD"; @@ -2047,49 +2029,54 @@ bool Game::createClient(const std::string &playername, return false; } + mapper = client->getMapper(); + mapper->setMinimapMode(MINIMAP_MODE_OFF); + return true; } bool Game::initGui() { // First line of debug text - guitext = guienv->addStaticText( - narrow_to_wide(PROJECT_NAME).c_str(), + 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; @@ -2097,7 +2084,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); @@ -2108,7 +2095,7 @@ bool Game::initGui() #ifdef HAVE_TOUCHSCREENGUI if (g_touchscreengui) - g_touchscreengui->init(texture_src, porting::getDisplayDensity()); + g_touchscreengui->init(texture_src); #endif @@ -2164,8 +2151,6 @@ bool Game::connectToServer(const std::string &playername, if (!client) return false; - gamedef = client; // Client acts as our GameDef - infostream << "Connecting to server at "; connect_address.print(&infostream); infostream << std::endl; @@ -2181,7 +2166,12 @@ bool Game::connectToServer(const std::string &playername, input->clear(); FpsControl fps_control = { 0 }; - f32 dtime; // in seconds + f32 dtime; + f32 wait_time = 0; // in seconds + + fps_control.last_time = device->getTimer()->getTime(); + + client->initMods(); while (device->run()) { @@ -2203,16 +2193,44 @@ 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; } - if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) { + if (wasKeyDown(KeyType::ESC) || input->wasKeyDown(CancelKey)) { *aborted = true; infostream << "Connect aborted [Escape]" << std::endl; break; } + wait_time += dtime; + // Only time out if we aren't waiting for the server we started + if ((*address != "") && (wait_time > 10)) { + bool sent_old_init = g_settings->getFlag("send_pre_v25_init"); + // If no pre v25 init was sent, and no answer was received, + // but the low level connection could be established + // (meaning that we have a peer id), then we probably wanted + // to connect to a legacy server. In this case, tell the user + // to enable the option to be able to connect. + if (!sent_old_init && + (client->getProtoVersion() == 0) && + client->connectedToServer()) { + *error_message = "Connection failure: init packet not " + "recognized by server.\n" + "Most likely the server uses an old protocol version ( Network -> Support older Servers'\n" + "entry in the advanced settings menu."; + } else { + *error_message = "Connection timed out."; + } + errorstream << *error_message << std::endl; + break; + } + // Update status showOverlayMessage(wgettext("Connecting to server..."), dtime, 20); } @@ -2232,6 +2250,8 @@ bool Game::getServerContent(bool *aborted) FpsControl fps_control = { 0 }; f32 dtime; // in seconds + fps_control.last_time = device->getTimer()->getTime(); + while (device->run()) { limitFps(&fps_control, &dtime); @@ -2258,7 +2278,7 @@ bool Game::getServerContent(bool *aborted) return false; } - if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) { + if (wasKeyDown(KeyType::ESC) || input->wasKeyDown(CancelKey)) { *aborted = true; infostream << "Connect aborted [Escape]" << std::endl; return false; @@ -2285,18 +2305,18 @@ bool Game::getServerContent(bool *aborted) if ((USE_CURL == 0) || (!g_settings->getBool("enable_remote_media_server"))) { float cur = client->getCurRate(); - std::string cur_unit = gettext(" KB/s"); + std::string cur_unit = gettext("KiB/s"); if (cur > 900) { cur /= 1024.0; - cur_unit = gettext(" MB/s"); + cur_unit = gettext("MiB/s"); } - message << " ( " << cur << cur_unit << " )"; + message << " (" << cur << ' ' << cur_unit << ")"; } progress = 30 + client->mediaReceiveProgress() * 35 + 0.5; - draw_load_screen(narrow_to_wide(message.str()), device, + draw_load_screen(utf8_to_wide(message.str()), device, guienv, dtime, progress); } } @@ -2330,6 +2350,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; } @@ -2355,7 +2376,7 @@ inline bool Game::handleCallbacks() if (g_gamecallback->changevolume_requested) { (new GUIVolumeChange(guienv, guiroot, -1, - &g_menumgr, client))->drop(); + &g_menumgr))->drop(); g_gamecallback->changevolume_requested = false; } @@ -2377,7 +2398,7 @@ inline bool Game::handleCallbacks() void Game::processQueues() { texture_src->processQueue(); - itemdef_manager->processQueue(gamedef); + itemdef_manager->processQueue(client); shader_src->processQueue(); } @@ -2489,7 +2510,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(); @@ -2498,25 +2529,18 @@ 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" if (m_cache_doubletap_jump && runData->jump_timer <= 0.2) runData->jump_timer += dtime; - processKeyboardInput( + processKeyInput( flags, &runData->statustext_time, &runData->jump_timer, @@ -2528,7 +2552,7 @@ void Game::processUserInput(VolatileRunFlags *flags, } -void Game::processKeyboardInput(VolatileRunFlags *flags, +void Game::processKeyInput(VolatileRunFlags *flags, float *statustext_time, float *jump_timer, bool *reset_jump_timer, @@ -2538,71 +2562,79 @@ void Game::processKeyboardInput(VolatileRunFlags *flags, //TimeTaker tt("process kybd input", NULL, PRECISION_NANO); - if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DROP])) { + if (wasKeyDown(KeyType::DROP)) { dropSelectedItem(); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_INVENTORY])) { + } else if (wasKeyDown(KeyType::AUTORUN)) { + toggleAutorun(statustext_time); + } else if (wasKeyDown(KeyType::INVENTORY)) { openInventory(); - } else if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) { - show_pause_menu(¤t_formspec, client, gamedef, texture_src, device, + } else if (wasKeyDown(KeyType::ESC) || input->wasKeyDown(CancelKey)) { + if (!gui_chat_console->isOpenInhibited()) { + show_pause_menu(¤t_formspec, client, + texture_src, device, &input->joystick, simple_singleplayer_mode); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CHAT])) { - show_chat_menu(¤t_formspec, client, gamedef, texture_src, device, - client, ""); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CMD])) { - show_chat_menu(¤t_formspec, client, gamedef, texture_src, device, - client, "/"); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CONSOLE])) { - openConsole(); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_FREEMOVE])) { + } + } else if (wasKeyDown(KeyType::CHAT)) { + openConsole(0.2, L""); + } else if (wasKeyDown(KeyType::CMD)) { + openConsole(0.2, L"/"); + } else if (wasKeyDown(KeyType::CONSOLE)) { + openConsole(core::clamp( + g_settings->getFloat("console_height"), 0.1f, 1.0f)); + } else if (wasKeyDown(KeyType::FREEMOVE)) { toggleFreeMove(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP])) { + } else if (wasKeyDown(KeyType::JUMP)) { toggleFreeMoveAlt(statustext_time, jump_timer); *reset_jump_timer = true; - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_FASTMOVE])) { + } else if (wasKeyDown(KeyType::FASTMOVE)) { toggleFast(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_NOCLIP])) { + } else if (wasKeyDown(KeyType::NOCLIP)) { toggleNoClip(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CINEMATIC])) { + } else if (wasKeyDown(KeyType::CINEMATIC)) { toggleCinematic(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_SCREENSHOT])) { + } else if (wasKeyDown(KeyType::SCREENSHOT)) { client->makeScreenshot(device); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_HUD])) { + } else if (wasKeyDown(KeyType::TOGGLE_HUD)) { toggleHud(statustext_time, &flags->show_hud); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_CHAT])) { + } else if (wasKeyDown(KeyType::MINIMAP)) { + toggleMinimap(statustext_time, &flags->show_minimap, flags->show_hud, + isKeyDown(KeyType::SNEAK)); + } else if (wasKeyDown(KeyType::TOGGLE_CHAT)) { toggleChat(statustext_time, &flags->show_chat); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_FORCE_FOG_OFF])) { + } else if (wasKeyDown(KeyType::TOGGLE_FORCE_FOG_OFF)) { toggleFog(statustext_time, &flags->force_fog_off); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_UPDATE_CAMERA])) { + } else if (wasKeyDown(KeyType::TOGGLE_UPDATE_CAMERA)) { toggleUpdateCamera(statustext_time, &flags->disable_camera_update); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_DEBUG])) { - toggleDebug(statustext_time, &flags->show_debug, &flags->show_profiler_graph); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_PROFILER])) { + } else if (wasKeyDown(KeyType::TOGGLE_DEBUG)) { + toggleDebug(statustext_time, &flags->show_debug, &flags->show_profiler_graph, + &draw_control->show_wireframe); + } else if (wasKeyDown(KeyType::TOGGLE_PROFILER)) { toggleProfiler(statustext_time, profiler_current_page, profiler_max_page); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_INCREASE_VIEWING_RANGE])) { + } else if (wasKeyDown(KeyType::INCREASE_VIEWING_RANGE)) { increaseViewRange(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DECREASE_VIEWING_RANGE])) { + } else if (wasKeyDown(KeyType::DECREASE_VIEWING_RANGE)) { decreaseViewRange(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_RANGESELECT])) { + } else if (wasKeyDown(KeyType::RANGESELECT)) { toggleFullViewRange(statustext_time); - } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_NEXT])) + } else if (wasKeyDown(KeyType::QUICKTUNE_NEXT)) { quicktune->next(); - else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_PREV])) + } else if (wasKeyDown(KeyType::QUICKTUNE_PREV)) { quicktune->prev(); - else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_INC])) + } else if (wasKeyDown(KeyType::QUICKTUNE_INC)) { quicktune->inc(); - else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_DEC])) + } else if (wasKeyDown(KeyType::QUICKTUNE_DEC)) { quicktune->dec(); - else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DEBUG_STACKS])) { + } else if (wasKeyDown(KeyType::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(); } - if (!input->isKeyDown(getKeySetting("keymap_jump")) && *reset_jump_timer) { + if (!isKeyDown(KeyType::JUMP) && *reset_jump_timer) { *reset_jump_timer = false; *jump_timer = 0.0; } @@ -2611,12 +2643,11 @@ void Game::processKeyboardInput(VolatileRunFlags *flags, if (quicktune->hasMessage()) { std::string msg = quicktune->getMessage(); - statustext = narrow_to_wide(msg); + statustext = utf8_to_wide(msg); *statustext_time = 0; } } - void Game::processItemSelection(u16 *new_playeritem) { LocalPlayer *player = client->getEnv().getLocalPlayer(); @@ -2627,14 +2658,23 @@ void Game::processItemSelection(u16 *new_playeritem) s32 wheel = input->getMouseWheel(); u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1, - player->hud_hotbar_itemcount - 1); + player->hud_hotbar_itemcount - 1); + + s32 dir = wheel; + + if (input->joystick.wasKeyDown(KeyType::SCROLL_DOWN)) { + dir = -1; + } - if (wheel < 0) + if (input->joystick.wasKeyDown(KeyType::SCROLL_UP)) { + dir = 1; + } + + if (dir < 0) *new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0; - else if (wheel > 0) + else if (dir > 0) *new_playeritem = *new_playeritem > 0 ? *new_playeritem - 1 : max_item; - // else wheel == 0 - + // else dir == 0 /* Item selection using keyboard */ @@ -2683,8 +2723,8 @@ void Game::openInventory() PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client); TextDest *txt_dst = new TextDestPlayerInventory(client); - create_formspec_menu(¤t_formspec, client, gamedef, texture_src, - device, fs_src, txt_dst, client); + create_formspec_menu(¤t_formspec, client, device, &input->joystick, fs_src, txt_dst); + cur_formname = ""; InventoryLocation inventoryloc; inventoryloc.setCurrentPlayer(); @@ -2692,14 +2732,33 @@ void Game::openInventory() } -void Game::openConsole() +void Game::openConsole(float scale, const wchar_t *line) +{ + assert(scale > 0.0f && scale <= 1.0f); + +#ifdef __ANDROID__ + porting::showInputDialog(gettext("ok"), "", "", 2); + m_android_chat_open = true; +#else + if (gui_chat_console->isOpenInhibited()) + return; + gui_chat_console->openConsole(scale); + if (line) { + gui_chat_console->setCloseOnEnter(true); + gui_chat_console->replaceAndAddToHistory(line); + } +#endif +} + +#ifdef __ANDROID__ +void Game::handleAndroidChatInput() { - if (!gui_chat_console->isOpenInhibited()) { - // Open up to over half of the screen - gui_chat_console->openConsole(0.6); - guienv->setFocus(gui_chat_console); + 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) @@ -2732,8 +2791,14 @@ void Game::toggleFast(float *statustext_time) *statustext_time = 0; statustext = msg[fast_move]; - if (fast_move && !client->checkPrivilege("fast")) + bool has_fast_privs = client->checkPrivilege("fast"); + + if (fast_move && !has_fast_privs) statustext += L" (note: no 'fast' privilege)"; + +#ifdef __ANDROID__ + m_cache_hold_aux1 = fast_move && has_fast_privs; +#endif } @@ -2760,6 +2825,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) { @@ -2778,10 +2853,57 @@ 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) +{ + 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) { @@ -2794,22 +2916,33 @@ void Game::toggleFog(float *statustext_time, bool *flag) void Game::toggleDebug(float *statustext_time, bool *show_debug, - bool *show_profiler_graph) + bool *show_profiler_graph, bool *show_wireframe) { - // Initial / 3x toggle: Chat only + // Initial / 4x toggle: Chat only // 1x toggle: Debug text with chat // 2x toggle: Debug text with profiler graph + // 3x toggle: Debug text and wireframe if (!*show_debug) { *show_debug = true; *show_profiler_graph = false; + *show_wireframe = false; statustext = L"Debug info shown"; - } else if (*show_profiler_graph) { - *show_debug = false; - *show_profiler_graph = false; - statustext = L"Debug info and profiler graph hidden"; - } else { + } else if (!*show_profiler_graph && !*show_wireframe) { *show_profiler_graph = true; statustext = L"Profiler graph shown"; + } else if (!*show_wireframe && client->checkPrivilege("debug")) { + *show_profiler_graph = false; + *show_wireframe = true; + statustext = L"Wireframe shown"; + } else { + *show_debug = false; + *show_profiler_graph = false; + *show_wireframe = false; + if (client->checkPrivilege("debug")) { + statustext = L"Debug info, profiler graph, and wireframe hidden"; + } else { + statustext = L"Debug info and profiler graph hidden"; + } } *statustext_time = 0; } @@ -2851,26 +2984,36 @@ 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 = narrow_to_wide("Minimum viewing range changed to " - + itos(range_new)); + + if (range_new > 4000) { + range_new = 4000; + statustext = utf8_to_wide("Viewing range is at maximum: " + + itos(range_new)); + } else { + statustext = utf8_to_wide("Viewing range changed to " + + itos(range_new)); + } + g_settings->set("viewing_range", itos(range_new)); *statustext_time = 0; } 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; - - g_settings->set("viewing_range_nodes_min", itos(range_new)); - statustext = narrow_to_wide("Minimum viewing range changed to " - + itos(range_new)); + if (range_new < 20) { + range_new = 20; + statustext = utf8_to_wide("Viewing range is at minimum: " + + itos(range_new)); + } else { + statustext = utf8_to_wide("Viewing range changed to " + + itos(range_new)); + } + g_settings->set("viewing_range", itos(range_new)); *statustext_time = 0; } @@ -2890,7 +3033,7 @@ void Game::toggleFullViewRange(float *statustext_time) void Game::updateCameraDirection(CameraOrientation *cam, - VolatileRunFlags *flags) + VolatileRunFlags *flags, float dtime) { if ((device->isWindowActive() && noMenuActive()) || random_input) { @@ -2905,7 +3048,7 @@ void Game::updateCameraDirection(CameraOrientation *cam, if (flags->first_loop_after_window_activation) flags->first_loop_after_window_activation = false; else - updateCameraOrientation(cam, *flags); + updateCameraOrientation(cam, *flags, dtime); input->setMousePos((driver->getScreenSize().Width / 2), (driver->getScreenSize().Height / 2)); @@ -2923,16 +3066,16 @@ void Game::updateCameraDirection(CameraOrientation *cam, } } - void Game::updateCameraOrientation(CameraOrientation *cam, - const VolatileRunFlags &flags) + const VolatileRunFlags &flags, float dtime) { #ifdef HAVE_TOUCHSCREENGUI if (g_touchscreengui) { - cam->camera_yaw = g_touchscreengui->getYaw(); - cam->camera_pitch = g_touchscreengui->getPitch(); + cam->camera_yaw += g_touchscreengui->getYawChange(); + cam->camera_pitch = g_touchscreengui->getPitch(); } else { #endif + s32 dx = input->getMousePos().X - (driver->getScreenSize().Width / 2); s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height / 2); @@ -2948,6 +3091,14 @@ void Game::updateCameraOrientation(CameraOrientation *cam, } #endif + if (m_cache_enable_joysticks) { + f32 c = m_cache_joystick_frustum_sensitivity * (1.f / 32767.f) * dtime; + cam->camera_yaw -= input->joystick.getAxisWithoutDead(JA_FRUSTUM_HORIZONTAL) * + c; + cam->camera_pitch += input->joystick.getAxisWithoutDead(JA_FRUSTUM_VERTICAL) * + c; + } + cam->camera_pitch = rangelim(cam->camera_pitch, -89.5, 89.5); } @@ -2956,42 +3107,46 @@ void Game::updatePlayerControl(const CameraOrientation &cam) { //TimeTaker tt("update player control", NULL, PRECISION_NANO); + // DO NOT use the isKeyDown method for the forward, backward, left, right + // buttons, as the code that uses the controls needs to be able to + // distinguish between the two in order to know when to use joysticks. + PlayerControl control( - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD]), - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]), - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT]), - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT]), - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP]), - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]), - input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]), - input->getLeftState(), - input->getRightState(), + input->isKeyDown(keycache.key[KeyType::FORWARD]), + input->isKeyDown(keycache.key[KeyType::BACKWARD]), + input->isKeyDown(keycache.key[KeyType::LEFT]), + input->isKeyDown(keycache.key[KeyType::RIGHT]), + isKeyDown(KeyType::JUMP), + isKeyDown(KeyType::SPECIAL1), + isKeyDown(KeyType::SNEAK), + isKeyDown(KeyType::ZOOM), + isLeftPressed(), + isRightPressed(), cam.camera_pitch, - cam.camera_yaw + cam.camera_yaw, + input->joystick.getAxisWithoutDead(JA_SIDEWARD_MOVE), + input->joystick.getAxisWithoutDead(JA_FORWARD_MOVE) ); u32 keypress_bits = - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD]) & 0x1) << 0) | - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]) & 0x1) << 1) | - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT]) & 0x1) << 2) | - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT]) & 0x1) << 3) | - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP]) & 0x1) << 4) | - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]) & 0x1) << 5) | - ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]) & 0x1) << 6) | - ( (u32)(input->getLeftState() & 0x1) << 7) | - ( (u32)(input->getRightState() & 0x1) << 8 + ( (u32)(isKeyDown(KeyType::FORWARD) & 0x1) << 0) | + ( (u32)(isKeyDown(KeyType::BACKWARD) & 0x1) << 1) | + ( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) | + ( (u32)(isKeyDown(KeyType::RIGHT) & 0x1) << 3) | + ( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) | + ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) | + ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | + ( (u32)(isLeftPressed() & 0x1) << 7) | + ( (u32)(isRightPressed() & 0x1) << 8 ); #ifdef ANDROID - /* For Android, invert the meaning of holding down the fast button (i.e. - * holding down the fast button -- if there is one -- means walk), unless - * performing an action, sneaking or jumping. + /* For Android, simulate holding down AUX1 (fast move) if the user has + * the fast_move setting toggled on. If there is an aux1 key defined for + * Android then its meaning is inverted (i.e. holding aux1 means walk and + * not fast) */ - const u32 autofast_exludebits = - (1U << 4) | (1U << 6) // jump, sneak - | (1U << 7) | (1U << 8); // left state, right state - - if ((keypress_bits & autofast_exludebits) == 0) { + if (m_cache_hold_aux1) { control.aux1 = control.aux1 ^ true; keypress_bits ^= ((u32)(1U << 5)); } @@ -3032,28 +3187,26 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) for ( ; event.type != CE_NONE; event = client->getClientEvent()) { - if (event.type == CE_PLAYER_DAMAGE && - client->getHP() != 0) { - //u16 damage = event.player_damage.amount; - //infostream<<"Player damage: "<getHP() != 0) { + if (client->moddingEnabled()) { + client->getScript()->on_damage_taken(event.player_damage.amount); + } - *damage_flash += 100.0; - *damage_flash += 8.0 * event.player_damage.amount; + *damage_flash += 95.0 + 3.2 * event.player_damage.amount; + *damage_flash = MYMIN(*damage_flash, 127.0); player->hurt_tilt_timer = 1.5; - player->hurt_tilt_strength = event.player_damage.amount / 4; - player->hurt_tilt_strength = rangelim(player->hurt_tilt_strength, 1.0, 4.0); + player->hurt_tilt_strength = + rangelim(event.player_damage.amount / 4, 1.0, 4.0); MtEvent *e = new SimpleTriggerEvent("PlayerDamage"); - gamedef->event()->put(e); + client->event()->put(e); } else if (event.type == CE_PLAYER_FORCE_MOVE) { cam->camera_yaw = event.player_force_move.yaw; cam->camera_pitch = event.player_force_move.pitch; } else if (event.type == CE_DEATHSCREEN) { - show_deathscreen(¤t_formspec, client, gamedef, texture_src, - device, client); - - chat_backend->addMessage(L"", L"You died."); + // This should be enabled for death formspec in builtin + client->getScript()->on_death(); /* Handle visualization */ *damage_flash = 0; @@ -3061,20 +3214,34 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) player->hurt_tilt_strength = 0; } else if (event.type == CE_SHOW_FORMSPEC) { - FormspecFormSource *fs_src = - new FormspecFormSource(*(event.show_formspec.formspec)); - TextDestPlayerInventory *txt_dst = - new TextDestPlayerInventory(client, *(event.show_formspec.formname)); - - create_formspec_menu(¤t_formspec, client, gamedef, - texture_src, device, fs_src, txt_dst, client); + if (*(event.show_formspec.formspec) == "") { + if (current_formspec && ( *(event.show_formspec.formname) == "" || *(event.show_formspec.formname) == cur_formname) ){ + current_formspec->quitMenu(); + } + } else { + FormspecFormSource *fs_src = + new FormspecFormSource(*(event.show_formspec.formspec)); + TextDestPlayerInventory *txt_dst = + new TextDestPlayerInventory(client, *(event.show_formspec.formname)); + + create_formspec_menu(¤t_formspec, client, device, &input->joystick, + fs_src, txt_dst); + cur_formname = *(event.show_formspec.formname); + } delete(event.show_formspec.formspec); delete(event.show_formspec.formname); + } else if (event.type == CE_SHOW_LOCAL_FORMSPEC) { + FormspecFormSource *fs_src = new FormspecFormSource(*event.show_formspec.formspec); + LocalFormspecHandler *txt_dst = new LocalFormspecHandler(*event.show_formspec.formname, client); + create_formspec_menu(¤t_formspec, client, device, &input->joystick, + fs_src, txt_dst); + delete event.show_formspec.formspec; + delete event.show_formspec.formname; } else if ((event.type == CE_SPAWN_PARTICLE) || (event.type == CE_ADD_PARTICLESPAWNER) || (event.type == CE_DELETE_PARTICLESPAWNER)) { - client->getParticleManager()->handleParticleEvent(&event, gamedef, + client->getParticleManager()->handleParticleEvent(&event, client, smgr, player); } else if (event.type == CE_HUDADD) { u32 id = event.hudadd.id; @@ -3202,12 +3369,12 @@ void Game::processClientEvents(CameraOrientation *cam, float *damage_flash) event.set_sky.params->size() == 6) { sky->setFallbackBgColor(*event.set_sky.bgcolor); skybox = smgr->addSkyBoxSceneNode( - texture_src->getTexture((*event.set_sky.params)[0]), - texture_src->getTexture((*event.set_sky.params)[1]), - texture_src->getTexture((*event.set_sky.params)[2]), - texture_src->getTexture((*event.set_sky.params)[3]), - texture_src->getTexture((*event.set_sky.params)[4]), - texture_src->getTexture((*event.set_sky.params)[5])); + texture_src->getTextureForMesh((*event.set_sky.params)[0]), + texture_src->getTextureForMesh((*event.set_sky.params)[1]), + texture_src->getTextureForMesh((*event.set_sky.params)[2]), + texture_src->getTextureForMesh((*event.set_sky.params)[3]), + texture_src->getTextureForMesh((*event.set_sky.params)[4]), + texture_src->getTextureForMesh((*event.set_sky.params)[5])); } // Handle everything else as plain color else { @@ -3248,13 +3415,19 @@ void Game::updateCamera(VolatileRunFlags *flags, u32 busy_time, 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); + } + ToolCapabilities playeritem_toolcap = playeritem.getToolCapabilities(itemdef_manager); v3s16 old_camera_offset = camera->getOffset(); - if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CAMERA_MODE])) { + if (wasKeyDown(KeyType::CAMERA_MODE)) { GenericCAO *playercao = player->getCAO(); // If playercao not loaded, don't change camera @@ -3264,6 +3437,7 @@ void Game::updateCamera(VolatileRunFlags *flags, u32 busy_time, camera->toggleCameraMode(); playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); + playercao->setChildrenVisible(camera->getCameraMode() > CAMERA_MODE_FIRST); } float full_punch_interval = playeritem_toolcap.full_punch_interval; @@ -3313,13 +3487,13 @@ void Game::updateSound(f32 dtime) LocalPlayer *player = client->getEnv().getLocalPlayer(); ClientMap &map = client->getEnv().getClientMap(); - MapNode n = map.getNodeNoEx(player->getStandingNodePos()); + MapNode n = map.getNodeNoEx(player->getFootstepNodePos()); soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep; } -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(); @@ -3331,6 +3505,11 @@ void Game::processPlayerInteraction(std::vector &highlight_boxes, 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); + } const ItemDefinition &playeritem_def = playeritem.getDefinition(itemdef_manager); @@ -3355,14 +3534,11 @@ void Game::processPlayerInteraction(std::vector &highlight_boxes, core::line3d shootline; if (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT) { - shootline = core::line3d(camera_position, - camera_position + camera_direction * BS * (d + 1)); - + camera_position + camera_direction * BS * d); } else { // prevent player pointing anything in front-view - if (camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT) - shootline = core::line3d(0, 0, 0, 0, 0, 0); + shootline = core::line3d(camera_position,camera_position); } #ifdef HAVE_TOUCHSCREENGUI @@ -3375,27 +3551,16 @@ void Game::processPlayerInteraction(std::vector &highlight_boxes, #endif - PointedThing pointed = getPointedThing( - // input - client, player_position, camera_direction, - camera_position, shootline, d, + PointedThing pointed = updatePointedThing(shootline, 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); } /* @@ -3404,7 +3569,7 @@ void Game::processPlayerInteraction(std::vector &highlight_boxes, - pointing away from node */ if (runData->digging) { - if (input->getLeftReleased()) { + if (getLeftReleased()) { infostream << "Left button released" << " (stopped digging)" << std::endl; runData->digging = false; @@ -3419,6 +3584,7 @@ void Game::processPlayerInteraction(std::vector &highlight_boxes, infostream << "Pointing away from node" << " (stopped digging)" << std::endl; runData->digging = false; + hud->updateSelectionMesh(camera_offset); } } @@ -3429,7 +3595,7 @@ void Game::processPlayerInteraction(std::vector &highlight_boxes, } } - if (!runData->digging && runData->ldown_for_dig && !input->getLeftState()) { + if (!runData->digging && runData->ldown_for_dig && !isLeftPressed()) { runData->ldown_for_dig = false; } @@ -3437,13 +3603,13 @@ void Game::processPlayerInteraction(std::vector &highlight_boxes, soundmaker->m_player_leftpunch_sound.name = ""; - if (input->getRightState()) + if (isRightPressed()) runData->repeat_rightclick_timer += dtime; else runData->repeat_rightclick_timer = 0; - if (playeritem_def.usable && input->getLeftState()) { - if (input->getLeftClicked()) + if (playeritem_def.usable && isLeftPressed()) { + if (getLeftClicked()) client->interact(4, pointed); } else if (pointed.type == POINTEDTHING_NODE) { ToolCapabilities playeritem_toolcap = @@ -3453,21 +3619,139 @@ void Game::processPlayerInteraction(std::vector &highlight_boxes, } else if (pointed.type == POINTEDTHING_OBJECT) { handlePointingAtObject(runData, pointed, playeritem, player_position, show_debug); - } else if (input->getLeftState()) { + } else if (isLeftPressed()) { // When button is held down in air, show continuous animation runData->left_punch = true; + } else if (getRightClicked()) { + handlePointingAtNothing(runData, playeritem); } runData->pointed_old = pointed; - if (runData->left_punch || input->getLeftClicked()) + if (runData->left_punch || getLeftClicked()) camera->setDigging(0); // left click animation input->resetLeftClicked(); input->resetRightClicked(); + input->joystick.clearWasKeyDown(KeyType::MOUSE_L); + input->joystick.clearWasKeyDown(KeyType::MOUSE_R); + input->resetLeftReleased(); input->resetRightReleased(); + + input->joystick.clearWasKeyReleased(KeyType::MOUSE_L); + input->joystick.clearWasKeyReleased(KeyType::MOUSE_R); +} + + +PointedThing Game::updatePointedThing( + const core::line3d &shootline, + bool liquids_pointable, + bool look_for_object, + const v3s16 &camera_offset, + ClientActiveObject *&selected_object) +{ + std::vector *selectionboxes = hud->getSelectionBoxes(); + selectionboxes->clear(); + hud->setSelectedFaceNormal(v3f(0.0, 0.0, 0.0)); + static const bool show_entity_selectionbox = g_settings->getBool( + "show_entity_selectionbox"); + + ClientMap &map = client->getEnv().getClientMap(); + INodeDefManager *nodedef=client->getNodeDefManager(); + + selected_object = NULL; + + PointedThing result=client->getEnv().getPointedThing( + shootline, liquids_pointable, look_for_object); + if (result.type == POINTEDTHING_OBJECT) { + selected_object = client->getEnv().getActiveObject(result.object_id); + 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(); + selectionboxes->push_back(aabb3f( + selection_box->MinEdge, selection_box->MaxEdge)); + selectionboxes->push_back( + aabb3f(selection_box->MinEdge, selection_box->MaxEdge)); + hud->setSelectionPos(pos, camera_offset); + } + } else if (result.type == POINTEDTHING_NODE) { + // Update selection boxes + MapNode n = map.getNodeNoEx(result.node_undersurface); + std::vector boxes; + n.getSelectionBoxes(nodedef, &boxes, + n.getNeighbors(result.node_undersurface, &map)); + + f32 d = 0.002 * BS; + 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(result.node_undersurface, BS), + camera_offset); + hud->setSelectedFaceNormal(v3f( + result.intersection_normal.X, + result.intersection_normal.Y, + result.intersection_normal.Z)); + } + + // 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; + } + + u32 daynight_ratio = client->getEnv().getDayNightRatio(); + video::SColor c; + final_color_blend(&c, light_level, 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; +} + + +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); } @@ -3486,29 +3770,29 @@ void Game::handlePointingAtNode(GameRunData *runData, NodeMetadata *meta = map.getNodeMetadata(nodepos); if (meta) { - infotext = narrow_to_wide(meta->getString("infotext")); + infotext = unescape_enriched(utf8_to_wide(meta->getString("infotext"))); } else { MapNode n = map.getNodeNoEx(nodepos); if (nodedef_manager->get(n).tiledef[0].name == "unknown_node.png") { infotext = L"Unknown node: "; - infotext += narrow_to_wide(nodedef_manager->get(n).name); + infotext += utf8_to_wide(nodedef_manager->get(n).name); } } - if (runData->nodig_delay_timer <= 0.0 && input->getLeftState() + if (runData->nodig_delay_timer <= 0.0 && isLeftPressed() && client->checkPrivilege("interact")) { handleDigging(runData, pointed, nodepos, playeritem_toolcap, dtime); } - if ((input->getRightClicked() || + if ((getRightClicked() || runData->repeat_rightclick_timer >= m_repeat_right_click_time) && client->checkPrivilege("interact")) { runData->repeat_rightclick_timer = 0; infostream << "Ground right-clicked" << std::endl; if (meta && meta->getString("formspec") != "" && !random_input - && !input->isKeyDown(getKeySetting("keymap_sneak"))) { + && !isKeyDown(KeyType::SNEAK)) { infostream << "Launching custom inventory view" << std::endl; InventoryLocation inventoryloc; @@ -3518,8 +3802,9 @@ void Game::handlePointingAtNode(GameRunData *runData, &client->getEnv().getClientMap(), nodepos); TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client); - create_formspec_menu(¤t_formspec, client, gamedef, - texture_src, device, fs_src, txt_dst, client); + create_formspec_menu(¤t_formspec, client, + device, &input->joystick, fs_src, txt_dst); + cur_formname = ""; current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc); } else { @@ -3542,11 +3827,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; + } + } } } } @@ -3558,13 +3847,18 @@ void Game::handlePointingAtObject(GameRunData *runData, const v3f &player_position, bool show_debug) { - infotext = narrow_to_wide(runData->selected_object->infoText()); + infotext = unescape_enriched( + utf8_to_wide(runData->selected_object->infoText())); - if (infotext == L"" && show_debug) { - infotext = narrow_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()) { + if (isLeftPressed()) { bool do_punch = false; bool do_punch_damage = false; @@ -3574,7 +3868,7 @@ void Game::handlePointingAtObject(GameRunData *runData, runData->object_hit_delay_timer = object_hit_delay; } - if (input->getLeftClicked()) + if (getLeftClicked()) do_punch = true; if (do_punch) { @@ -3594,7 +3888,7 @@ void Game::handlePointingAtObject(GameRunData *runData, if (!disable_send) client->interact(0, pointed); } - } else if (input->getRightClicked()) { + } else if (getRightClicked()) { infostream << "Right-clicked object" << std::endl; client->interact(3, pointed); // place } @@ -3605,17 +3899,20 @@ void Game::handleDigging(GameRunData *runData, const PointedThing &pointed, const v3s16 &nodepos, const ToolCapabilities &playeritem_toolcap, f32 dtime) { + + LocalPlayer *player = client->getEnv().getLocalPlayer(); + ClientMap &map = client->getEnv().getClientMap(); + MapNode n = client->getEnv().getClientMap().getNodeNoEx(nodepos); + if (!runData->digging) { infostream << "Started digging" << std::endl; + if (client->moddingEnabled() && client->getScript()->on_punchnode(nodepos, n)) + return; client->interact(0, pointed); runData->digging = true; runData->ldown_for_dig = true; } - LocalPlayer *player = client->getEnv().getLocalPlayer(); - ClientMap &map = client->getEnv().getClientMap(); - MapNode n = client->getEnv().getClientMap().getNodeNoEx(nodepos); - // NOTE: Similar piece of code exists on the server side for // cheat detection. // Get digging parameters @@ -3640,8 +3937,8 @@ void Game::handleDigging(GameRunData *runData, if (m_cache_enable_particles) { const ContentFeatures &features = client->getNodeDefManager()->get(n); - client->getParticleManager()->addPunchingParticles(gamedef, smgr, - player, nodepos, features.tiles); + client->getParticleManager()->addPunchingParticles(client, smgr, + player, nodepos, n, features); } } @@ -3677,19 +3974,7 @@ void Game::handleDigging(GameRunData *runData, client->setCrack(runData->dig_index, nodepos); } else { infostream << "Digging completed" << std::endl; - client->interact(2, pointed); client->setCrack(-1, v3s16(0, 0, 0)); - bool is_valid_position; - MapNode wasnode = map.getNodeNoEx(nodepos, &is_valid_position); - if (is_valid_position) - client->removeNode(nodepos); - - if (m_cache_enable_particles) { - const ContentFeatures &features = - client->getNodeDefManager()->get(wasnode); - client->getParticleManager()->addDiggingParticles(gamedef, smgr, - player, nodepos, features.tiles); - } runData->dig_time = 0; runData->digging = false; @@ -3709,9 +3994,30 @@ void Game::handleDigging(GameRunData *runData, if (runData->nodig_delay_timer < mindelay) runData->nodig_delay_timer = mindelay; + bool is_valid_position; + MapNode wasnode = map.getNodeNoEx(nodepos, &is_valid_position); + if (is_valid_position) { + if (client->moddingEnabled()) { + if (client->getScript()->on_dignode(nodepos, wasnode)) { + return; + } + } + client->removeNode(nodepos); + } + + client->interact(2, pointed); + + if (m_cache_enable_particles) { + const ContentFeatures &features = + client->getNodeDefManager()->get(wasnode); + client->getParticleManager()->addDiggingParticles(client, smgr, + player, nodepos, wasnode, features); + } + + // Send event to trigger sound MtEvent *e = new NodeDugEvent(nodepos, wasnode); - gamedef->event()->put(e); + client->event()->put(e); } if (runData->dig_time_complete < 100000.0) { @@ -3725,9 +4031,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(); @@ -3738,12 +4044,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 = draw_control->wanted_range * BS; } /* @@ -3754,7 +4055,7 @@ void Game::updateFrame(std::vector &highlight_boxes, float direct_brightness; bool sunlight_seen; - if (g_settings->getBool("free_move")) { + if (m_cache_enable_noclip && m_cache_enable_free_move) { direct_brightness = time_brightness; sunlight_seen = true; } else { @@ -3821,11 +4122,11 @@ void Game::updateFrame(std::vector &highlight_boxes, driver->setFog( sky->getBgColor(), video::EFT_FOG_LINEAR, - runData->fog_range * 0.4, + runData->fog_range * m_cache_fog_start, runData->fog_range * 1.0, 0.01, false, // pixel fog - false // range fog + true // range fog ); } else { driver->setFog( @@ -3869,8 +4170,14 @@ void Game::updateFrame(std::vector &highlight_boxes, 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; } @@ -3918,8 +4225,9 @@ void Game::updateFrame(std::vector &highlight_boxes, stats->beginscenetime = timer.stop(true); } - draw_scene(driver, smgr, *camera, *client, player, *hud, guienv, - highlight_boxes, screensize, skycolor, flags.show_hud); + draw_scene(driver, smgr, *camera, *client, player, *hud, *mapper, + guienv, screensize, skycolor, flags.show_hud, + flags.show_minimap); /* Profiler graph @@ -3931,10 +4239,7 @@ void Game::updateFrame(std::vector &highlight_boxes, Damage flash */ if (runData->damage_flash > 0.0) { - video::SColor color(std::min(runData->damage_flash, 180.0f), - 180, - 0, - 0); + video::SColor color(runData->damage_flash, 180, 0, 0); driver->draw2DRectangle(color, core::rect(0, 0, screensize.X, screensize.Y), NULL); @@ -3952,6 +4257,14 @@ void Game::updateFrame(std::vector &highlight_boxes, player->hurt_tilt_strength = 0; } + /* + Update minimap pos and rotation + */ + if (flags.show_minimap && flags.show_hud) { + mapper->setPos(floatToInt(player->getPosition(), BS)); + mapper->setAngle(player->getYaw()); + } + /* End scene */ @@ -3968,23 +4281,12 @@ void Game::updateFrame(std::vector &highlight_boxes, inline static const char *yawToDirectionString(int yaw) { - // NOTE: TODO: This can be done mathematically without the else/else-if - // cascade. - - const char *player_direction; + static const char *direction[4] = {"North [+Z]", "West [-X]", "South [-Z]", "East [+X]"}; yaw = wrapDegrees_0_360(yaw); + yaw = (yaw + 45) % 360 / 90; - if (yaw >= 45 && yaw < 135) - player_direction = "West [-X]"; - else if (yaw >= 135 && yaw < 225) - player_direction = "South [-Z]"; - else if (yaw >= 225 && yaw < 315) - player_direction = "East [+X]"; - else - player_direction = "North [+Z]"; - - return player_direction; + return direction[yaw]; } @@ -4005,7 +4307,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats, std::ostringstream os(std::ios_base::binary); os << std::fixed - << PROJECT_NAME " " << g_version_hash + << PROJECT_NAME_C " " << g_version_hash << " FPS = " << fps << " (R: range_all=" << draw_control->range_all << ")" << std::setprecision(0) @@ -4017,12 +4319,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats, << ", v_range = " << draw_control->wanted_range << std::setprecision(3) << ", RTT = " << client->getRTT(); - guitext->setText(narrow_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 " " << g_version_hash; - guitext->setText(narrow_to_wide(os.str()).c_str()); + setStaticText(guitext, utf8_to_wide(os.str()).c_str()); guitext->setVisible(true); } else { guitext->setVisible(false); @@ -4059,7 +4356,7 @@ void Game::updateGui(float *statustext_time, const RunStats &stats, } } - guitext2->setText(narrow_to_wide(os.str()).c_str()); + setStaticText(guitext2, utf8_to_wide(os.str()).c_str()); guitext2->setVisible(true); core::rect rect( @@ -4071,7 +4368,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; @@ -4085,7 +4382,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()) { @@ -4137,7 +4434,6 @@ inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime) // not using getRealTime is necessary for wine device->getTimer()->tick(); // Maker sure device time is up-to-date u32 time = device->getTimer()->getTime(); - u32 last_time = fps_timings->last_time; if (time > last_time) // Make sure time hasn't overflowed @@ -4183,6 +4479,38 @@ 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_joysticks = g_settings->getBool("enable_joysticks"); + 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_cache_joystick_frustum_sensitivity = g_settings->getFloat("joystick_frustum_sensitivity"); + m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time"); + + m_cache_enable_noclip = g_settings->getBool("noclip"); + m_cache_enable_free_move = g_settings->getBool("free_move"); + + m_cache_fog_start = g_settings->getFloat("fog_start"); + + m_cache_cam_smoothing = 0; + if (g_settings->getBool("cinematic")) + m_cache_cam_smoothing = 1 - g_settings->getFloat("cinematic_camera_smoothing"); + else + m_cache_cam_smoothing = 1 - g_settings->getFloat("camera_smoothing"); + + m_cache_fog_start = rangelim(m_cache_fog_start, 0.0f, 0.99f); + m_cache_cam_smoothing = rangelim(m_cache_cam_smoothing, 0.01f, 1.0f); + m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0); + +} /****************************************************************************/ /**************************************************************************** @@ -4231,6 +4559,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) { @@ -4245,8 +4574,8 @@ 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(); @@ -4255,7 +4584,7 @@ void the_game(bool *kill, } catch (SerializationError &e) { error_message = std::string("A serialization error occurred:\n") + e.what() + "\n\nThe server is probably " - " running a different version of " PROJECT_NAME "."; + " running a different version of " PROJECT_NAME_C "."; errorstream << error_message << std::endl; } catch (ServerError &e) { error_message = e.what(); @@ -4265,4 +4594,3 @@ void the_game(bool *kill, errorstream << "ModError: " << error_message << std::endl; } } -