statistics.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553
  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 "audio/sound_manager.hpp"
  23. #include "math/util.hpp"
  24. #include "squirrel/squirrel_util.hpp"
  25. #include "supertux/globals.hpp"
  26. #include "supertux/level.hpp"
  27. #include "supertux/resources.hpp"
  28. #include "util/gettext.hpp"
  29. #include "util/log.hpp"
  30. #include "video/drawing_context.hpp"
  31. #include "video/surface.hpp"
  32. #include "video/video_system.hpp"
  33. #include "video/viewport.hpp"
  34. Statistics::Statistics() :
  35. m_status(INVALID),
  36. m_total_coins(),
  37. m_total_badguys(),
  38. m_total_secrets(),
  39. m_coins(),
  40. m_badguys(),
  41. m_secrets(),
  42. m_time(),
  43. m_cleared_coins(false),
  44. m_cleared_badguys(false),
  45. m_cleared_secrets(false),
  46. m_coins_time(0.f),
  47. m_badguys_time(0.f),
  48. m_secrets_time(0.f),
  49. m_max_width(256),
  50. CAPTION_MAX_COINS(_("Max coins collected:")),
  51. CAPTION_MAX_FRAGGING(_("Max fragging:")),
  52. CAPTION_MAX_SECRETS(_("Max secrets found:")),
  53. CAPTION_BEST_TIME(_("Best time completed:")),
  54. CAPTION_TARGET_TIME(_("Level target time:")),
  55. WMAP_INFO_LEFT_X(),
  56. WMAP_INFO_RIGHT_X(),
  57. WMAP_INFO_TOP_Y1(),
  58. WMAP_INFO_TOP_Y2(),
  59. coin_icon(Surface::from_file("/images/engine/hud/coin-icon.png")),
  60. badguy_icon(Surface::from_file("/images/engine/hud/badguy-icon.png")),
  61. secret_icon(Surface::from_file("/images/engine/hud/secret-icon.png"))
  62. {
  63. calculate_max_caption_length();
  64. WMAP_INFO_LEFT_X = static_cast<float>(SCREEN_WIDTH) - 32.0f - static_cast<float>(m_max_width);
  65. WMAP_INFO_RIGHT_X = WMAP_INFO_LEFT_X + static_cast<float>(m_max_width);
  66. WMAP_INFO_TOP_Y1 = static_cast<float>(SCREEN_HEIGHT) - 100.0f;
  67. WMAP_INFO_TOP_Y2 = WMAP_INFO_TOP_Y1 + 16.0f;
  68. }
  69. void
  70. Statistics::calculate_max_caption_length()
  71. {
  72. auto captions = {CAPTION_MAX_COINS, CAPTION_MAX_FRAGGING, CAPTION_MAX_SECRETS,
  73. CAPTION_BEST_TIME, CAPTION_TARGET_TIME};
  74. m_max_width = 256;
  75. for (const auto& caption : captions)
  76. {
  77. auto font = Resources::small_font;
  78. // Add padding the size of lengthiest string:
  79. auto width = font->get_text_width(caption) +
  80. font->get_text_width("XX:XX:XX");
  81. if (width >= static_cast<float>(m_max_width))
  82. {
  83. m_max_width = static_cast<int>(width);
  84. }
  85. }
  86. }
  87. void
  88. Statistics::serialize_to_squirrel(SquirrelVM& vm) const
  89. {
  90. if (m_status != FINAL) return;
  91. vm.begin_table("statistics");
  92. vm.store_int("coins-collected", m_coins);
  93. vm.store_int("badguys-killed", m_badguys);
  94. vm.store_int("secrets-found", m_secrets);
  95. vm.store_float("time-needed", m_time);
  96. vm.store_int("coins-collected-total", m_total_coins);
  97. vm.store_int("badguys-killed-total", m_total_badguys);
  98. vm.store_int("secrets-found-total", m_total_secrets);
  99. vm.end_table("statistics");
  100. }
  101. void
  102. Statistics::unserialize_from_squirrel(SquirrelVM& vm)
  103. {
  104. try
  105. {
  106. vm.get_table_entry("statistics");
  107. vm.get_int("coins-collected", m_coins);
  108. vm.get_int("badguys-killed", m_badguys);
  109. vm.get_int("secrets-found", m_secrets);
  110. vm.get_float("time-needed", m_time);
  111. vm.get_int("coins-collected-total", m_total_coins);
  112. vm.get_int("badguys-killed-total", m_total_badguys);
  113. vm.get_int("secrets-found-total", m_total_secrets);
  114. sq_pop(vm.get_vm(), 1);
  115. m_status = FINAL;
  116. }
  117. catch(const std::exception&)
  118. {
  119. // ignore non-existing or malformed statistics table
  120. }
  121. }
  122. void
  123. Statistics::draw_worldmap_info(DrawingContext& context, float target_time)
  124. {
  125. if (m_status != FINAL) return;
  126. // check to see if screen size has been changed
  127. if (!(WMAP_INFO_TOP_Y1 == static_cast<float>(SCREEN_HEIGHT - 100))) {
  128. calculate_max_caption_length();
  129. WMAP_INFO_LEFT_X = context.get_width() - 32.f - static_cast<float>(m_max_width);
  130. WMAP_INFO_RIGHT_X = WMAP_INFO_LEFT_X + static_cast<float>(m_max_width);
  131. WMAP_INFO_TOP_Y1 = static_cast<float>(SCREEN_HEIGHT - 100);
  132. WMAP_INFO_TOP_Y2 = WMAP_INFO_TOP_Y1 + 16;
  133. }
  134. context.color().draw_text(
  135. Resources::small_font, "- " + _("Best Level Statistics") + " -",
  136. Vector((WMAP_INFO_LEFT_X + WMAP_INFO_RIGHT_X) / 2, WMAP_INFO_TOP_Y1),
  137. ALIGN_CENTER, LAYER_HUD,Statistics::header_color);
  138. std::string caption_buf;
  139. std::string stat_buf;
  140. float posy = WMAP_INFO_TOP_Y2;
  141. Color tcolor;
  142. for (int stat_no = 0; stat_no < 5; stat_no++) {
  143. tcolor = Statistics::header_color;
  144. switch (stat_no)
  145. {
  146. case 0:
  147. caption_buf = CAPTION_MAX_COINS;
  148. stat_buf = coins_to_string(m_coins, m_total_coins);
  149. if (m_coins >= m_total_coins)
  150. tcolor = Statistics::perfect_color;
  151. break;
  152. case 1:
  153. caption_buf = CAPTION_MAX_FRAGGING;
  154. stat_buf = frags_to_string(m_badguys, m_total_badguys);
  155. if (m_badguys >= m_total_badguys)
  156. tcolor = Statistics::perfect_color;
  157. break;
  158. case 2:
  159. caption_buf = CAPTION_MAX_SECRETS;
  160. stat_buf = secrets_to_string(m_secrets, m_total_secrets);
  161. if (m_secrets >= m_total_secrets)
  162. tcolor = Statistics::perfect_color;
  163. break;
  164. case 3:
  165. caption_buf = CAPTION_BEST_TIME;
  166. stat_buf = time_to_string(m_time);
  167. if ((m_time < target_time) || (target_time == 0.0f))
  168. tcolor = Statistics::perfect_color;
  169. break;
  170. case 4:
  171. if (target_time != 0.0f) { // display target time only if defined for level
  172. caption_buf = CAPTION_TARGET_TIME;
  173. stat_buf = time_to_string(target_time);
  174. if ((m_time < target_time) || (target_time == 0.0f))
  175. tcolor = Statistics::perfect_color;
  176. } else {
  177. caption_buf = "";
  178. stat_buf = "";
  179. }
  180. break;
  181. default:
  182. log_debug << "Invalid stat requested to be drawn" << std::endl;
  183. break;
  184. }
  185. context.color().draw_text(Resources::small_font, caption_buf, Vector(WMAP_INFO_LEFT_X, posy), ALIGN_LEFT, LAYER_HUD, Statistics::header_color);
  186. context.color().draw_text(Resources::small_font, stat_buf, Vector(WMAP_INFO_RIGHT_X, posy), ALIGN_RIGHT, LAYER_HUD, tcolor);
  187. posy += Resources::small_font->get_height() + 2;
  188. }
  189. }
  190. void
  191. Statistics::draw_endseq_panel(DrawingContext& context, Statistics* best_stats, const SurfacePtr& backdrop, float target_time)
  192. {
  193. if (m_status != FINAL) return;
  194. int box_w = 220+110+110;
  195. int box_h = 30+20+20+20;
  196. int box_x = static_cast<int>((static_cast<int>(context.get_width()) - box_w) / 2);
  197. int box_y = static_cast<int>(SCREEN_HEIGHT / 2) - box_h;
  198. int bd_w = static_cast<int>(backdrop->get_width());
  199. int bd_h = static_cast<int>(backdrop->get_height());
  200. int bd_x = static_cast<int>((static_cast<int>(context.get_width()) - bd_w) / 2);
  201. int bd_y = box_y + (box_h / 2) - (bd_h / 2);
  202. float col1_x = static_cast<float>(box_x);
  203. float col2_x = col1_x + 200.0f;
  204. float col3_x = col2_x + 130.0f;
  205. float row1_y = static_cast<float>(box_y);
  206. float row2_y = row1_y + 30.0f;
  207. float row3_y = row2_y + 20.0f;
  208. float row4_y = row3_y + 20.0f;
  209. float row5_y = row4_y + 20.0f;
  210. context.push_transform();
  211. context.set_alpha(0.5f);
  212. context.color().draw_surface(backdrop, Vector(static_cast<float>(bd_x), static_cast<float>(bd_y)), LAYER_HUD);
  213. context.pop_transform();
  214. context.color().draw_text(Resources::normal_font, _("You"), Vector(col2_x, row1_y), ALIGN_LEFT, LAYER_HUD, Statistics::header_color);
  215. if (best_stats)
  216. context.color().draw_text(Resources::normal_font, _("Best"), Vector(col3_x, row1_y), ALIGN_LEFT, LAYER_HUD, Statistics::header_color);
  217. context.color().draw_text(Resources::normal_font, _("Coins"), Vector(col2_x - 16.0f, static_cast<float>(row3_y)), ALIGN_RIGHT, LAYER_HUD, Statistics::header_color);
  218. Color tcolor;
  219. if (m_coins >= m_total_coins)
  220. tcolor = Statistics::perfect_color;
  221. else
  222. tcolor = Statistics::text_color;
  223. context.color().draw_text(Resources::normal_font, coins_to_string(m_coins, m_total_coins), Vector(col2_x, static_cast<float>(row3_y)), ALIGN_LEFT, LAYER_HUD, tcolor);
  224. if (best_stats) {
  225. int coins_best = (best_stats->m_coins > m_coins) ? best_stats->m_coins : m_coins;
  226. int total_coins_best = (best_stats->m_total_coins > m_total_coins) ? best_stats->m_total_coins : m_total_coins;
  227. if (coins_best >= total_coins_best)
  228. tcolor = Statistics::perfect_color;
  229. else
  230. tcolor = Statistics::text_color;
  231. context.color().draw_text(Resources::normal_font, coins_to_string(coins_best, total_coins_best), Vector(col3_x, static_cast<float>(row3_y)), ALIGN_LEFT, LAYER_HUD, tcolor);
  232. }
  233. if (m_badguys >= m_total_badguys)
  234. tcolor = Statistics::perfect_color;
  235. else
  236. tcolor = Statistics::text_color;
  237. context.color().draw_text(Resources::normal_font, _("Badguys"), Vector(col2_x - 16.0f, static_cast<float>(row4_y)), ALIGN_RIGHT, LAYER_HUD, Statistics::header_color);
  238. context.color().draw_text(Resources::normal_font, frags_to_string(m_badguys, m_total_badguys), Vector(col2_x, static_cast<float>(row4_y)), ALIGN_LEFT, LAYER_HUD, tcolor);
  239. if (best_stats) {
  240. int badguys_best = (best_stats->m_badguys > m_badguys) ? best_stats->m_badguys : m_badguys;
  241. int total_badguys_best = (best_stats->m_total_badguys > m_total_badguys) ? best_stats->m_total_badguys : m_total_badguys;
  242. if (badguys_best >= total_badguys_best)
  243. tcolor = Statistics::perfect_color;
  244. else
  245. tcolor = Statistics::text_color;
  246. context.color().draw_text(Resources::normal_font, frags_to_string(badguys_best, total_badguys_best), Vector(col3_x, row4_y), ALIGN_LEFT, LAYER_HUD, tcolor);
  247. }
  248. if (m_secrets >= m_total_secrets)
  249. tcolor = Statistics::perfect_color;
  250. else
  251. tcolor = Statistics::text_color;
  252. context.color().draw_text(Resources::normal_font, _("Secrets"), Vector(col2_x-16, row5_y), ALIGN_RIGHT, LAYER_HUD, Statistics::header_color);
  253. context.color().draw_text(Resources::normal_font, secrets_to_string(m_secrets, m_total_secrets), Vector(col2_x, row5_y), ALIGN_LEFT, LAYER_HUD, tcolor);
  254. if (best_stats) {
  255. int secrets_best = (best_stats->m_secrets > m_secrets) ? best_stats->m_secrets : m_secrets;
  256. int total_secrets_best = (best_stats->m_total_secrets > m_total_secrets) ? best_stats->m_total_secrets : m_total_secrets;
  257. if (secrets_best >= total_secrets_best)
  258. tcolor = Statistics::perfect_color;
  259. else
  260. tcolor = Statistics::text_color;
  261. context.color().draw_text(Resources::normal_font, secrets_to_string(secrets_best, total_secrets_best), Vector(col3_x, row5_y), ALIGN_LEFT, LAYER_HUD, tcolor);
  262. }
  263. 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, row2_y), ALIGN_RIGHT, LAYER_HUD, Statistics::header_color);
  267. context.color().draw_text(Resources::normal_font, time_to_string(m_time), Vector(col2_x, row2_y), ALIGN_LEFT, LAYER_HUD, tcolor);
  268. if (best_stats) {
  269. float time_best = (best_stats->m_time < m_time && best_stats->m_time > 0.0f) ? best_stats->m_time : m_time;
  270. if (target_time == 0.0f || (time_best != 0.0f && time_best < target_time))
  271. tcolor = Statistics::perfect_color;
  272. else
  273. tcolor = Statistics::text_color;
  274. context.color().draw_text(Resources::normal_font, time_to_string(time_best), Vector(col3_x, row2_y), ALIGN_LEFT, LAYER_HUD, tcolor);
  275. }
  276. }
  277. void
  278. Statistics::draw_ingame_stats(DrawingContext& context, bool on_pause_menu)
  279. {
  280. if (on_pause_menu || (m_cleared_coins && m_coins_time < 5.f))
  281. {
  282. std::string text(coins_to_string(m_coins, m_total_coins));
  283. float width = Resources::normal_font->get_text_width(text),
  284. height = Resources::normal_font->get_height(),
  285. x_offset = width + 75.f;
  286. if (!on_pause_menu)
  287. x_offset *= std::min(1.f, -std::abs(m_coins_time - 2.5f) + 2.5f);
  288. Vector pos(context.get_width() - x_offset,
  289. context.get_height() - height * 6.f - 20.f - 16.f);
  290. context.color().draw_filled_rect(Rectf(pos.x, pos.y, pos.x + width + 37.f,
  291. pos.y + height).grown(5.f),
  292. Color(0.f, 0.f, 0.f, 0.5f),
  293. 10.f, LAYER_HUD - 1);
  294. context.color().draw_text(Resources::normal_font, text, pos,
  295. FontAlignment::ALIGN_LEFT, LAYER_HUD,
  296. (m_coins < m_total_coins)
  297. ? Statistics::text_color
  298. : Statistics::perfect_color
  299. );
  300. context.color().draw_surface_scaled(coin_icon,
  301. Rectf(Vector(pos.x + width + 3.f, pos.y - 5.f), Sizef(32.f, 32.f)),
  302. LAYER_HUD);
  303. }
  304. if (on_pause_menu || (m_cleared_badguys && m_badguys_time < 5.f))
  305. {
  306. std::string text(frags_to_string(m_badguys, m_total_badguys));
  307. float width = Resources::normal_font->get_text_width(text),
  308. height = Resources::normal_font->get_height(),
  309. x_offset = width + 75.f;
  310. if (!on_pause_menu)
  311. x_offset *= std::min(1.f, -std::abs(m_badguys_time - 2.5f) + 2.5f);
  312. Vector pos(context.get_width() - x_offset,
  313. context.get_height() - height * 5.f - 10.f - 8.f);
  314. context.color().draw_filled_rect(Rectf(pos.x, pos.y, pos.x + width + 37.f,
  315. pos.y + height).grown(5.f),
  316. Color(0.f, 0.f, 0.f, 0.5f),
  317. 10.f, LAYER_HUD - 1);
  318. context.color().draw_text(Resources::normal_font, text, pos,
  319. FontAlignment::ALIGN_LEFT, LAYER_HUD,
  320. (m_badguys < m_total_badguys)
  321. ? Statistics::text_color
  322. : Statistics::perfect_color
  323. );
  324. context.color().draw_surface_scaled(badguy_icon,
  325. Rectf(Vector(pos.x + width + 3.f, pos.y - 5.f), Sizef(32.f, 32.f)),
  326. LAYER_HUD);
  327. }
  328. if (on_pause_menu || (m_cleared_secrets && m_secrets_time < 5.f))
  329. {
  330. std::string text(secrets_to_string(m_secrets, m_total_secrets));
  331. float width = Resources::normal_font->get_text_width(text),
  332. height = Resources::normal_font->get_height(),
  333. x_offset = width + 75.f;
  334. if (!on_pause_menu)
  335. x_offset *= std::min(1.f, -std::abs(m_secrets_time - 2.5f) + 2.5f);
  336. Vector pos(context.get_width() - x_offset,
  337. context.get_height() - height * 4.f);
  338. context.color().draw_filled_rect(Rectf(pos.x, pos.y, pos.x + width + 37.f,
  339. pos.y + height).grown(5.f),
  340. Color(0.f, 0.f, 0.f, 0.5f),
  341. 10.f, LAYER_HUD - 1);
  342. context.color().draw_text(Resources::normal_font, text, pos,
  343. FontAlignment::ALIGN_LEFT, LAYER_HUD,
  344. (m_secrets < m_total_secrets)
  345. ? Statistics::text_color
  346. : Statistics::perfect_color
  347. );
  348. context.color().draw_surface_scaled(secret_icon,
  349. Rectf(Vector(pos.x + width + 3.f, pos.y - 5.f), Sizef(32.f, 32.f)),
  350. LAYER_HUD);
  351. }
  352. }
  353. void
  354. Statistics::update_timers(float dt_sec)
  355. {
  356. if (m_cleared_coins)
  357. m_coins_time += dt_sec;
  358. if (m_cleared_badguys)
  359. m_badguys_time += dt_sec;
  360. if (m_cleared_secrets)
  361. m_secrets_time += dt_sec;
  362. }
  363. void
  364. Statistics::init(const Level& level)
  365. {
  366. m_status = ACCUMULATING;
  367. m_coins = 0;
  368. m_badguys = 0;
  369. m_secrets = 0;
  370. m_total_coins = level.get_total_coins();
  371. m_total_badguys = level.get_total_badguys();
  372. m_total_secrets = level.get_total_secrets();
  373. }
  374. void
  375. Statistics::finish(float time)
  376. {
  377. m_status = FINAL;
  378. m_time = time;
  379. }
  380. void
  381. Statistics::invalidate()
  382. {
  383. m_status = INVALID;
  384. }
  385. void
  386. Statistics::update(const Statistics& other)
  387. {
  388. if (other.m_status != FINAL) return;
  389. m_coins = std::max(m_coins, other.m_coins);
  390. m_badguys = std::max(m_badguys, other.m_badguys);
  391. m_secrets = std::max(m_secrets, other.m_secrets);
  392. if (m_time == 0)
  393. m_time = other.m_time;
  394. else
  395. m_time = std::min(m_time, other.m_time);
  396. m_total_coins = other.m_total_coins;
  397. m_total_badguys = other.m_total_badguys;
  398. m_total_secrets = other.m_total_secrets;
  399. m_coins = math::clamp(m_coins, 0, m_total_coins);
  400. m_badguys = math::clamp(m_badguys, 0, m_total_badguys);
  401. m_secrets = math::clamp(m_secrets, 0, m_total_secrets);
  402. m_status = FINAL;
  403. }
  404. bool
  405. Statistics::completed(const Statistics& stats, const float target_time) const
  406. {
  407. return (stats.m_coins == stats.m_total_coins &&
  408. stats.m_badguys == stats.m_total_badguys &&
  409. stats.m_secrets == stats.m_total_secrets &&
  410. ((target_time == 0.0f) || (stats.m_time <= target_time)));
  411. }
  412. std::string
  413. Statistics::coins_to_string(int coins, int total_coins)
  414. {
  415. std::ostringstream os;
  416. os << std::min(std::min(coins, total_coins), 999) << "/" << std::min(total_coins, 999);
  417. return os.str();
  418. }
  419. std::string
  420. Statistics::frags_to_string(int badguys, int total_badguys)
  421. {
  422. std::ostringstream os;
  423. os << std::min(std::min(badguys, total_badguys), 999) << "/" << std::min(total_badguys, 999);
  424. return os.str();
  425. }
  426. std::string
  427. Statistics::time_to_string(float time)
  428. {
  429. std::ostringstream os;
  430. if (time == 0.0f)
  431. {
  432. os << "--:--:--";
  433. }
  434. else
  435. {
  436. int time_csecs = static_cast<int>(time * 100);
  437. int mins = (time_csecs / 6000);
  438. int secs = (time_csecs % 6000) / 100;
  439. int cscs = (time_csecs % 6000) % 100;
  440. os << std::setw(2) << std::setfill('0') << mins << ":" << std::setw(2) << std::setfill('0') << secs << "." << std::setw(2) << std::setfill('0') << cscs;
  441. }
  442. return os.str();
  443. }
  444. void
  445. Statistics::check_coins()
  446. {
  447. if (m_cleared_coins)
  448. return;
  449. if (m_coins >= m_total_coins)
  450. {
  451. m_cleared_coins = true;
  452. SoundManager::current()->play("/sounds/coins_cleared.ogg", 0.5f);
  453. }
  454. }
  455. void
  456. Statistics::check_badguys()
  457. {
  458. if (m_cleared_badguys)
  459. return;
  460. if (m_badguys >= m_total_badguys)
  461. {
  462. m_cleared_badguys = true;
  463. SoundManager::current()->play("/sounds/retro_fall.wav", 0.5f);
  464. }
  465. }
  466. void
  467. Statistics::check_secrets()
  468. {
  469. if (m_cleared_secrets)
  470. return;
  471. if (m_secrets >= m_total_secrets)
  472. {
  473. m_cleared_secrets = true;
  474. SoundManager::current()->play("/sounds/tada.ogg", 0.5f);
  475. }
  476. }
  477. std::string
  478. Statistics::secrets_to_string(int secrets, int total_secrets)
  479. {
  480. std::ostringstream os;
  481. os << std::min(secrets, 999) << "/" << std::min(total_secrets, 999);
  482. return os.str();
  483. }
  484. /* EOF */