diff --git a/.github/workflows/gnulinux.yml b/.github/workflows/gnulinux.yml index ddf1eb65afc..6c7d2c7b333 100644 --- a/.github/workflows/gnulinux.yml +++ b/.github/workflows/gnulinux.yml @@ -212,7 +212,7 @@ jobs: doxygen docs/Doxyfile - name: Package - if: ${{ matrix.arch != '32' }} + if: matrix.arch != '32' env: OS_NAME: ${{ matrix.os }} ARCH: ${{ matrix.arch }} # TODO: Working Linux 32-bit packaging @@ -224,6 +224,7 @@ jobs: run: ../.ci_scripts/package.sh - name: Upload AppImage + if: matrix.build_type != 'Debug' && matrix.glbinding == 'OFF' uses: actions/upload-artifact@v4 with: name: "${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.compiler }}-${{ matrix.build_type }}${{ matrix.glbinding == 'ON' && '-glbinding' || '' }}-appimage" diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 4e8cab09ed5..59d3402ac3b 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -116,6 +116,7 @@ jobs: run: ../.ci_scripts/package.sh - uses: actions/upload-artifact@v4 + if: matrix.build_type != 'Debug' && matrix.glbinding == 'OFF' with: name: "${{ matrix.os }}-${{ matrix.build_type }}${{ matrix.glbinding == 'ON' && '-glbinding' || '' }}-dmg" path: build/upload/*.dmg diff --git a/.github/workflows/ubuntu-touch.yml b/.github/workflows/ubuntu-touch.yml index e85ee4f9781..f442de00677 100644 --- a/.github/workflows/ubuntu-touch.yml +++ b/.github/workflows/ubuntu-touch.yml @@ -88,6 +88,7 @@ jobs: ~/.local/bin/clickable build --verbose ${BUILD_TYPE} --arch ${ARCH} - uses: actions/upload-artifact@v4 + if: matrix.build_type != 'Debug' with: name: "clickable-${{ matrix.arch }}-${{ matrix.build_type }}-${{ matrix.opengl }}-click" path: build.clickable/*.click diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 0376ea86100..5f51cce5aab 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -85,6 +85,7 @@ jobs: mv supertux2* upload/ - uses: actions/upload-artifact@v4 + if: matrix.build_type != 'Debug' with: name: "wasm32-emscripten-${{ matrix.build_type }}-html" path: build/upload/* diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 6ecb33548ae..fa44fa4f70f 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -105,6 +105,7 @@ jobs: "./$Env:BUILD_TYPE/run_tests.exe" - name: Package MSI Installer + if: matrix.build_type != 'Debug' && matrix.glbinding == 'OFF' shell: pwsh working-directory: build env: @@ -148,6 +149,7 @@ jobs: #> - name: Upload Portable Package + if: matrix.build_type != 'Debug' && matrix.glbinding == 'OFF' uses: actions/upload-artifact@v4 with: name: "windows-${{ matrix.arch }}-${{ matrix.build_type }}${{ matrix.glbinding == 'ON' && '-glbinding' || '' }}-portable" 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); }