statistics.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. // SuperTux (Statistics module)
  2. // Copyright (C) 2004 Ricardo Cruz <rick2@aeiou.pt>
  3. // Copyright (C) 2006 Ondrej Hosek <ondra.hosek@gmail.com>
  4. // Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
  5. //
  6. // This program is free software: you can redistribute it and/or modify
  7. // it under the terms of the GNU General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // This program is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU General Public License
  17. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. #include "supertux/statistics.hpp"
  19. #include <algorithm>
  20. #include <iomanip>
  21. #include <limits>
  22. #include <simplesquirrel/table.hpp>
  23. #include "audio/sound_manager.hpp"
  24. #include "gui/menu.hpp"
  25. #include "math/util.hpp"
  26. #include "supertux/globals.hpp"
  27. #include "supertux/level.hpp"
  28. #include "supertux/resources.hpp"
  29. #include "util/gettext.hpp"
  30. #include "util/log.hpp"
  31. #include "util/reader_mapping.hpp"
  32. #include "util/writer.hpp"
  33. #include "video/drawing_context.hpp"
  34. #include "video/surface.hpp"
  35. #include "video/video_system.hpp"
  36. #include "video/viewport.hpp"
  37. static const float INGAME_STATS_DISTANCE = 1.7f;
  38. Statistics::Preferences::Preferences() :
  39. enable_coins(true),
  40. enable_badguys(false),
  41. enable_secrets(true)
  42. {
  43. }
  44. void
  45. Statistics::Preferences::parse(const ReaderMapping& reader)
  46. {
  47. reader.get("enable-coins", enable_coins);
  48. reader.get("enable-badguys", enable_badguys);
  49. reader.get("enable-secrets", enable_secrets);
  50. }
  51. void
  52. Statistics::Preferences::write(Writer& writer) const
  53. {
  54. writer.write("enable-coins", enable_coins);
  55. writer.write("enable-badguys", enable_badguys);
  56. writer.write("enable-secrets", enable_secrets);
  57. }
  58. void
  59. Statistics::Preferences::add_to_menu(Menu& menu)
  60. {
  61. menu.add_toggle(-1, _("Enable Coins Statistic"), &enable_coins);
  62. menu.add_toggle(-1, _("Enable Badguys Statistic"), &enable_badguys);
  63. menu.add_toggle(-1, _("Enable Secrets Statistic"), &enable_secrets);
  64. }
  65. Statistics::Statistics() :
  66. m_status(INVALID),
  67. m_total_coins(),
  68. m_total_badguys(),
  69. m_total_secrets(),
  70. m_coins(),
  71. m_badguys(),
  72. m_secrets(),
  73. m_time(),
  74. m_cleared_coins(false),
  75. m_cleared_badguys(false),
  76. m_cleared_secrets(false),
  77. m_coins_time(0.f),
  78. m_badguys_time(0.f),
  79. m_secrets_time(0.f),
  80. m_preferences(),
  81. m_max_width(256),
  82. CAPTION_MAX_COINS(_("Max coins collected:")),
  83. CAPTION_MAX_FRAGGING(_("Max fragging:")),
  84. CAPTION_MAX_SECRETS(_("Max secrets found:")),
  85. CAPTION_BEST_TIME(_("Best time completed:")),
  86. CAPTION_TARGET_TIME(_("Level target time:")),
  87. WMAP_INFO_LEFT_X(),
  88. WMAP_INFO_RIGHT_X(),
  89. WMAP_INFO_TOP_Y1(),
  90. WMAP_INFO_TOP_Y2(),
  91. coin_icon(Surface::from_file("/images/engine/hud/coin-icon.png")),
  92. badguy_icon(Surface::from_file("/images/engine/hud/badguy-icon.png")),
  93. secret_icon(Surface::from_file("/images/engine/hud/secret-icon.png"))
  94. {
  95. calculate_max_caption_length();
  96. WMAP_INFO_LEFT_X = static_cast<float>(SCREEN_WIDTH) - 32.0f - static_cast<float>(m_max_width);
  97. WMAP_INFO_RIGHT_X = WMAP_INFO_LEFT_X + static_cast<float>(m_max_width);
  98. WMAP_INFO_TOP_Y1 = static_cast<float>(SCREEN_HEIGHT) - 100.0f;
  99. WMAP_INFO_TOP_Y2 = WMAP_INFO_TOP_Y1 + 16.0f;
  100. }
  101. void
  102. Statistics::calculate_max_caption_length()
  103. {
  104. auto captions = {CAPTION_MAX_COINS, CAPTION_MAX_FRAGGING, CAPTION_MAX_SECRETS,
  105. CAPTION_BEST_TIME, CAPTION_TARGET_TIME};
  106. m_max_width = 256;
  107. for (const auto& caption : captions)
  108. {
  109. auto font = Resources::small_font;
  110. // Add padding the size of lengthiest string:
  111. auto width = font->get_text_width(caption) +
  112. font->get_text_width("XX:XX:XX");
  113. if (width >= static_cast<float>(m_max_width))
  114. {
  115. m_max_width = static_cast<int>(width);
  116. }
  117. }
  118. }
  119. void
  120. Statistics::serialize_to_squirrel(ssq::Table& table) const
  121. {
  122. if (m_status != FINAL) return;
  123. ssq::Table statistics = table.addTable("statistics");
  124. statistics.set("coins-collected", m_coins);
  125. statistics.set("badguys-killed", m_badguys);
  126. statistics.set("secrets-found", m_secrets);
  127. statistics.set("time-needed", m_time);
  128. statistics.set("coins-collected-total", m_total_coins);
  129. statistics.set("badguys-killed-total", m_total_badguys);
  130. statistics.set("secrets-found-total", m_total_secrets);
  131. }
  132. void
  133. Statistics::unserialize_from_squirrel(const ssq::Table& table)
  134. {
  135. try
  136. {
  137. const ssq::Table statistics = table.findTable("statistics");
  138. statistics.get("coins-collected", m_coins);
  139. statistics.get("badguys-killed", m_badguys);
  140. statistics.get("secrets-found", m_secrets);
  141. statistics.get("time-needed", m_time);
  142. statistics.get("coins-collected-total", m_total_coins);
  143. statistics.get("badguys-killed-total", m_total_badguys);
  144. statistics.get("secrets-found-total", m_total_secrets);
  145. m_status = FINAL;
  146. }
  147. catch(const ssq::NotFoundException&)
  148. {
  149. // ignore non-existing or malformed statistics table
  150. }
  151. }
  152. void
  153. Statistics::add_preferences_to_menu(Menu& menu)
  154. {
  155. m_preferences.add_to_menu(menu);
  156. }
  157. void
  158. Statistics::draw_worldmap_info(DrawingContext& context, float target_time)
  159. {
  160. if (m_status != FINAL) return;
  161. // check to see if screen size has been changed
  162. if (!(WMAP_INFO_TOP_Y1 == static_cast<float>(SCREEN_HEIGHT - 100))) {
  163. calculate_max_caption_length();
  164. WMAP_INFO_LEFT_X = context.get_width() - 32.f - static_cast<float>(m_max_width);
  165. WMAP_INFO_RIGHT_X = WMAP_INFO_LEFT_X + static_cast<float>(m_max_width);
  166. WMAP_INFO_TOP_Y1 = static_cast<float>(SCREEN_HEIGHT - 100);
  167. WMAP_INFO_TOP_Y2 = WMAP_INFO_TOP_Y1 + 16;
  168. }
  169. context.color().draw_text(
  170. Resources::small_font, "- " + _("Best Level Statistics") + " -",
  171. Vector((WMAP_INFO_LEFT_X + WMAP_INFO_RIGHT_X) / 2, WMAP_INFO_TOP_Y1),
  172. ALIGN_CENTER, LAYER_HUD,Statistics::header_color);
  173. std::string caption_buf;
  174. std::string stat_buf;
  175. float posy = WMAP_INFO_TOP_Y2;
  176. Color tcolor;
  177. for (int stat_no = 0; stat_no < 5; stat_no++) {
  178. tcolor = Statistics::header_color;
  179. switch (stat_no)
  180. {
  181. case 0:
  182. if (!m_preferences.enable_coins)
  183. continue;
  184. caption_buf = CAPTION_MAX_COINS;
  185. stat_buf = coins_to_string(m_coins, m_total_coins);
  186. if (m_coins >= m_total_coins)
  187. tcolor = Statistics::perfect_color;
  188. break;
  189. case 1:
  190. if (!m_preferences.enable_badguys)
  191. continue;
  192. caption_buf = CAPTION_MAX_FRAGGING;
  193. stat_buf = frags_to_string(m_badguys, m_total_badguys);
  194. if (m_badguys >= m_total_badguys)
  195. tcolor = Statistics::perfect_color;
  196. break;
  197. case 2:
  198. if (!m_preferences.enable_secrets)
  199. continue;
  200. caption_buf = CAPTION_MAX_SECRETS;
  201. stat_buf = secrets_to_string(m_secrets, m_total_secrets);
  202. if (m_secrets >= m_total_secrets)
  203. tcolor = Statistics::perfect_color;
  204. break;
  205. case 3:
  206. caption_buf = CAPTION_BEST_TIME;
  207. stat_buf = time_to_string(m_time);
  208. if ((m_time < target_time) || (target_time == 0.0f))
  209. tcolor = Statistics::perfect_color;
  210. break;
  211. case 4:
  212. if (target_time != 0.0f) { // display target time only if defined for level
  213. caption_buf = CAPTION_TARGET_TIME;
  214. stat_buf = time_to_string(target_time);
  215. if ((m_time < target_time) || (target_time == 0.0f))
  216. tcolor = Statistics::perfect_color;
  217. } else {
  218. caption_buf = "";
  219. stat_buf = "";
  220. }
  221. break;
  222. default:
  223. log_debug << "Invalid stat requested to be drawn" << std::endl;
  224. continue;
  225. }
  226. context.color().draw_text(Resources::small_font, caption_buf, Vector(WMAP_INFO_LEFT_X, posy), ALIGN_LEFT, LAYER_HUD, Statistics::header_color);
  227. context.color().draw_text(Resources::small_font, stat_buf, Vector(WMAP_INFO_RIGHT_X, posy), ALIGN_RIGHT, LAYER_HUD, tcolor);
  228. posy += Resources::small_font->get_height() + 2;
  229. }
  230. }
  231. void
  232. Statistics::draw_endseq_panel(DrawingContext& context, Statistics* best_stats, const SurfacePtr& backdrop, float target_time)
  233. {
  234. if (m_status != FINAL) return;
  235. int layer = LAYER_GUI + 50;
  236. int box_w = 220+110+110;
  237. int box_h = 30+20+20+20;
  238. int box_x = static_cast<int>((static_cast<int>(context.get_width()) - box_w) / 2);
  239. int box_y = static_cast<int>(SCREEN_HEIGHT / 2) - box_h;
  240. int bd_w = static_cast<int>(backdrop->get_width());
  241. int bd_h = static_cast<int>(backdrop->get_height());
  242. int bd_x = static_cast<int>((static_cast<int>(context.get_width()) - bd_w) / 2);
  243. int bd_y = box_y + (box_h / 2) - (bd_h / 2);
  244. float col1_x = static_cast<float>(box_x);
  245. float col2_x = col1_x + 200.0f;
  246. float col3_x = col2_x + 130.0f;
  247. float y_offset = 47.f;
  248. if (m_preferences.enable_coins)
  249. y_offset -= 9.f;
  250. if (m_preferences.enable_badguys)
  251. y_offset -= 9.f;
  252. if (m_preferences.enable_secrets)
  253. y_offset -= 9.f;
  254. float y = static_cast<float>(box_y);
  255. context.push_transform();
  256. context.set_alpha(0.5f);
  257. context.color().draw_surface(backdrop, Vector(static_cast<float>(bd_x), static_cast<float>(bd_y)), layer);
  258. context.pop_transform();
  259. context.color().draw_text(Resources::normal_font, _("You"), Vector(col2_x, y), ALIGN_LEFT, layer, Statistics::header_color);
  260. if (best_stats)
  261. context.color().draw_text(Resources::normal_font, _("Best"), Vector(col3_x, y), ALIGN_LEFT, layer, Statistics::header_color);
  262. y += 10.f + y_offset;
  263. Color tcolor = Statistics::text_color;
  264. if (target_time == 0.0f || (m_time != 0.0f && m_time < target_time))
  265. tcolor = Statistics::perfect_color;
  266. context.color().draw_text(Resources::normal_font, _("Time"), Vector(col2_x - 16.f, y), ALIGN_RIGHT, layer, Statistics::header_color);
  267. context.color().draw_text(Resources::normal_font, time_to_string(m_time), Vector(col2_x, y), ALIGN_LEFT, layer, tcolor);
  268. if (best_stats)
  269. {
  270. float time_best = (best_stats->m_time < m_time && best_stats->m_time > 0.0f) ? best_stats->m_time : m_time;
  271. if (target_time == 0.0f || (time_best != 0.0f && time_best < target_time))
  272. tcolor = Statistics::perfect_color;
  273. else
  274. tcolor = Statistics::text_color;
  275. context.color().draw_text(Resources::normal_font, time_to_string(time_best), Vector(col3_x, y), ALIGN_LEFT, layer, tcolor);
  276. }
  277. if (m_preferences.enable_coins)
  278. {
  279. y += y_offset;
  280. context.color().draw_text(Resources::normal_font, _("Coins"), Vector(col2_x - 16.f, y), ALIGN_RIGHT, layer, Statistics::header_color);
  281. if (m_coins >= m_total_coins)
  282. tcolor = Statistics::perfect_color;
  283. else
  284. tcolor = Statistics::text_color;
  285. context.color().draw_text(Resources::normal_font, coins_to_string(m_coins, m_total_coins), Vector(col2_x, y), ALIGN_LEFT, layer, tcolor);
  286. if (best_stats)
  287. {
  288. int coins_best = (best_stats->m_coins > m_coins) ? best_stats->m_coins : m_coins;
  289. int total_coins_best = (best_stats->m_total_coins > m_total_coins) ? best_stats->m_total_coins : m_total_coins;
  290. if (coins_best >= total_coins_best)
  291. tcolor = Statistics::perfect_color;
  292. else
  293. tcolor = Statistics::text_color;
  294. context.color().draw_text(Resources::normal_font, coins_to_string(coins_best, total_coins_best), Vector(col3_x, y), ALIGN_LEFT, layer, tcolor);
  295. }
  296. }
  297. if (m_preferences.enable_badguys)
  298. {
  299. y += y_offset;
  300. if (m_badguys >= m_total_badguys)
  301. tcolor = Statistics::perfect_color;
  302. else
  303. tcolor = Statistics::text_color;
  304. context.color().draw_text(Resources::normal_font, _("Badguys"), Vector(col2_x - 16.f, y), ALIGN_RIGHT, layer, Statistics::header_color);
  305. context.color().draw_text(Resources::normal_font, frags_to_string(m_badguys, m_total_badguys), Vector(col2_x, y), ALIGN_LEFT, layer, tcolor);
  306. if (best_stats)
  307. {
  308. int badguys_best = (best_stats->m_badguys > m_badguys) ? best_stats->m_badguys : m_badguys;
  309. int total_badguys_best = (best_stats->m_total_badguys > m_total_badguys) ? best_stats->m_total_badguys : m_total_badguys;
  310. if (badguys_best >= total_badguys_best)
  311. tcolor = Statistics::perfect_color;
  312. else
  313. tcolor = Statistics::text_color;
  314. context.color().draw_text(Resources::normal_font, frags_to_string(badguys_best, total_badguys_best), Vector(col3_x, y), ALIGN_LEFT, layer, tcolor);
  315. }
  316. }
  317. if (m_preferences.enable_secrets)
  318. {
  319. y += y_offset;
  320. if (m_secrets >= m_total_secrets)
  321. tcolor = Statistics::perfect_color;
  322. else
  323. tcolor = Statistics::text_color;
  324. context.color().draw_text(Resources::normal_font, _("Secrets"), Vector(col2_x - 16.f, y), ALIGN_RIGHT, layer, Statistics::header_color);
  325. context.color().draw_text(Resources::normal_font, secrets_to_string(m_secrets, m_total_secrets), Vector(col2_x, y), ALIGN_LEFT, layer, tcolor);
  326. if (best_stats)
  327. {
  328. int secrets_best = (best_stats->m_secrets > m_secrets) ? best_stats->m_secrets : m_secrets;
  329. int total_secrets_best = (best_stats->m_total_secrets > m_total_secrets) ? best_stats->m_total_secrets : m_total_secrets;
  330. if (secrets_best >= total_secrets_best)
  331. tcolor = Statistics::perfect_color;
  332. else
  333. tcolor = Statistics::text_color;
  334. context.color().draw_text(Resources::normal_font, secrets_to_string(secrets_best, total_secrets_best), Vector(col3_x, y), ALIGN_LEFT, layer, tcolor);
  335. }
  336. }
  337. }
  338. void
  339. Statistics::draw_ingame_stats(DrawingContext& context, bool on_pause_menu)
  340. {
  341. const float height = Resources::normal_font->get_height();
  342. float y = context.get_height() - height * 4.f;
  343. if (m_preferences.enable_secrets)
  344. {
  345. if (on_pause_menu || (m_cleared_secrets && m_secrets_time < 5.f))
  346. {
  347. std::string text(secrets_to_string(m_secrets, m_total_secrets));
  348. float width = Resources::normal_font->get_text_width(text),
  349. x_offset = width + 75.f;
  350. if (!on_pause_menu)
  351. x_offset *= std::min(1.f, -std::abs(m_secrets_time - 2.5f) + 2.5f);
  352. Vector pos(context.get_width() - x_offset, y);
  353. context.color().draw_filled_rect(Rectf(pos.x, pos.y, pos.x + width + 37.f,
  354. pos.y + height).grown(5.f),
  355. Color(0.f, 0.f, 0.f, 0.5f),
  356. 10.f, LAYER_HUD - 1);
  357. context.color().draw_text(Resources::normal_font, text, pos,
  358. FontAlignment::ALIGN_LEFT, LAYER_HUD,
  359. (m_secrets < m_total_secrets)
  360. ? Statistics::text_color
  361. : Statistics::perfect_color
  362. );
  363. context.color().draw_surface_scaled(secret_icon,
  364. Rectf(Vector(pos.x + width + 3.f, pos.y - 5.f), Sizef(32.f, 32.f)),
  365. LAYER_HUD);
  366. }
  367. y -= height * INGAME_STATS_DISTANCE;
  368. }
  369. if (m_preferences.enable_badguys)
  370. {
  371. if (on_pause_menu || (m_cleared_badguys && m_badguys_time < 5.f))
  372. {
  373. std::string text(frags_to_string(m_badguys, m_total_badguys));
  374. float width = Resources::normal_font->get_text_width(text),
  375. x_offset = width + 75.f;
  376. if (!on_pause_menu)
  377. x_offset *= std::min(1.f, -std::abs(m_badguys_time - 2.5f) + 2.5f);
  378. Vector pos(context.get_width() - x_offset, y);
  379. context.color().draw_filled_rect(Rectf(pos.x, pos.y, pos.x + width + 37.f,
  380. pos.y + height).grown(5.f),
  381. Color(0.f, 0.f, 0.f, 0.5f),
  382. 10.f, LAYER_HUD - 1);
  383. context.color().draw_text(Resources::normal_font, text, pos,
  384. FontAlignment::ALIGN_LEFT, LAYER_HUD,
  385. (m_badguys < m_total_badguys)
  386. ? Statistics::text_color
  387. : Statistics::perfect_color
  388. );
  389. context.color().draw_surface_scaled(badguy_icon,
  390. Rectf(Vector(pos.x + width + 3.f, pos.y - 5.f), Sizef(32.f, 32.f)),
  391. LAYER_HUD);
  392. }
  393. y -= height * INGAME_STATS_DISTANCE;
  394. }
  395. if (m_preferences.enable_coins && (on_pause_menu || (m_cleared_coins && m_coins_time < 5.f)))
  396. {
  397. std::string text(coins_to_string(m_coins, m_total_coins));
  398. float width = Resources::normal_font->get_text_width(text),
  399. x_offset = width + 75.f;
  400. if (!on_pause_menu)
  401. x_offset *= std::min(1.f, -std::abs(m_coins_time - 2.5f) + 2.5f);
  402. Vector pos(context.get_width() - x_offset, y);
  403. context.color().draw_filled_rect(Rectf(pos.x, pos.y, pos.x + width + 37.f,
  404. pos.y + height).grown(5.f),
  405. Color(0.f, 0.f, 0.f, 0.5f),
  406. 10.f, LAYER_HUD - 1);
  407. context.color().draw_text(Resources::normal_font, text, pos,
  408. FontAlignment::ALIGN_LEFT, LAYER_HUD,
  409. (m_coins < m_total_coins)
  410. ? Statistics::text_color
  411. : Statistics::perfect_color
  412. );
  413. context.color().draw_surface_scaled(coin_icon,
  414. Rectf(Vector(pos.x + width + 3.f, pos.y - 5.f), Sizef(32.f, 32.f)),
  415. LAYER_HUD);
  416. }
  417. }
  418. void
  419. Statistics::update_timers(float dt_sec)
  420. {
  421. if (m_cleared_coins)
  422. m_coins_time += dt_sec;
  423. if (m_cleared_badguys)
  424. m_badguys_time += dt_sec;
  425. if (m_cleared_secrets)
  426. m_secrets_time += dt_sec;
  427. }
  428. void
  429. Statistics::init(const Level& level)
  430. {
  431. m_status = ACCUMULATING;
  432. m_coins = 0;
  433. m_badguys = 0;
  434. m_secrets = 0;
  435. m_total_coins = level.get_total_coins();
  436. m_total_badguys = level.get_total_badguys();
  437. m_total_secrets = level.get_total_secrets();
  438. }
  439. void
  440. Statistics::finish(float time)
  441. {
  442. m_status = FINAL;
  443. m_time = time;
  444. }
  445. void
  446. Statistics::invalidate()
  447. {
  448. m_status = INVALID;
  449. }
  450. void
  451. Statistics::update(const Statistics& other)
  452. {
  453. if (other.m_status != FINAL) return;
  454. m_coins = std::max(m_coins, other.m_coins);
  455. m_badguys = std::max(m_badguys, other.m_badguys);
  456. m_secrets = std::max(m_secrets, other.m_secrets);
  457. if (m_time == 0)
  458. m_time = other.m_time;
  459. else
  460. m_time = std::min(m_time, other.m_time);
  461. m_total_coins = other.m_total_coins;
  462. m_total_badguys = other.m_total_badguys;
  463. m_total_secrets = other.m_total_secrets;
  464. m_preferences = other.m_preferences;
  465. m_coins = math::clamp(m_coins, 0, m_total_coins);
  466. m_badguys = math::clamp(m_badguys, 0, m_total_badguys);
  467. m_secrets = math::clamp(m_secrets, 0, m_total_secrets);
  468. m_status = FINAL;
  469. }
  470. bool
  471. Statistics::completed(const float target_time) const
  472. {
  473. return ((!m_preferences.enable_coins || m_coins == m_total_coins) &&
  474. (!m_preferences.enable_badguys || m_badguys == m_total_badguys) &&
  475. (!m_preferences.enable_secrets || m_secrets == m_total_secrets) &&
  476. (target_time == 0.0f || m_time <= target_time));
  477. }
  478. std::string
  479. Statistics::coins_to_string(int coins, int total_coins)
  480. {
  481. std::ostringstream os;
  482. os << std::min(std::min(coins, total_coins), 999) << "/" << std::min(total_coins, 999);
  483. return os.str();
  484. }
  485. std::string
  486. Statistics::frags_to_string(int badguys, int total_badguys)
  487. {
  488. std::ostringstream os;
  489. os << std::min(std::min(badguys, total_badguys), 999) << "/" << std::min(total_badguys, 999);
  490. return os.str();
  491. }
  492. std::string
  493. Statistics::secrets_to_string(int secrets, int total_secrets)
  494. {
  495. std::ostringstream os;
  496. os << std::min(secrets, 999) << "/" << std::min(total_secrets, 999);
  497. return os.str();
  498. }
  499. std::string
  500. Statistics::time_to_string(float time)
  501. {
  502. std::ostringstream os;
  503. if (time == 0.0f)
  504. {
  505. os << "--:--:--";
  506. }
  507. else
  508. {
  509. int time_csecs = static_cast<int>(time * 100);
  510. int mins = (time_csecs / 6000);
  511. int secs = (time_csecs % 6000) / 100;
  512. int cscs = (time_csecs % 6000) % 100;
  513. os << std::setw(2) << std::setfill('0') << mins << ":" << std::setw(2) << std::setfill('0') << secs << "." << std::setw(2) << std::setfill('0') << cscs;
  514. }
  515. return os.str();
  516. }
  517. void
  518. Statistics::check_coins()
  519. {
  520. if (!m_preferences.enable_coins || m_cleared_coins)
  521. return;
  522. if (m_coins >= m_total_coins)
  523. {
  524. m_cleared_coins = true;
  525. SoundManager::current()->play("/sounds/coins_cleared.ogg", 0.5f);
  526. }
  527. }
  528. void
  529. Statistics::check_badguys()
  530. {
  531. if (!m_preferences.enable_badguys || m_cleared_badguys)
  532. return;
  533. if (m_badguys >= m_total_badguys)
  534. {
  535. m_cleared_badguys = true;
  536. SoundManager::current()->play("/sounds/retro_fall.wav", 0.5f);
  537. }
  538. }
  539. void
  540. Statistics::check_secrets()
  541. {
  542. if (!m_preferences.enable_secrets || m_cleared_secrets)
  543. return;
  544. if (m_secrets >= m_total_secrets)
  545. {
  546. m_cleared_secrets = true;
  547. SoundManager::current()->play("/sounds/tada.ogg", 0.5f);
  548. }
  549. }
  550. /* EOF */