From 0fbe13667e5603a179656a1723a674fc97f00e0c Mon Sep 17 00:00:00 2001 From: Vankata453 <78196474+Vankata453@users.noreply.github.com> Date: Sun, 29 Sep 2024 13:54:44 +0300 Subject: [PATCH] Toggling level statistics (#3063) The coins, badguys and secrets level statistics can now be toggled from a level's settings. By default, coins and secrets are enabled, but badguys are not (due to a recent discussion regarding the difficulty of making it possible to kill all badguys in a level; can be discussed further). A level is shown as "perfect" on a worldmap if only the enabled statistics are maxed out. All code which draws statistics has been updated to incorporate this change as well. Statistics which are turned off will not be shown alongside others. --- src/supertux/level.cpp | 11 +- src/supertux/level.hpp | 2 +- src/supertux/level_parser.cpp | 20 +- src/supertux/levelintro.cpp | 28 +- src/supertux/menu/editor_level_menu.cpp | 6 +- src/supertux/statistics.cpp | 342 +++++++++++++++--------- src/supertux/statistics.hpp | 31 ++- src/worldmap/worldmap_sector.cpp | 2 +- 8 files changed, 293 insertions(+), 149 deletions(-) diff --git a/src/supertux/level.cpp b/src/supertux/level.cpp index 28e2389ff35..77ff3413d34 100644 --- a/src/supertux/level.cpp +++ b/src/supertux/level.cpp @@ -68,12 +68,12 @@ Level::~Level() } void -Level::initialize() +Level::initialize(const Statistics::Preferences& stat_preferences) { if (m_sectors.empty()) throw std::runtime_error("Level has no sectors!"); - m_stats.init(*this); + m_stats.init(*this, stat_preferences); Savegame* savegame = (GameSession::current() && !Editor::current() ? &GameSession::current()->get_savegame() : nullptr); @@ -195,6 +195,13 @@ Level::save(Writer& writer) if (!m_wmselect_bkg.empty()) writer.write("bkg", m_wmselect_bkg); + if (!m_is_worldmap) + { + writer.start_list("statistics"); + m_stats.get_preferences().write(writer); + writer.end_list("statistics"); + } + for (auto& sector : m_sectors) { sector->save(writer); } diff --git a/src/supertux/level.hpp b/src/supertux/level.hpp index 9713bbaa932..358e069aa02 100644 --- a/src/supertux/level.hpp +++ b/src/supertux/level.hpp @@ -70,7 +70,7 @@ class Level final const std::string& get_license() const { return m_license; } private: - void initialize(); + void initialize(const Statistics::Preferences& stat_preferences); void save(Writer& writer); void load_old_format(const ReaderMapping& reader); diff --git a/src/supertux/level_parser.cpp b/src/supertux/level_parser.cpp index 3b3b87874e5..63584112e5f 100644 --- a/src/supertux/level_parser.cpp +++ b/src/supertux/level_parser.cpp @@ -153,13 +153,17 @@ LevelParser::load(const ReaderDocument& doc) throw std::runtime_error("file is not a supertux-level file."); auto level = root.get_mapping(); + Statistics::Preferences stat_preferences; int version = 1; level.get("version", version); - if (version == 1) { + if (version == 1) + { log_info << "[" << doc.get_filename() << "] level uses old format: version 1" << std::endl; load_old_format(level); - } else if (version == 2 || version == 3) { + } + else if (version == 2 || version == 3) + { level.get("tileset", m_level.m_tileset); level.get("name", m_level.m_name); @@ -173,6 +177,10 @@ LevelParser::load(const ReaderDocument& doc) level.get("icon-locked", m_level.m_icon_locked); level.get("bkg", m_level.m_wmselect_bkg); + std::optional level_stat_preferences; + if (level.get("statistics", level_stat_preferences)) + stat_preferences.parse(*level_stat_preferences); + auto iter = level.get_iter(); while (iter.next()) { @@ -189,11 +197,13 @@ LevelParser::load(const ReaderDocument& doc) << m_level.m_name << "\". You might not be allowed to share it." << std::endl; } - } else { + } + else + { log_warning << "[" << doc.get_filename() << "] level format version " << version << " is not supported" << std::endl; } - m_level.initialize(); + m_level.initialize(stat_preferences); } void @@ -204,8 +214,6 @@ LevelParser::load_old_format(const ReaderMapping& reader) auto sector = SectorParser::from_reader_old_format(m_level, reader, m_editable); m_level.add_sector(std::move(sector)); - - m_level.initialize(); } void diff --git a/src/supertux/levelintro.cpp b/src/supertux/levelintro.cpp index 16c4d58000b..0ee7d377adb 100644 --- a/src/supertux/levelintro.cpp +++ b/src/supertux/levelintro.cpp @@ -178,15 +178,25 @@ LevelIntro::draw(Compositor& compositor) py += static_cast(Resources::normal_font->get_height()); - draw_stats_line(context, py, _("Coins"), - Statistics::coins_to_string(m_best_level_statistics->get_coins(), stats.m_total_coins), - m_best_level_statistics->get_coins() >= stats.m_total_coins); - draw_stats_line(context, py, _("Badguys killed"), - Statistics::frags_to_string(m_best_level_statistics->get_badguys(), stats.m_total_badguys), - m_best_level_statistics->get_badguys() >= stats.m_total_badguys); - draw_stats_line(context, py, _("Secrets"), - Statistics::secrets_to_string(m_best_level_statistics->get_secrets(), stats.m_total_secrets), - m_best_level_statistics->get_secrets() >= stats.m_total_secrets); + const Statistics::Preferences& preferences = m_level.m_stats.get_preferences(); + if (preferences.enable_coins) + { + draw_stats_line(context, py, _("Coins"), + Statistics::coins_to_string(m_best_level_statistics->get_coins(), stats.m_total_coins), + m_best_level_statistics->get_coins() >= stats.m_total_coins); + } + if (preferences.enable_badguys) + { + draw_stats_line(context, py, _("Badguys killed"), + Statistics::frags_to_string(m_best_level_statistics->get_badguys(), stats.m_total_badguys), + m_best_level_statistics->get_badguys() >= stats.m_total_badguys); + } + if (preferences.enable_secrets) + { + draw_stats_line(context, py, _("Secrets"), + Statistics::secrets_to_string(m_best_level_statistics->get_secrets(), stats.m_total_secrets), + m_best_level_statistics->get_secrets() >= stats.m_total_secrets); + } bool targetTimeBeaten = m_level.m_target_time == 0.0f || (m_best_level_statistics->get_time() != 0.0f && m_best_level_statistics->get_time() < m_level.m_target_time); draw_stats_line(context, py, _("Best time"), diff --git a/src/supertux/menu/editor_level_menu.cpp b/src/supertux/menu/editor_level_menu.cpp index 3fb23b67bea..48e12400739 100644 --- a/src/supertux/menu/editor_level_menu.cpp +++ b/src/supertux/menu/editor_level_menu.cpp @@ -37,8 +37,12 @@ EditorLevelMenu::EditorLevelMenu() : add_textfield(_("Level Note"), &(level->m_note)); add_file(_("Tileset"), &(level->m_tileset), std::vector(1, ".strf"), {}, true); - if (!is_worldmap) { + if (!is_worldmap) + { add_floatfield(_("Target Time"), &(level->m_target_time)); + + add_hl(); + level->m_stats.add_preferences_to_menu(*this); } add_hl(); diff --git a/src/supertux/statistics.cpp b/src/supertux/statistics.cpp index e5bafd36a15..e9086c3e32a 100644 --- a/src/supertux/statistics.cpp +++ b/src/supertux/statistics.cpp @@ -25,17 +25,54 @@ #include #include "audio/sound_manager.hpp" +#include "gui/menu.hpp" #include "math/util.hpp" #include "supertux/globals.hpp" #include "supertux/level.hpp" #include "supertux/resources.hpp" #include "util/gettext.hpp" #include "util/log.hpp" +#include "util/reader_mapping.hpp" +#include "util/writer.hpp" #include "video/drawing_context.hpp" #include "video/surface.hpp" #include "video/video_system.hpp" #include "video/viewport.hpp" +static const float INGAME_STATS_DISTANCE = 1.7f; + +Statistics::Preferences::Preferences() : + enable_coins(true), + enable_badguys(false), + enable_secrets(true) +{ +} + +void +Statistics::Preferences::parse(const ReaderMapping& reader) +{ + reader.get("enable-coins", enable_coins); + reader.get("enable-badguys", enable_badguys); + reader.get("enable-secrets", enable_secrets); +} + +void +Statistics::Preferences::write(Writer& writer) const +{ + writer.write("enable-coins", enable_coins); + writer.write("enable-badguys", enable_badguys); + writer.write("enable-secrets", enable_secrets); +} + +void +Statistics::Preferences::add_to_menu(Menu& menu) +{ + menu.add_toggle(-1, _("Enable Coins Statistic"), &enable_coins); + menu.add_toggle(-1, _("Enable Badguys Statistic"), &enable_badguys); + menu.add_toggle(-1, _("Enable Secrets Statistic"), &enable_secrets); +} + + Statistics::Statistics() : m_status(INVALID), m_total_coins(), @@ -51,6 +88,7 @@ Statistics::Statistics() : m_coins_time(0.f), m_badguys_time(0.f), m_secrets_time(0.f), + m_preferences(), m_max_width(256), CAPTION_MAX_COINS(_("Max coins collected:")), CAPTION_MAX_FRAGGING(_("Max fragging:")), @@ -130,6 +168,12 @@ Statistics::unserialize_from_squirrel(const ssq::Table& table) } } +void +Statistics::add_preferences_to_menu(Menu& menu) +{ + m_preferences.add_to_menu(menu); +} + void Statistics::draw_worldmap_info(DrawingContext& context, float target_time) { @@ -159,18 +203,27 @@ Statistics::draw_worldmap_info(DrawingContext& context, float target_time) switch (stat_no) { case 0: + if (!m_preferences.enable_coins) + continue; + caption_buf = CAPTION_MAX_COINS; stat_buf = coins_to_string(m_coins, m_total_coins); if (m_coins >= m_total_coins) tcolor = Statistics::perfect_color; break; case 1: + if (!m_preferences.enable_badguys) + continue; + caption_buf = CAPTION_MAX_FRAGGING; stat_buf = frags_to_string(m_badguys, m_total_badguys); if (m_badguys >= m_total_badguys) tcolor = Statistics::perfect_color; break; case 2: + if (!m_preferences.enable_secrets) + continue; + caption_buf = CAPTION_MAX_SECRETS; stat_buf = secrets_to_string(m_secrets, m_total_secrets); if (m_secrets >= m_total_secrets) @@ -195,7 +248,7 @@ Statistics::draw_worldmap_info(DrawingContext& context, float target_time) break; default: log_debug << "Invalid stat requested to be drawn" << std::endl; - break; + continue; } context.color().draw_text(Resources::small_font, caption_buf, Vector(WMAP_INFO_LEFT_X, posy), ALIGN_LEFT, LAYER_HUD, Statistics::header_color); @@ -223,159 +276,192 @@ Statistics::draw_endseq_panel(DrawingContext& context, Statistics* best_stats, c float col2_x = col1_x + 200.0f; float col3_x = col2_x + 130.0f; - float row1_y = static_cast(box_y); - float row2_y = row1_y + 30.0f; - float row3_y = row2_y + 20.0f; - float row4_y = row3_y + 20.0f; - float row5_y = row4_y + 20.0f; + float y_offset = 47.f; + if (m_preferences.enable_coins) + y_offset -= 9.f; + if (m_preferences.enable_badguys) + y_offset -= 9.f; + if (m_preferences.enable_secrets) + y_offset -= 9.f; + + float y = static_cast(box_y); context.push_transform(); context.set_alpha(0.5f); context.color().draw_surface(backdrop, Vector(static_cast(bd_x), static_cast(bd_y)), LAYER_HUD); context.pop_transform(); - context.color().draw_text(Resources::normal_font, _("You"), Vector(col2_x, row1_y), ALIGN_LEFT, LAYER_HUD, Statistics::header_color); + context.color().draw_text(Resources::normal_font, _("You"), Vector(col2_x, y), ALIGN_LEFT, LAYER_HUD, Statistics::header_color); if (best_stats) - context.color().draw_text(Resources::normal_font, _("Best"), Vector(col3_x, row1_y), ALIGN_LEFT, LAYER_HUD, Statistics::header_color); + context.color().draw_text(Resources::normal_font, _("Best"), Vector(col3_x, y), ALIGN_LEFT, LAYER_HUD, Statistics::header_color); - context.color().draw_text(Resources::normal_font, _("Coins"), Vector(col2_x - 16.0f, static_cast(row3_y)), ALIGN_RIGHT, LAYER_HUD, Statistics::header_color); + y += 10.f + y_offset; - Color tcolor; - if (m_coins >= m_total_coins) + Color tcolor = Statistics::text_color; + if (target_time == 0.0f || (m_time != 0.0f && m_time < target_time)) tcolor = Statistics::perfect_color; - else - tcolor = Statistics::text_color; - context.color().draw_text(Resources::normal_font, coins_to_string(m_coins, m_total_coins), Vector(col2_x, static_cast(row3_y)), ALIGN_LEFT, LAYER_HUD, tcolor); - if (best_stats) { - int coins_best = (best_stats->m_coins > m_coins) ? best_stats->m_coins : m_coins; - int total_coins_best = (best_stats->m_total_coins > m_total_coins) ? best_stats->m_total_coins : m_total_coins; - if (coins_best >= total_coins_best) + context.color().draw_text(Resources::normal_font, _("Time"), Vector(col2_x - 16.f, y), ALIGN_RIGHT, LAYER_HUD, Statistics::header_color); + context.color().draw_text(Resources::normal_font, time_to_string(m_time), Vector(col2_x, y), ALIGN_LEFT, LAYER_HUD, tcolor); + if (best_stats) + { + float time_best = (best_stats->m_time < m_time && best_stats->m_time > 0.0f) ? best_stats->m_time : m_time; + if (target_time == 0.0f || (time_best != 0.0f && time_best < target_time)) tcolor = Statistics::perfect_color; else tcolor = Statistics::text_color; - context.color().draw_text(Resources::normal_font, coins_to_string(coins_best, total_coins_best), Vector(col3_x, static_cast(row3_y)), ALIGN_LEFT, LAYER_HUD, tcolor); + context.color().draw_text(Resources::normal_font, time_to_string(time_best), Vector(col3_x, y), ALIGN_LEFT, LAYER_HUD, tcolor); } - if (m_badguys >= m_total_badguys) - tcolor = Statistics::perfect_color; - else - tcolor = Statistics::text_color; - context.color().draw_text(Resources::normal_font, _("Badguys"), Vector(col2_x - 16.0f, static_cast(row4_y)), ALIGN_RIGHT, LAYER_HUD, Statistics::header_color); - context.color().draw_text(Resources::normal_font, frags_to_string(m_badguys, m_total_badguys), Vector(col2_x, static_cast(row4_y)), ALIGN_LEFT, LAYER_HUD, tcolor); - if (best_stats) { - int badguys_best = (best_stats->m_badguys > m_badguys) ? best_stats->m_badguys : m_badguys; - int total_badguys_best = (best_stats->m_total_badguys > m_total_badguys) ? best_stats->m_total_badguys : m_total_badguys; - if (badguys_best >= total_badguys_best) - tcolor = Statistics::perfect_color; - else - tcolor = Statistics::text_color; - 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); + if (m_preferences.enable_coins) + { + y += y_offset; + + context.color().draw_text(Resources::normal_font, _("Coins"), Vector(col2_x - 16.f, y), ALIGN_RIGHT, LAYER_HUD, Statistics::header_color); + + if (m_coins >= m_total_coins) + tcolor = Statistics::perfect_color; + else + tcolor = Statistics::text_color; + context.color().draw_text(Resources::normal_font, coins_to_string(m_coins, m_total_coins), Vector(col2_x, y), ALIGN_LEFT, LAYER_HUD, tcolor); + + if (best_stats) + { + int coins_best = (best_stats->m_coins > m_coins) ? best_stats->m_coins : m_coins; + int total_coins_best = (best_stats->m_total_coins > m_total_coins) ? best_stats->m_total_coins : m_total_coins; + if (coins_best >= total_coins_best) + tcolor = Statistics::perfect_color; + else + tcolor = Statistics::text_color; + context.color().draw_text(Resources::normal_font, coins_to_string(coins_best, total_coins_best), Vector(col3_x, y), ALIGN_LEFT, LAYER_HUD, tcolor); + } } - if (m_secrets >= m_total_secrets) - tcolor = Statistics::perfect_color; - else - tcolor = Statistics::text_color; - context.color().draw_text(Resources::normal_font, _("Secrets"), Vector(col2_x-16, row5_y), ALIGN_RIGHT, LAYER_HUD, Statistics::header_color); - 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); - if (best_stats) { - int secrets_best = (best_stats->m_secrets > m_secrets) ? best_stats->m_secrets : m_secrets; - int total_secrets_best = (best_stats->m_total_secrets > m_total_secrets) ? best_stats->m_total_secrets : m_total_secrets; - if (secrets_best >= total_secrets_best) + if (m_preferences.enable_badguys) + { + y += y_offset; + + if (m_badguys >= m_total_badguys) tcolor = Statistics::perfect_color; else tcolor = Statistics::text_color; - 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); + context.color().draw_text(Resources::normal_font, _("Badguys"), Vector(col2_x - 16.f, y), ALIGN_RIGHT, LAYER_HUD, Statistics::header_color); + context.color().draw_text(Resources::normal_font, frags_to_string(m_badguys, m_total_badguys), Vector(col2_x, y), ALIGN_LEFT, LAYER_HUD, tcolor); + + if (best_stats) + { + int badguys_best = (best_stats->m_badguys > m_badguys) ? best_stats->m_badguys : m_badguys; + int total_badguys_best = (best_stats->m_total_badguys > m_total_badguys) ? best_stats->m_total_badguys : m_total_badguys; + if (badguys_best >= total_badguys_best) + tcolor = Statistics::perfect_color; + else + tcolor = Statistics::text_color; + context.color().draw_text(Resources::normal_font, frags_to_string(badguys_best, total_badguys_best), Vector(col3_x, y), ALIGN_LEFT, LAYER_HUD, tcolor); + } } - tcolor = Statistics::text_color; - if (target_time == 0.0f || (m_time != 0.0f && m_time < target_time)) - tcolor = Statistics::perfect_color; + if (m_preferences.enable_secrets) + { + y += y_offset; - context.color().draw_text(Resources::normal_font, _("Time"), Vector(col2_x - 16, row2_y), ALIGN_RIGHT, LAYER_HUD, Statistics::header_color); - context.color().draw_text(Resources::normal_font, time_to_string(m_time), Vector(col2_x, row2_y), ALIGN_LEFT, LAYER_HUD, tcolor); - if (best_stats) { - float time_best = (best_stats->m_time < m_time && best_stats->m_time > 0.0f) ? best_stats->m_time : m_time; - if (target_time == 0.0f || (time_best != 0.0f && time_best < target_time)) + if (m_secrets >= m_total_secrets) tcolor = Statistics::perfect_color; else tcolor = Statistics::text_color; - context.color().draw_text(Resources::normal_font, time_to_string(time_best), Vector(col3_x, row2_y), ALIGN_LEFT, LAYER_HUD, tcolor); + context.color().draw_text(Resources::normal_font, _("Secrets"), Vector(col2_x - 16.f, y), ALIGN_RIGHT, LAYER_HUD, Statistics::header_color); + context.color().draw_text(Resources::normal_font, secrets_to_string(m_secrets, m_total_secrets), Vector(col2_x, y), ALIGN_LEFT, LAYER_HUD, tcolor); + + if (best_stats) + { + int secrets_best = (best_stats->m_secrets > m_secrets) ? best_stats->m_secrets : m_secrets; + int total_secrets_best = (best_stats->m_total_secrets > m_total_secrets) ? best_stats->m_total_secrets : m_total_secrets; + if (secrets_best >= total_secrets_best) + tcolor = Statistics::perfect_color; + else + tcolor = Statistics::text_color; + context.color().draw_text(Resources::normal_font, secrets_to_string(secrets_best, total_secrets_best), Vector(col3_x, y), ALIGN_LEFT, LAYER_HUD, tcolor); + } } } void Statistics::draw_ingame_stats(DrawingContext& context, bool on_pause_menu) { - if (on_pause_menu || (m_cleared_coins && m_coins_time < 5.f)) - { - std::string text(coins_to_string(m_coins, m_total_coins)); - float width = Resources::normal_font->get_text_width(text), - height = Resources::normal_font->get_height(), - x_offset = width + 75.f; - - if (!on_pause_menu) - x_offset *= std::min(1.f, -std::abs(m_coins_time - 2.5f) + 2.5f); + const float height = Resources::normal_font->get_height(); + float y = context.get_height() - height * 4.f; - Vector pos(context.get_width() - x_offset, - context.get_height() - height * 6.f - 20.f - 16.f); + if (m_preferences.enable_secrets) + { + if (on_pause_menu || (m_cleared_secrets && m_secrets_time < 5.f)) + { + std::string text(secrets_to_string(m_secrets, m_total_secrets)); + float width = Resources::normal_font->get_text_width(text), + x_offset = width + 75.f; + + if (!on_pause_menu) + x_offset *= std::min(1.f, -std::abs(m_secrets_time - 2.5f) + 2.5f); + + Vector pos(context.get_width() - x_offset, y); + + context.color().draw_filled_rect(Rectf(pos.x, pos.y, pos.x + width + 37.f, + pos.y + height).grown(5.f), + Color(0.f, 0.f, 0.f, 0.5f), + 10.f, LAYER_HUD - 1); + context.color().draw_text(Resources::normal_font, text, pos, + FontAlignment::ALIGN_LEFT, LAYER_HUD, + (m_secrets < m_total_secrets) + ? Statistics::text_color + : Statistics::perfect_color + ); + context.color().draw_surface_scaled(secret_icon, + Rectf(Vector(pos.x + width + 3.f, pos.y - 5.f), Sizef(32.f, 32.f)), + LAYER_HUD); + } - context.color().draw_filled_rect(Rectf(pos.x, pos.y, pos.x + width + 37.f, - pos.y + height).grown(5.f), - Color(0.f, 0.f, 0.f, 0.5f), - 10.f, LAYER_HUD - 1); - context.color().draw_text(Resources::normal_font, text, pos, - FontAlignment::ALIGN_LEFT, LAYER_HUD, - (m_coins < m_total_coins) - ? Statistics::text_color - : Statistics::perfect_color - ); - context.color().draw_surface_scaled(coin_icon, - Rectf(Vector(pos.x + width + 3.f, pos.y - 5.f), Sizef(32.f, 32.f)), - LAYER_HUD); + y -= height * INGAME_STATS_DISTANCE; } - if (on_pause_menu || (m_cleared_badguys && m_badguys_time < 5.f)) + if (m_preferences.enable_badguys) { - std::string text(frags_to_string(m_badguys, m_total_badguys)); - float width = Resources::normal_font->get_text_width(text), - height = Resources::normal_font->get_height(), - x_offset = width + 75.f; - - if (!on_pause_menu) - x_offset *= std::min(1.f, -std::abs(m_badguys_time - 2.5f) + 2.5f); - - Vector pos(context.get_width() - x_offset, - context.get_height() - height * 5.f - 10.f - 8.f); + if (on_pause_menu || (m_cleared_badguys && m_badguys_time < 5.f)) + { + std::string text(frags_to_string(m_badguys, m_total_badguys)); + float width = Resources::normal_font->get_text_width(text), + x_offset = width + 75.f; + + if (!on_pause_menu) + x_offset *= std::min(1.f, -std::abs(m_badguys_time - 2.5f) + 2.5f); + + Vector pos(context.get_width() - x_offset, y); + + context.color().draw_filled_rect(Rectf(pos.x, pos.y, pos.x + width + 37.f, + pos.y + height).grown(5.f), + Color(0.f, 0.f, 0.f, 0.5f), + 10.f, LAYER_HUD - 1); + context.color().draw_text(Resources::normal_font, text, pos, + FontAlignment::ALIGN_LEFT, LAYER_HUD, + (m_badguys < m_total_badguys) + ? Statistics::text_color + : Statistics::perfect_color + ); + context.color().draw_surface_scaled(badguy_icon, + Rectf(Vector(pos.x + width + 3.f, pos.y - 5.f), Sizef(32.f, 32.f)), + LAYER_HUD); + } - context.color().draw_filled_rect(Rectf(pos.x, pos.y, pos.x + width + 37.f, - pos.y + height).grown(5.f), - Color(0.f, 0.f, 0.f, 0.5f), - 10.f, LAYER_HUD - 1); - context.color().draw_text(Resources::normal_font, text, pos, - FontAlignment::ALIGN_LEFT, LAYER_HUD, - (m_badguys < m_total_badguys) - ? Statistics::text_color - : Statistics::perfect_color - ); - context.color().draw_surface_scaled(badguy_icon, - Rectf(Vector(pos.x + width + 3.f, pos.y - 5.f), Sizef(32.f, 32.f)), - LAYER_HUD); + y -= height * INGAME_STATS_DISTANCE; } - if (on_pause_menu || (m_cleared_secrets && m_secrets_time < 5.f)) + if (m_preferences.enable_coins && (on_pause_menu || (m_cleared_coins && m_coins_time < 5.f))) { - std::string text(secrets_to_string(m_secrets, m_total_secrets)); + std::string text(coins_to_string(m_coins, m_total_coins)); float width = Resources::normal_font->get_text_width(text), - height = Resources::normal_font->get_height(), x_offset = width + 75.f; if (!on_pause_menu) - x_offset *= std::min(1.f, -std::abs(m_secrets_time - 2.5f) + 2.5f); + x_offset *= std::min(1.f, -std::abs(m_coins_time - 2.5f) + 2.5f); - Vector pos(context.get_width() - x_offset, - context.get_height() - height * 4.f); + Vector pos(context.get_width() - x_offset, y); context.color().draw_filled_rect(Rectf(pos.x, pos.y, pos.x + width + 37.f, pos.y + height).grown(5.f), @@ -383,11 +469,11 @@ Statistics::draw_ingame_stats(DrawingContext& context, bool on_pause_menu) 10.f, LAYER_HUD - 1); context.color().draw_text(Resources::normal_font, text, pos, FontAlignment::ALIGN_LEFT, LAYER_HUD, - (m_secrets < m_total_secrets) + (m_coins < m_total_coins) ? Statistics::text_color : Statistics::perfect_color ); - context.color().draw_surface_scaled(secret_icon, + context.color().draw_surface_scaled(coin_icon, Rectf(Vector(pos.x + width + 3.f, pos.y - 5.f), Sizef(32.f, 32.f)), LAYER_HUD); } @@ -407,7 +493,7 @@ Statistics::update_timers(float dt_sec) } void -Statistics::init(const Level& level) +Statistics::init(const Level& level, const Statistics::Preferences& preferences) { m_status = ACCUMULATING; @@ -418,6 +504,8 @@ Statistics::init(const Level& level) m_total_coins = level.get_total_coins(); m_total_badguys = level.get_total_badguys(); m_total_secrets = level.get_total_secrets(); + + m_preferences = preferences; } void @@ -450,6 +538,8 @@ Statistics::update(const Statistics& other) m_total_badguys = other.m_total_badguys; m_total_secrets = other.m_total_secrets; + m_preferences = other.m_preferences; + m_coins = math::clamp(m_coins, 0, m_total_coins); m_badguys = math::clamp(m_badguys, 0, m_total_badguys); m_secrets = math::clamp(m_secrets, 0, m_total_secrets); @@ -457,12 +547,12 @@ Statistics::update(const Statistics& other) } bool -Statistics::completed(const Statistics& stats, const float target_time) const +Statistics::completed(const float target_time) const { - return (stats.m_coins == stats.m_total_coins && - stats.m_badguys == stats.m_total_badguys && - stats.m_secrets == stats.m_total_secrets && - ((target_time == 0.0f) || (stats.m_time <= target_time))); + return ((!m_preferences.enable_coins || m_coins == m_total_coins) && + (!m_preferences.enable_badguys || m_badguys == m_total_badguys) && + (!m_preferences.enable_secrets || m_secrets == m_total_secrets) && + (target_time == 0.0f || m_time <= target_time)); } std::string @@ -481,6 +571,14 @@ Statistics::frags_to_string(int badguys, int total_badguys) return os.str(); } +std::string +Statistics::secrets_to_string(int secrets, int total_secrets) +{ + std::ostringstream os; + os << std::min(secrets, 999) << "/" << std::min(total_secrets, 999); + return os.str(); +} + std::string Statistics::time_to_string(float time) { @@ -504,7 +602,7 @@ Statistics::time_to_string(float time) void Statistics::check_coins() { - if (m_cleared_coins) + if (!m_preferences.enable_coins || m_cleared_coins) return; if (m_coins >= m_total_coins) @@ -517,7 +615,7 @@ Statistics::check_coins() void Statistics::check_badguys() { - if (m_cleared_badguys) + if (!m_preferences.enable_badguys || m_cleared_badguys) return; if (m_badguys >= m_total_badguys) @@ -530,7 +628,7 @@ Statistics::check_badguys() void Statistics::check_secrets() { - if (m_cleared_secrets) + if (!m_preferences.enable_secrets || m_cleared_secrets) return; if (m_secrets >= m_total_secrets) @@ -540,12 +638,4 @@ Statistics::check_secrets() } } -std::string -Statistics::secrets_to_string(int secrets, int total_secrets) -{ - std::ostringstream os; - os << std::min(secrets, 999) << "/" << std::min(total_secrets, 999); - return os.str(); -} - /* EOF */ diff --git a/src/supertux/statistics.hpp b/src/supertux/statistics.hpp index 3a61934919b..640056b53d4 100644 --- a/src/supertux/statistics.hpp +++ b/src/supertux/statistics.hpp @@ -25,6 +25,9 @@ class DrawingContext; class Level; +class Menu; +class ReaderMapping; +class Writer; namespace ssq { class Table; @@ -43,8 +46,25 @@ class Statistics final public: static std::string coins_to_string(int coins, int total_coins); static std::string frags_to_string(int badguys, int total_badguys); - static std::string time_to_string(float time); static std::string secrets_to_string(int secrets, int total_secrets); + static std::string time_to_string(float time); + +public: + class Preferences final + { + public: + Preferences(); + + void parse(const ReaderMapping& reader); + void write(Writer& writer) const; + + void add_to_menu(Menu& menu); + + public: + bool enable_coins; + bool enable_badguys; + bool enable_secrets; + }; public: enum Status { INVALID, ACCUMULATING, FINAL }; @@ -58,6 +78,9 @@ class Statistics final /** unserialize statistics object from squirrel table "statistics" */ void unserialize_from_squirrel(const ssq::Table& table); + const Preferences& get_preferences() const { return m_preferences; } + void add_preferences_to_menu(Menu& menu); + void draw_worldmap_info(DrawingContext& context, float target_time); /**< draw worldmap stat HUD */ void draw_endseq_panel(DrawingContext& context, Statistics* best_stats, const SurfacePtr& backdrop, float target_time); /**< draw panel shown during level's end sequence */ void draw_ingame_stats(DrawingContext& context, bool on_pause_menu); /**< draw in-game stats */ @@ -65,12 +88,12 @@ class Statistics final /** Updates the timers for in-game stats rendering. Should be used from the same object that calls draw_ingame_stats(). */ void update_timers(float dt_sec); - void init(const Level& level); + void init(const Level& level, const Preferences& preferences); void finish(float time); void invalidate(); void update(const Statistics& stats); /**< Given another Statistics object finds the best of each one */ - bool completed(const Statistics& stats, const float target_time) const; /* Check if stats match total stats */ + bool completed(const float target_time) const; /* Check if stats match total stats */ int get_coins() const { return m_coins; } int get_badguys() const { return m_badguys; } @@ -112,6 +135,8 @@ class Statistics final m_badguys_time, m_secrets_time; + Preferences m_preferences; + private: int m_max_width; /** < Gets the max width of a stats line, 255 by default */ diff --git a/src/worldmap/worldmap_sector.cpp b/src/worldmap/worldmap_sector.cpp index c7d4ada2ca4..3e38ac2eecd 100644 --- a/src/worldmap/worldmap_sector.cpp +++ b/src/worldmap/worldmap_sector.cpp @@ -474,7 +474,7 @@ WorldMapSector::finished_level(Level* gamelevel) // deal with statistics level->get_statistics().update(gamelevel->m_stats); - if (level->get_statistics().completed(level->get_statistics(), level->get_target_time())) { + if (level->get_statistics().completed(level->get_target_time())) { level->set_perfect(true); }