gameui.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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, true, guiroot);
  50. // Second line of debug text
  51. m_guitext2 = gui::StaticText::add(guienv, L"", core::rect<s32>(0, 0, 0, 0), false,
  52. true, 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. LocalPlayer *player = client->getEnv().getLocalPlayer();
  89. s32 minimal_debug_height = 0;
  90. // Minimal debug text must only contain info that can't give a gameplay advantage
  91. if (m_flags.show_minimal_debug) {
  92. const u16 fps = 1.0 / stats.dtime_jitter.avg;
  93. m_drawtime_avg *= 0.95f;
  94. m_drawtime_avg += 0.05f * (stats.drawtime / 1000);
  95. std::ostringstream os(std::ios_base::binary);
  96. os << std::fixed
  97. << PROJECT_NAME_C " " << g_version_hash
  98. << " | FPS: " << fps
  99. << std::setprecision(0)
  100. << " | drawtime: " << m_drawtime_avg << "ms"
  101. << std::setprecision(1)
  102. << " | dtime jitter: "
  103. << (stats.dtime_jitter.max_fraction * 100.0) << "%"
  104. << std::setprecision(1)
  105. << " | view range: "
  106. << (draw_control->range_all ? "All" : itos(draw_control->wanted_range))
  107. << std::setprecision(2)
  108. << " | RTT: " << (client->getRTT() * 1000.0f) << "ms";
  109. m_guitext->setRelativePosition(core::rect<s32>(5, 5, screensize.X, screensize.Y));
  110. setStaticText(m_guitext, utf8_to_wide(os.str()).c_str());
  111. minimal_debug_height = m_guitext->getTextHeight();
  112. }
  113. // Finally set the guitext visible depending on the flag
  114. m_guitext->setVisible(m_flags.show_minimal_debug);
  115. // Basic debug text also shows info that might give a gameplay advantage
  116. if (m_flags.show_basic_debug) {
  117. v3f player_position = player->getPosition();
  118. std::ostringstream os(std::ios_base::binary);
  119. os << std::setprecision(1) << std::fixed
  120. << "pos: (" << (player_position.X / BS)
  121. << ", " << (player_position.Y / BS)
  122. << ", " << (player_position.Z / BS)
  123. << ") | yaw: " << (wrapDegrees_0_360(cam.camera_yaw)) << "° "
  124. << yawToDirectionString(cam.camera_yaw)
  125. << " | pitch: " << (-wrapDegrees_180(cam.camera_pitch)) << "°"
  126. << " | seed: " << ((u64)client->getMapSeed());
  127. if (pointed_old.type == POINTEDTHING_NODE) {
  128. ClientMap &map = client->getEnv().getClientMap();
  129. const NodeDefManager *nodedef = client->getNodeDefManager();
  130. MapNode n = map.getNode(pointed_old.node_undersurface);
  131. if (n.getContent() != CONTENT_IGNORE) {
  132. if (nodedef->get(n).name == "unknown") {
  133. os << ", pointed: <unknown node>";
  134. } else {
  135. os << ", pointed: " << nodedef->get(n).name;
  136. }
  137. os << ", param2: " << (u64) n.getParam2();
  138. }
  139. }
  140. m_guitext2->setRelativePosition(core::rect<s32>(5, 5 + minimal_debug_height,
  141. screensize.X, screensize.Y));
  142. setStaticText(m_guitext2, utf8_to_wide(os.str()).c_str());
  143. }
  144. m_guitext2->setVisible(m_flags.show_basic_debug);
  145. setStaticText(m_guitext_info, m_infotext.c_str());
  146. m_guitext_info->setVisible(m_flags.show_hud && g_menumgr.menuCount() == 0);
  147. static const float statustext_time_max = 1.5f;
  148. if (!m_statustext.empty()) {
  149. m_statustext_time += dtime;
  150. if (m_statustext_time >= statustext_time_max) {
  151. clearStatusText();
  152. m_statustext_time = 0.0f;
  153. }
  154. }
  155. setStaticText(m_guitext_status, m_statustext.c_str());
  156. m_guitext_status->setVisible(!m_statustext.empty());
  157. if (!m_statustext.empty()) {
  158. s32 status_width = m_guitext_status->getTextWidth();
  159. s32 status_height = m_guitext_status->getTextHeight();
  160. s32 status_y = screensize.Y - 150;
  161. s32 status_x = (screensize.X - status_width) / 2;
  162. m_guitext_status->setRelativePosition(core::rect<s32>(status_x ,
  163. status_y - status_height, status_x + status_width, status_y));
  164. // Fade out
  165. video::SColor final_color = m_statustext_initial_color;
  166. final_color.setAlpha(0);
  167. video::SColor fade_color = m_statustext_initial_color.getInterpolated_quadratic(
  168. m_statustext_initial_color, final_color, m_statustext_time / statustext_time_max);
  169. m_guitext_status->setOverrideColor(fade_color);
  170. m_guitext_status->enableOverrideColor(true);
  171. }
  172. // Hide chat when disabled by server or when console is visible
  173. m_guitext_chat->setVisible(isChatVisible() && !chat_console->isVisible() && (player->hud_flags & HUD_FLAG_CHAT_VISIBLE));
  174. }
  175. void GameUI::initFlags()
  176. {
  177. m_flags = GameUI::Flags();
  178. m_flags.show_minimal_debug = g_settings->getBool("show_debug");
  179. }
  180. void GameUI::showTranslatedStatusText(const char *str)
  181. {
  182. showStatusText(wstrgettext(str));
  183. }
  184. void GameUI::setChatText(const EnrichedString &chat_text, u32 recent_chat_count)
  185. {
  186. setStaticText(m_guitext_chat, chat_text);
  187. m_recent_chat_count = recent_chat_count;
  188. }
  189. void GameUI::updateChatSize()
  190. {
  191. // Update gui element size and position
  192. s32 chat_y = 5;
  193. if (m_flags.show_minimal_debug)
  194. chat_y += m_guitext->getTextHeight();
  195. if (m_flags.show_basic_debug)
  196. chat_y += m_guitext2->getTextHeight();
  197. const v2u32 &window_size = RenderingEngine::getWindowSize();
  198. core::rect<s32> chat_size(10, chat_y, window_size.X - 20, 0);
  199. chat_size.LowerRightCorner.Y = std::min((s32)window_size.Y,
  200. m_guitext_chat->getTextHeight() + chat_y);
  201. if (chat_size == m_current_chat_size)
  202. return;
  203. m_current_chat_size = chat_size;
  204. m_guitext_chat->setRelativePosition(chat_size);
  205. }
  206. void GameUI::updateProfiler()
  207. {
  208. if (m_profiler_current_page != 0) {
  209. std::ostringstream os(std::ios_base::binary);
  210. os << " Profiler page " << (int)m_profiler_current_page <<
  211. ", elapsed: " << g_profiler->getElapsedMs() << " ms)" << std::endl;
  212. g_profiler->print(os, m_profiler_current_page, m_profiler_max_page);
  213. EnrichedString str(utf8_to_wide(os.str()));
  214. str.setBackground(video::SColor(120, 0, 0, 0));
  215. setStaticText(m_guitext_profiler, str);
  216. core::dimension2d<u32> size = m_guitext_profiler->getOverrideFont()->
  217. getDimension(str.c_str());
  218. core::position2di upper_left(6, m_guitext->getTextHeight() * 2.5f);
  219. core::position2di lower_right = upper_left;
  220. lower_right.X += size.Width + 10;
  221. lower_right.Y += size.Height;
  222. m_guitext_profiler->setRelativePosition(core::rect<s32>(upper_left, lower_right));
  223. }
  224. m_guitext_profiler->setVisible(m_profiler_current_page != 0);
  225. }
  226. void GameUI::toggleChat(Client *client)
  227. {
  228. if (client->getEnv().getLocalPlayer()->hud_flags & HUD_FLAG_CHAT_VISIBLE) {
  229. m_flags.show_chat = !m_flags.show_chat;
  230. if (m_flags.show_chat)
  231. showTranslatedStatusText("Chat shown");
  232. else
  233. showTranslatedStatusText("Chat hidden");
  234. } else {
  235. showTranslatedStatusText("Chat currently disabled by game or mod");
  236. }
  237. }
  238. void GameUI::toggleHud()
  239. {
  240. m_flags.show_hud = !m_flags.show_hud;
  241. if (m_flags.show_hud)
  242. showTranslatedStatusText("HUD shown");
  243. else
  244. showTranslatedStatusText("HUD hidden");
  245. }
  246. void GameUI::toggleProfiler()
  247. {
  248. m_profiler_current_page = (m_profiler_current_page + 1) % (m_profiler_max_page + 1);
  249. // FIXME: This updates the profiler with incomplete values
  250. updateProfiler();
  251. if (m_profiler_current_page != 0) {
  252. std::wstring msg = fwgettext("Profiler shown (page %d of %d)",
  253. m_profiler_current_page, m_profiler_max_page);
  254. showStatusText(msg);
  255. } else {
  256. showTranslatedStatusText("Profiler hidden");
  257. }
  258. }
  259. void GameUI::deleteFormspec()
  260. {
  261. if (m_formspec) {
  262. m_formspec->drop();
  263. m_formspec = nullptr;
  264. }
  265. m_formname.clear();
  266. }
  267. void GameUI::clearText()
  268. {
  269. if (m_guitext_chat) {
  270. m_guitext_chat->remove();
  271. m_guitext_chat = nullptr;
  272. }
  273. if (m_guitext) {
  274. m_guitext->remove();
  275. m_guitext = nullptr;
  276. }
  277. if (m_guitext2) {
  278. m_guitext2->remove();
  279. m_guitext2 = nullptr;
  280. }
  281. if (m_guitext_info) {
  282. m_guitext_info->remove();
  283. m_guitext_info = nullptr;
  284. }
  285. if (m_guitext_status) {
  286. m_guitext_status->remove();
  287. m_guitext_status = nullptr;
  288. }
  289. if (m_guitext_profiler) {
  290. m_guitext_profiler->remove();
  291. m_guitext_profiler = nullptr;
  292. }
  293. }