gameui.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. /*
  2. Minetest
  3. Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. Copyright (C) 2018 nerzhul, Loic Blot <loic.blot@unix-experience.fr>
  5. This program is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU Lesser General Public License as published by
  7. the Free Software Foundation; either version 2.1 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU Lesser General Public License for more details.
  13. You should have received a copy of the GNU Lesser General Public License along
  14. with this program; if not, write to the Free Software Foundation, Inc.,
  15. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  16. */
  17. #include "gameui.h"
  18. #include <irrlicht_changes/static_text.h>
  19. #include <gettext.h>
  20. #include "gui/mainmenumanager.h"
  21. #include "gui/guiChatConsole.h"
  22. #include "util/pointedthing.h"
  23. #include "client.h"
  24. #include "clientmap.h"
  25. #include "fontengine.h"
  26. #include "nodedef.h"
  27. #include "profiler.h"
  28. #include "renderingengine.h"
  29. #include "version.h"
  30. inline static const char *yawToDirectionString(int yaw)
  31. {
  32. static const char *direction[4] =
  33. {"North +Z", "West -X", "South -Z", "East +X"};
  34. yaw = wrapDegrees_0_360(yaw);
  35. yaw = (yaw + 45) % 360 / 90;
  36. return direction[yaw];
  37. }
  38. GameUI::GameUI()
  39. {
  40. if (guienv && guienv->getSkin())
  41. m_statustext_initial_color = guienv->getSkin()->getColor(gui::EGDC_BUTTON_TEXT);
  42. else
  43. m_statustext_initial_color = video::SColor(255, 0, 0, 0);
  44. }
  45. void GameUI::init()
  46. {
  47. // First line of debug text
  48. m_guitext = gui::StaticText::add(guienv, utf8_to_wide(PROJECT_NAME_C).c_str(),
  49. core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
  50. // Second line of debug text
  51. m_guitext2 = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0), false,
  52. false, guiroot);
  53. // Chat text
  54. m_guitext_chat = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0),
  55. //false, false); // Disable word wrap as of now
  56. false, true, guiroot);
  57. u16 chat_font_size = g_settings->getU16("chat_font_size");
  58. if (chat_font_size != 0) {
  59. m_guitext_chat->setOverrideFont(g_fontengine->getFont(
  60. rangelim(chat_font_size, 5, 72), FM_Unspecified));
  61. }
  62. // Infotext of nodes and objects.
  63. // If in debug mode, object debug infos shown here, too.
  64. // Located on the left on the screen, below chat.
  65. u32 chat_font_height = m_guitext_chat->getActiveFont()->getDimension(L"Ay").Height;
  66. m_guitext_info = gui::StaticText::add(guienv, L"",
  67. // Size is limited; text will be truncated after 6 lines.
  68. core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 6) +
  69. v2s32(100, chat_font_height *
  70. (g_settings->getU16("recent_chat_messages") + 3)),
  71. false, true, guiroot);
  72. // Status text (displays info when showing and hiding GUI stuff, etc.)
  73. m_guitext_status = gui::StaticText::add(guienv, L"<Status>",
  74. core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
  75. m_guitext_status->setVisible(false);
  76. // Profiler text (size is updated when text is updated)
  77. m_guitext_profiler = gui::StaticText::add(guienv, L"<Profiler>",
  78. core::rect<s32>(0, 0, 0, 0), false, false, guiroot);
  79. m_guitext_profiler->setOverrideFont(g_fontengine->getFont(
  80. g_fontengine->getDefaultFontSize() * 0.9f, FM_Mono));
  81. m_guitext_profiler->setVisible(false);
  82. }
  83. void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_control,
  84. const CameraOrientation &cam, const PointedThing &pointed_old,
  85. const GUIChatConsole *chat_console, float dtime)
  86. {
  87. v2u32 screensize = RenderingEngine::getWindowSize();
  88. // Minimal debug text must only contain info that can't give a gameplay advantage
  89. if (m_flags.show_minimal_debug) {
  90. const u16 fps = 1.0 / stats.dtime_jitter.avg;
  91. m_drawtime_avg *= 0.95f;
  92. m_drawtime_avg += 0.05f * (stats.drawtime / 1000);
  93. std::ostringstream os(std::ios_base::binary);
  94. os << std::fixed
  95. << PROJECT_NAME_C " " << g_version_hash
  96. << " | FPS: " << fps
  97. << std::setprecision(0)
  98. << " | drawtime: " << m_drawtime_avg << "ms"
  99. << std::setprecision(1)
  100. << " | dtime jitter: "
  101. << (stats.dtime_jitter.max_fraction * 100.0) << "%"
  102. << std::setprecision(1)
  103. << " | view range: "
  104. << (draw_control->range_all ? "All" : itos(draw_control->wanted_range))
  105. << std::setprecision(2)
  106. << " | RTT: " << (client->getRTT() * 1000.0f) << "ms";
  107. setStaticText(m_guitext, utf8_to_wide(os.str()).c_str());
  108. m_guitext->setRelativePosition(core::rect<s32>(5, 5, screensize.X,
  109. 5 + g_fontengine->getTextHeight()));
  110. }
  111. // Finally set the guitext visible depending on the flag
  112. m_guitext->setVisible(m_flags.show_minimal_debug);
  113. // Basic debug text also shows info that might give a gameplay advantage
  114. if (m_flags.show_basic_debug) {
  115. LocalPlayer *player = client->getEnv().getLocalPlayer();
  116. v3f player_position = player->getPosition();
  117. std::ostringstream os(std::ios_base::binary);
  118. os << std::setprecision(1) << std::fixed
  119. << "pos: (" << (player_position.X / BS)
  120. << ", " << (player_position.Y / BS)
  121. << ", " << (player_position.Z / BS)
  122. << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "° "
  123. << yawToDirectionString(cam.camera_yaw)
  124. << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "°"
  125. << " | seed: " << ((u64)client->getMapSeed());
  126. if (pointed_old.type == POINTEDTHING_NODE) {
  127. ClientMap &map = client->getEnv().getClientMap();
  128. const NodeDefManager *nodedef = client->getNodeDefManager();
  129. MapNode n = map.getNode(pointed_old.node_undersurface);
  130. if (n.getContent() != CONTENT_IGNORE) {
  131. if (nodedef->get(n).name == "unknown") {
  132. os << ", pointed: <unknown node>";
  133. } else {
  134. os << ", pointed: " << nodedef->get(n).name;
  135. }
  136. os << ", param2: " << (u64) n.getParam2();
  137. }
  138. }
  139. setStaticText(m_guitext2, utf8_to_wide(os.str()).c_str());
  140. m_guitext2->setRelativePosition(core::rect<s32>(5,
  141. 5 + g_fontengine->getTextHeight(), screensize.X,
  142. 5 + g_fontengine->getTextHeight() * 2
  143. ));
  144. }
  145. m_guitext2->setVisible(m_flags.show_basic_debug);
  146. setStaticText(m_guitext_info, m_infotext.c_str());
  147. m_guitext_info->setVisible(m_flags.show_hud && g_menumgr.menuCount() == 0);
  148. static const float statustext_time_max = 1.5f;
  149. if (!m_statustext.empty()) {
  150. m_statustext_time += dtime;
  151. if (m_statustext_time >= statustext_time_max) {
  152. clearStatusText();
  153. m_statustext_time = 0.0f;
  154. }
  155. }
  156. setStaticText(m_guitext_status, m_statustext.c_str());
  157. m_guitext_status->setVisible(!m_statustext.empty());
  158. if (!m_statustext.empty()) {
  159. s32 status_width = m_guitext_status->getTextWidth();
  160. s32 status_height = m_guitext_status->getTextHeight();
  161. s32 status_y = screensize.Y - 150;
  162. s32 status_x = (screensize.X - status_width) / 2;
  163. m_guitext_status->setRelativePosition(core::rect<s32>(status_x ,
  164. status_y - status_height, status_x + status_width, status_y));
  165. // Fade out
  166. video::SColor final_color = m_statustext_initial_color;
  167. final_color.setAlpha(0);
  168. video::SColor fade_color = m_statustext_initial_color.getInterpolated_quadratic(
  169. m_statustext_initial_color, final_color, m_statustext_time / statustext_time_max);
  170. m_guitext_status->setOverrideColor(fade_color);
  171. m_guitext_status->enableOverrideColor(true);
  172. }
  173. // Hide chat when console is visible
  174. m_guitext_chat->setVisible(isChatVisible() && !chat_console->isVisible());
  175. }
  176. void GameUI::initFlags()
  177. {
  178. m_flags = GameUI::Flags();
  179. m_flags.show_minimal_debug = g_settings->getBool("show_debug");
  180. }
  181. void GameUI::showMinimap(bool show)
  182. {
  183. m_flags.show_minimap = show;
  184. }
  185. void GameUI::showTranslatedStatusText(const char *str)
  186. {
  187. const wchar_t *wmsg = wgettext(str);
  188. showStatusText(wmsg);
  189. delete[] wmsg;
  190. }
  191. void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count)
  192. {
  193. setStaticText(m_guitext_chat, chat_text);
  194. m_recent_chat_count = recent_chat_count;
  195. }
  196. void GameUI::updateChatSize()
  197. {
  198. // Update gui element size and position
  199. s32 chat_y = 5;
  200. if (m_flags.show_minimal_debug)
  201. chat_y += g_fontengine->getLineHeight();
  202. if (m_flags.show_basic_debug)
  203. chat_y += g_fontengine->getLineHeight();
  204. const v2u32 &window_size = RenderingEngine::getWindowSize();
  205. core::rect<s32> chat_size(10, chat_y, window_size.X - 20, 0);
  206. chat_size.LowerRightCorner.Y = std::min((s32)window_size.Y,
  207. m_guitext_chat->getTextHeight() + chat_y);
  208. if (chat_size == m_current_chat_size)
  209. return;
  210. m_current_chat_size = chat_size;
  211. m_guitext_chat->setRelativePosition(chat_size);
  212. }
  213. void GameUI::updateProfiler()
  214. {
  215. if (m_profiler_current_page != 0) {
  216. std::ostringstream os(std::ios_base::binary);
  217. os << " Profiler page " << (int)m_profiler_current_page <<
  218. ", elapsed: " << g_profiler->getElapsedMs() << " ms)" << std::endl;
  219. int lines = g_profiler->print(os, m_profiler_current_page, m_profiler_max_page);
  220. ++lines;
  221. EnrichedString str(utf8_to_wide(os.str()));
  222. str.setBackground(video::SColor(120, 0, 0, 0));
  223. setStaticText(m_guitext_profiler, str);
  224. core::dimension2d<u32> size = m_guitext_profiler->getOverrideFont()->
  225. getDimension(str.c_str());
  226. core::position2di upper_left(6, 50);
  227. core::position2di lower_right = upper_left;
  228. lower_right.X += size.Width + 10;
  229. lower_right.Y += size.Height;
  230. m_guitext_profiler->setRelativePosition(core::rect<s32>(upper_left, lower_right));
  231. }
  232. m_guitext_profiler->setVisible(m_profiler_current_page != 0);
  233. }
  234. void GameUI::toggleChat()
  235. {
  236. m_flags.show_chat = !m_flags.show_chat;
  237. if (m_flags.show_chat)
  238. showTranslatedStatusText("Chat shown");
  239. else
  240. showTranslatedStatusText("Chat hidden");
  241. }
  242. void GameUI::toggleHud()
  243. {
  244. m_flags.show_hud = !m_flags.show_hud;
  245. if (m_flags.show_hud)
  246. showTranslatedStatusText("HUD shown");
  247. else
  248. showTranslatedStatusText("HUD hidden");
  249. }
  250. void GameUI::toggleProfiler()
  251. {
  252. m_profiler_current_page = (m_profiler_current_page + 1) % (m_profiler_max_page + 1);
  253. // FIXME: This updates the profiler with incomplete values
  254. updateProfiler();
  255. if (m_profiler_current_page != 0) {
  256. std::wstring msg = fwgettext("Profiler shown (page %d of %d)",
  257. m_profiler_current_page, m_profiler_max_page);
  258. showStatusText(msg);
  259. } else {
  260. showTranslatedStatusText("Profiler hidden");
  261. }
  262. }
  263. void GameUI::deleteFormspec()
  264. {
  265. if (m_formspec) {
  266. m_formspec->drop();
  267. m_formspec = nullptr;
  268. }
  269. m_formname.clear();
  270. }