Add statistics screens (#910)
* Add statistics screens - New statistics dashboard with four tiles: overview, recently sung, best list per difficulty and players - Each tile opens a detail screen with longer lists, filters (left/right) and scrolling - Detail lists show ranks for places 1-3, avatars, bars and alternating row backgrounds - Players can be ranked by most songs or by best average - Overview details: most sung songs, genres, languages and songs never sung - Navigation help at the bottom of both screens - New Statistics button in the main menu - English and German texts * Address code analysis findings in statistics screens * Retrigger CI --------- Co-authored-by: Florian Ostertag <flokuep@users.noreply.github.com>
This commit is contained in:
1 parent
d03224f29b
commit
602417e773
17 files changed
+3853
-12
No files matched your search
@@ -111,6 +111,45 @@
|
||||
<string name="TR_SCREENMAIN_BUTTON_OPTIONS">Optionen</string>
|
||||
<string name="TR_SCREENMAIN_BUTTON_PROFILES">Profile</string>
|
||||
<string name="TR_SCREENMAIN_TEXT_NOPROFILES">Keine Profile gefunden!</string>
|
||||
<string name="TR_SCREENMAIN_BUTTON_STATISTICS">Statistik</string>
|
||||
<string name="TR_SCREENSTATISTICS_TITLE">Statistik</string>
|
||||
<string name="TR_SCREENSTATISTICS_HINT">Navigieren = Kachel wählen • Enter = Details öffnen • ESC = Zurück zum Hauptmenü</string>
|
||||
<string name="TR_SCREENSTATISTICS_DETAILHINT">Links/Rechts = Ansicht wechseln • Hoch/Runter = Blättern • ESC = Zurück zur Statistik</string>
|
||||
<string name="TR_SCREENSTATISTICS_OVERVIEW">Übersicht</string>
|
||||
<string name="TR_SCREENSTATISTICS_RECENT">Zuletzt gesungen</string>
|
||||
<string name="TR_SCREENSTATISTICS_BEST">Bestenliste</string>
|
||||
<string name="TR_SCREENSTATISTICS_PLAYERS">Spieler</string>
|
||||
<string name="TR_SCREENSTATISTICS_SONGS">Songs</string>
|
||||
<string name="TR_SCREENSTATISTICS_SUNG">gesungen</string>
|
||||
<string name="TR_SCREENSTATISTICS_PLAYERCOUNT">Spieler</string>
|
||||
<string name="TR_SCREENSTATISTICS_COVERAGE">Sammlung gesungen: {0} %</string>
|
||||
<string name="TR_SCREENSTATISTICS_MOSTSUNG">Meistgesungen</string>
|
||||
<string name="TR_SCREENSTATISTICS_GENRES">Genres</string>
|
||||
<string name="TR_SCREENSTATISTICS_LANGUAGES">Sprachen</string>
|
||||
<string name="TR_SCREENSTATISTICS_NEVERSUNG">Noch nie gesungen</string>
|
||||
<string name="TR_SCREENSTATISTICS_ALLPLAYERS">Alle Spieler</string>
|
||||
<string name="TR_SCREENSTATISTICS_PLAYERSONGS">{0} · {1} Songs</string>
|
||||
<string name="TR_SCREENSTATISTICS_AVERAGE">Ø {0}</string>
|
||||
<string name="TR_SCREENSTATISTICS_PLAYERDETAIL">{0} Songs · Bestwert {1}</string>
|
||||
<string name="TR_SCREENSTATISTICS_MOSTSONGS">Meiste Songs</string>
|
||||
<string name="TR_SCREENSTATISTICS_BESTAVERAGE">Bester Durchschnitt</string>
|
||||
<string name="TR_SCREENSTATISTICS_PLAYERAVERAGEDETAIL">Ø {0} · Bestwert {1}</string>
|
||||
<string name="TR_SCREENSTATISTICS_SONGCOUNT">{0} Songs</string>
|
||||
<string name="TR_SCREENSTATISTICS_SUMMARY">{0} Songs · {1} gesungen ({2} %) · insgesamt {3}-mal gesungen</string>
|
||||
<string name="TR_SCREENSTATISTICS_TODAY">Heute</string>
|
||||
<string name="TR_SCREENSTATISTICS_YESTERDAY">Gestern</string>
|
||||
<string name="TR_SCREENSTATISTICS_NODATA">Noch keine Daten</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_SONG">Song</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_TIMES">Mal</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_GENRE">Genre</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_LANGUAGE">Sprache</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_SUNG">Gesungen</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_YEAR">Jahr</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_DATE">Datum</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_PLAYER">Spieler</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_SCORE">Punkte</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_SONGS">Songs</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_AVERAGE">Durchschnitt</string>
|
||||
<string name="TR_SCREENPARTY_TITLE">Party-Modus Auswahl</string>
|
||||
<string name="TR_SCREENPARTY_MODES">Party-Modus</string>
|
||||
<string name="TR_SCREENPARTY_DESCRIPTION">Beschreibung</string>
|
||||
|
||||
@@ -111,6 +111,45 @@
|
||||
<string name="TR_SCREENMAIN_BUTTON_OPTIONS">Options</string>
|
||||
<string name="TR_SCREENMAIN_BUTTON_PROFILES">Profiles</string>
|
||||
<string name="TR_SCREENMAIN_TEXT_NOPROFILES">No profiles found!</string>
|
||||
<string name="TR_SCREENMAIN_BUTTON_STATISTICS">Statistics</string>
|
||||
<string name="TR_SCREENSTATISTICS_TITLE">Statistics</string>
|
||||
<string name="TR_SCREENSTATISTICS_HINT">Navigate = Select a tile • Enter = Open details • ESC = Back to main menu</string>
|
||||
<string name="TR_SCREENSTATISTICS_DETAILHINT">Left/Right = Change view • Up/Down = Scroll • ESC = Back to statistics</string>
|
||||
<string name="TR_SCREENSTATISTICS_OVERVIEW">Overview</string>
|
||||
<string name="TR_SCREENSTATISTICS_RECENT">Recently sung</string>
|
||||
<string name="TR_SCREENSTATISTICS_BEST">Best list</string>
|
||||
<string name="TR_SCREENSTATISTICS_PLAYERS">Players</string>
|
||||
<string name="TR_SCREENSTATISTICS_SONGS">Songs</string>
|
||||
<string name="TR_SCREENSTATISTICS_SUNG">sung</string>
|
||||
<string name="TR_SCREENSTATISTICS_PLAYERCOUNT">Players</string>
|
||||
<string name="TR_SCREENSTATISTICS_COVERAGE">Collection sung: {0} %</string>
|
||||
<string name="TR_SCREENSTATISTICS_MOSTSUNG">Most sung</string>
|
||||
<string name="TR_SCREENSTATISTICS_GENRES">Genres</string>
|
||||
<string name="TR_SCREENSTATISTICS_LANGUAGES">Languages</string>
|
||||
<string name="TR_SCREENSTATISTICS_NEVERSUNG">Never sung</string>
|
||||
<string name="TR_SCREENSTATISTICS_ALLPLAYERS">All players</string>
|
||||
<string name="TR_SCREENSTATISTICS_PLAYERSONGS">{0} · {1} songs</string>
|
||||
<string name="TR_SCREENSTATISTICS_AVERAGE">Ø {0}</string>
|
||||
<string name="TR_SCREENSTATISTICS_PLAYERDETAIL">{0} songs · best {1}</string>
|
||||
<string name="TR_SCREENSTATISTICS_MOSTSONGS">Most songs</string>
|
||||
<string name="TR_SCREENSTATISTICS_BESTAVERAGE">Best average</string>
|
||||
<string name="TR_SCREENSTATISTICS_PLAYERAVERAGEDETAIL">Ø {0} · best {1}</string>
|
||||
<string name="TR_SCREENSTATISTICS_SONGCOUNT">{0} songs</string>
|
||||
<string name="TR_SCREENSTATISTICS_SUMMARY">{0} songs · {1} sung ({2} %) · sung {3} times in total</string>
|
||||
<string name="TR_SCREENSTATISTICS_TODAY">Today</string>
|
||||
<string name="TR_SCREENSTATISTICS_YESTERDAY">Yesterday</string>
|
||||
<string name="TR_SCREENSTATISTICS_NODATA">No data yet</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_SONG">Song</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_TIMES">Times</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_GENRE">Genre</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_LANGUAGE">Language</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_SUNG">Sung</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_YEAR">Year</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_DATE">Date</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_PLAYER">Player</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_SCORE">Score</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_SONGS">Songs</string>
|
||||
<string name="TR_SCREENSTATISTICS_COL_AVERAGE">Average</string>
|
||||
<string name="TR_SCREENPARTY_TITLE">Party-Mode Selection</string>
|
||||
<string name="TR_SCREENPARTY_MODES">Party-mode</string>
|
||||
<string name="TR_SCREENPARTY_DESCRIPTION">Description</string>
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
ScreenMain
|
||||
|
||||
++++++++++
|
||||
Version 4:
|
||||
(2026 Oct 16)
|
||||
|
||||
Add:
|
||||
<ButtonStatistics>
|
||||
|
||||
Change:
|
||||
Positions of all buttons to make room for the new button
|
||||
|
||||
++++++++++
|
||||
Version 3:
|
||||
(2023 Dec 31)
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
ScreenStatistics
|
||||
|
||||
++++++++++
|
||||
Version 1:
|
||||
(2026 Oct 16)
|
||||
|
||||
New screen: statistics dashboard with four tiles (overview, recently sung, best list per difficulty, players)
|
||||
Help text <TextHint> at the bottom of the screen
|
||||
@@ -0,0 +1,7 @@
|
||||
ScreenStatisticsDetail
|
||||
|
||||
++++++++++
|
||||
Version 1:
|
||||
(2026 Oct 16)
|
||||
|
||||
New screen: details for the tiles of the statistics dashboard (table with rank, avatar, progress bar per row, filter and help text)
|
||||
@@ -2,7 +2,7 @@
|
||||
<Screen xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<Informations>
|
||||
<ScreenName>ScreenMain</ScreenName>
|
||||
<ScreenVersion>2</ScreenVersion>
|
||||
<ScreenVersion>4</ScreenVersion>
|
||||
</Informations>
|
||||
<Backgrounds>
|
||||
<Background Name="Background1">
|
||||
@@ -40,9 +40,9 @@
|
||||
<Skin>TextBG</Skin>
|
||||
<Color Name="StaticWarningColor" />
|
||||
<Rect>
|
||||
<X>225</X>
|
||||
<X>135</X>
|
||||
<Y>915</Y>
|
||||
<W>532.5</W>
|
||||
<W>510</W>
|
||||
<H>45</H>
|
||||
<Z>-1.9</Z>
|
||||
</Rect>
|
||||
@@ -64,11 +64,11 @@
|
||||
<Text>Vocaluxe "Shining Heaven" v0.3 (x64) Beta (0083)</Text>
|
||||
</Text>
|
||||
<Text Name="TextWarningProfiles">
|
||||
<X>480</X>
|
||||
<X>390</X>
|
||||
<Y>918</Y>
|
||||
<Z>-2</Z>
|
||||
<H>40.5</H>
|
||||
<MaxW>532.5</MaxW>
|
||||
<MaxW>500</MaxW>
|
||||
<Color Name="TextWarningColor" />
|
||||
<SelColor Name="TextWarningColor" />
|
||||
<Align>Center</Align>
|
||||
@@ -83,7 +83,7 @@
|
||||
<Skin>Button</Skin>
|
||||
<SkinSelected>Button</SkinSelected>
|
||||
<Rect>
|
||||
<X>217.5</X>
|
||||
<X>135</X>
|
||||
<Y>900</Y>
|
||||
<W>225</W>
|
||||
<H>75</H>
|
||||
@@ -118,7 +118,7 @@
|
||||
<Skin>Button</Skin>
|
||||
<SkinSelected>Button</SkinSelected>
|
||||
<Rect>
|
||||
<X>532.5</X>
|
||||
<X>420</X>
|
||||
<Y>900</Y>
|
||||
<W>225</W>
|
||||
<H>75</H>
|
||||
@@ -149,11 +149,46 @@
|
||||
<Space>15</Space>
|
||||
</SelReflection>
|
||||
</Button>
|
||||
<Button Name="ButtonStatistics">
|
||||
<Skin>Button</Skin>
|
||||
<SkinSelected>Button</SkinSelected>
|
||||
<Rect>
|
||||
<X>705</X>
|
||||
<Y>900</Y>
|
||||
<W>225</W>
|
||||
<H>75</H>
|
||||
<Z>0</Z>
|
||||
</Rect>
|
||||
<Color Name="ButtonColor" />
|
||||
<SelColor Name="ButtonSelColor" />
|
||||
<Text Name="Text">
|
||||
<X>112.5</X>
|
||||
<Y>18</Y>
|
||||
<Z>0</Z>
|
||||
<H>40.5</H>
|
||||
<MaxW>213</MaxW>
|
||||
<Color Name="ButtonTextColor" />
|
||||
<SelColor Name="ButtonTextSelColor" />
|
||||
<Align>Center</Align>
|
||||
<ResizeAlign>Center</ResizeAlign>
|
||||
<Style>Bold</Style>
|
||||
<Font>Outline</Font>
|
||||
<Text>TR_SCREENMAIN_BUTTON_STATISTICS</Text>
|
||||
</Text>
|
||||
<Reflection>
|
||||
<Height>39</Height>
|
||||
<Space>15</Space>
|
||||
</Reflection>
|
||||
<SelReflection>
|
||||
<Height>39</Height>
|
||||
<Space>15</Space>
|
||||
</SelReflection>
|
||||
</Button>
|
||||
<Button Name="ButtonOptions">
|
||||
<Skin>Button</Skin>
|
||||
<SkinSelected>Button</SkinSelected>
|
||||
<Rect>
|
||||
<X>847.5</X>
|
||||
<X>990</X>
|
||||
<Y>900</Y>
|
||||
<W>225</W>
|
||||
<H>75</H>
|
||||
@@ -188,7 +223,7 @@
|
||||
<Skin>Button</Skin>
|
||||
<SkinSelected>Button</SkinSelected>
|
||||
<Rect>
|
||||
<X>1162.5</X>
|
||||
<X>1275</X>
|
||||
<Y>900</Y>
|
||||
<W>225</W>
|
||||
<H>75</H>
|
||||
@@ -223,7 +258,7 @@
|
||||
<Skin>Button</Skin>
|
||||
<SkinSelected>Button</SkinSelected>
|
||||
<Rect>
|
||||
<X>1477.5</X>
|
||||
<X>1560</X>
|
||||
<Y>900</Y>
|
||||
<W>225</W>
|
||||
<H>75</H>
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -78,6 +78,21 @@ namespace Vocaluxe.Base
|
||||
return _HighscoreDB.GetDataBaseSongInfos(artist, title, out numPlayed, out dateAdded, out highscoreId);
|
||||
}
|
||||
|
||||
public static List<SDBStatisticsScore> LoadRecentScores(int count)
|
||||
{
|
||||
return _HighscoreDB == null ? new List<SDBStatisticsScore>() : _HighscoreDB.LoadRecentScores(count);
|
||||
}
|
||||
|
||||
public static List<SDBStatisticsScore> LoadTopScores(EGameDifficulty difficulty, int count)
|
||||
{
|
||||
return _HighscoreDB == null ? new List<SDBStatisticsScore>() : _HighscoreDB.LoadTopScores(difficulty, count);
|
||||
}
|
||||
|
||||
public static List<SDBPlayerStatistics> LoadPlayerStatistics()
|
||||
{
|
||||
return _HighscoreDB == null ? new List<SDBPlayerStatistics>() : _HighscoreDB.LoadPlayerStatistics();
|
||||
}
|
||||
|
||||
public static List<SDBScoreEntry> LoadScore(int songId, EGameMode gameMode, EHighscoreStyle style)
|
||||
{
|
||||
return _HighscoreDB == null ? null : _HighscoreDB.LoadScore(songId, gameMode, style);
|
||||
|
||||
@@ -85,6 +85,8 @@ namespace Vocaluxe.Base
|
||||
_Screens.Add(new CScreenNames());
|
||||
_Screens.Add(new CScreenCredits());
|
||||
_Screens.Add(new CScreenParty());
|
||||
_Screens.Add(new CScreenStatistics());
|
||||
_Screens.Add(new CScreenStatisticsDetail());
|
||||
|
||||
Debug.Assert(_Screens.Count == (int)EScreen.CountEntry, "Screen list and screens enum do not match");
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using VocaluxeLib;
|
||||
using VocaluxeLib.Draw;
|
||||
using VocaluxeLib.Songs;
|
||||
|
||||
namespace Vocaluxe.Base
|
||||
{
|
||||
public struct SStatisticsCategory
|
||||
{
|
||||
public string Name;
|
||||
public int NumSongs;
|
||||
public int NumSung;
|
||||
|
||||
public int Coverage
|
||||
{
|
||||
get { return NumSongs == 0 ? 0 : (int)Math.Round(NumSung * 100.0 / NumSongs); }
|
||||
}
|
||||
}
|
||||
|
||||
static class CStatistics
|
||||
{
|
||||
public static int NumSongs
|
||||
{
|
||||
get { return CSongs.AllSongs.Count; }
|
||||
}
|
||||
|
||||
public static int NumSungSongs
|
||||
{
|
||||
get { return CSongs.AllSongs.Count(song => song.NumPlayed > 0); }
|
||||
}
|
||||
|
||||
public static int NumPlayed
|
||||
{
|
||||
get { return CSongs.AllSongs.Sum(song => song.NumPlayed); }
|
||||
}
|
||||
|
||||
public static int Coverage
|
||||
{
|
||||
get { return NumSongs == 0 ? 0 : (int)Math.Round(NumSungSongs * 100.0 / NumSongs); }
|
||||
}
|
||||
|
||||
public static int NumPlayers
|
||||
{
|
||||
get { return CProfiles.GetProfiles().Count(profile => profile.Active == EOffOn.TR_CONFIG_ON && !CProfiles.IsGuestProfile(profile.Id)); }
|
||||
}
|
||||
|
||||
public static List<CSong> GetMostSung(int count)
|
||||
{
|
||||
return CSongs.AllSongs.Where(song => song.NumPlayed > 0).OrderByDescending(song => song.NumPlayed).ThenBy(song => song.Artist).Take(count).ToList();
|
||||
}
|
||||
|
||||
public static List<CSong> GetNeverSung(int count)
|
||||
{
|
||||
return CSongs.AllSongs.Where(song => song.NumPlayed == 0).OrderBy(song => CGame.Rand.Next()).Take(count).ToList();
|
||||
}
|
||||
|
||||
public static List<SStatisticsCategory> GetGenres()
|
||||
{
|
||||
return _GetCategories(song => song.Genres);
|
||||
}
|
||||
|
||||
public static List<SStatisticsCategory> GetLanguages()
|
||||
{
|
||||
return _GetCategories(song => song.Languages);
|
||||
}
|
||||
|
||||
private static List<SStatisticsCategory> _GetCategories(Func<CSong, IEnumerable<string>> selector)
|
||||
{
|
||||
var categories = new Dictionary<string, SStatisticsCategory>(StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var song in CSongs.AllSongs)
|
||||
{
|
||||
foreach (var name in selector(song).Where(name => !string.IsNullOrWhiteSpace(name)).Distinct(StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
SStatisticsCategory category;
|
||||
if (!categories.TryGetValue(name, out category))
|
||||
{
|
||||
category = new SStatisticsCategory { Name = name.Trim() };
|
||||
}
|
||||
|
||||
category.NumSongs++;
|
||||
if (song.NumPlayed > 0)
|
||||
{
|
||||
category.NumSung++;
|
||||
}
|
||||
|
||||
categories[name] = category;
|
||||
}
|
||||
}
|
||||
|
||||
return categories.Values.OrderByDescending(category => category.NumSongs).ThenBy(category => category.Name).ToList();
|
||||
}
|
||||
|
||||
public static string FormatDate(DateTime date, bool full)
|
||||
{
|
||||
if (date.Date == DateTime.Today)
|
||||
{
|
||||
return CLanguage.Translate("TR_SCREENSTATISTICS_TODAY");
|
||||
}
|
||||
|
||||
if (date.Date == DateTime.Today.AddDays(-1))
|
||||
{
|
||||
return CLanguage.Translate("TR_SCREENSTATISTICS_YESTERDAY");
|
||||
}
|
||||
|
||||
return date.ToString(full ? "dd.MM.yyyy" : "dd.MM.");
|
||||
}
|
||||
|
||||
public static CTextureRef GetAvatar(string playerName)
|
||||
{
|
||||
var profile = CProfiles.GetProfiles().FirstOrDefault(pr => pr.PlayerName == playerName && pr.Avatar != null);
|
||||
return profile == null ? null : profile.Avatar.Texture;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,26 @@ using VocaluxeLib.Log;
|
||||
|
||||
namespace Vocaluxe.Lib.Database
|
||||
{
|
||||
public struct SDBStatisticsScore
|
||||
{
|
||||
public string Artist;
|
||||
public string Title;
|
||||
public string PlayerName;
|
||||
public int Score;
|
||||
public DateTime Date;
|
||||
public EGameDifficulty Difficulty;
|
||||
public bool Duet;
|
||||
}
|
||||
|
||||
public struct SDBPlayerStatistics
|
||||
{
|
||||
public string PlayerName;
|
||||
public int NumSongs;
|
||||
public int NumDuets;
|
||||
public double AverageScore;
|
||||
public int BestScore;
|
||||
}
|
||||
|
||||
public class CHighscoreDB : CDatabaseBase
|
||||
{
|
||||
private struct SData
|
||||
@@ -380,6 +400,111 @@ namespace Vocaluxe.Lib.Database
|
||||
return scores;
|
||||
}
|
||||
|
||||
private const string _StatisticsScoresQuery = "SELECT so.Artist, so.Title, sc.PlayerName, sc.Score, sc.Date, sc.Difficulty, sc.Duet " +
|
||||
"FROM Scores sc INNER JOIN Songs so ON sc.SongId = so.id ";
|
||||
private const string _RecentScoresQuery = _StatisticsScoresQuery + "ORDER BY sc.Date DESC, sc.id DESC LIMIT @Count";
|
||||
private const string _TopScoresQuery = _StatisticsScoresQuery + "WHERE sc.Difficulty = @Difficulty ORDER BY sc.Score DESC, sc.Date ASC LIMIT @Count";
|
||||
|
||||
public List<SDBStatisticsScore> LoadRecentScores(int count)
|
||||
{
|
||||
return _LoadStatisticsScores(count, null);
|
||||
}
|
||||
|
||||
public List<SDBStatisticsScore> LoadTopScores(EGameDifficulty difficulty, int count)
|
||||
{
|
||||
return _LoadStatisticsScores(count, difficulty);
|
||||
}
|
||||
|
||||
private List<SDBStatisticsScore> _LoadStatisticsScores(int count, EGameDifficulty? difficulty)
|
||||
{
|
||||
var scores = new List<SDBStatisticsScore>();
|
||||
using (var connection = new SqliteConnection())
|
||||
{
|
||||
connection.ConnectionString = "Data Source=" + _FilePath;
|
||||
|
||||
try
|
||||
{
|
||||
connection.Open();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return scores;
|
||||
}
|
||||
|
||||
using (var command = new SqliteCommand())
|
||||
{
|
||||
command.Connection = connection;
|
||||
command.CommandText = difficulty.HasValue ? _TopScoresQuery : _RecentScoresQuery;
|
||||
command.Parameters.AddWithValue("@Count", count);
|
||||
if (difficulty.HasValue)
|
||||
{
|
||||
command.Parameters.AddWithValue("@Difficulty", (int)difficulty.Value);
|
||||
}
|
||||
|
||||
using (var reader = command.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
scores.Add(new SDBStatisticsScore
|
||||
{
|
||||
Artist = reader.GetString(0),
|
||||
Title = reader.GetString(1),
|
||||
PlayerName = reader.GetString(2),
|
||||
Score = reader.GetInt32(3),
|
||||
Date = new DateTime(reader.GetInt64(4), DateTimeKind.Local),
|
||||
Difficulty = (EGameDifficulty)reader.GetInt32(5),
|
||||
Duet = reader.GetInt32(6) != 0
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scores;
|
||||
}
|
||||
|
||||
public List<SDBPlayerStatistics> LoadPlayerStatistics()
|
||||
{
|
||||
var players = new List<SDBPlayerStatistics>();
|
||||
using (var connection = new SqliteConnection())
|
||||
{
|
||||
connection.ConnectionString = "Data Source=" + _FilePath;
|
||||
|
||||
try
|
||||
{
|
||||
connection.Open();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return players;
|
||||
}
|
||||
|
||||
using (var command = new SqliteCommand())
|
||||
{
|
||||
command.Connection = connection;
|
||||
command.CommandText = "SELECT PlayerName, COUNT(*), SUM(Duet), AVG(Score), MAX(Score) FROM Scores " +
|
||||
"GROUP BY PlayerName ORDER BY COUNT(*) DESC, AVG(Score) DESC";
|
||||
|
||||
using (var reader = command.ExecuteReader())
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
players.Add(new SDBPlayerStatistics
|
||||
{
|
||||
PlayerName = reader.GetString(0),
|
||||
NumSongs = reader.GetInt32(1),
|
||||
NumDuets = reader.GetInt32(2),
|
||||
AverageScore = reader.GetDouble(3),
|
||||
BestScore = reader.GetInt32(4)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return players;
|
||||
}
|
||||
|
||||
private void _IncreaseSongCounter(int dataBaseSongId, SqliteCommand command)
|
||||
{
|
||||
command.CommandText = "UPDATE Songs SET NumPlayed = NumPlayed + 1 WHERE [id] = @id";
|
||||
|
||||
@@ -27,11 +27,12 @@ namespace Vocaluxe.Screens
|
||||
// Version number for theme files. Increment it, if you've changed something on the theme files!
|
||||
protected override int _ScreenVersion
|
||||
{
|
||||
get { return 2; }
|
||||
get { return 4; }
|
||||
}
|
||||
|
||||
private const string _ButtonSing = "ButtonSing";
|
||||
private const string _ButtonParty = "ButtonParty";
|
||||
private const string _ButtonStatistics = "ButtonStatistics";
|
||||
private const string _ButtonOptions = "ButtonOptions";
|
||||
private const string _ButtonProfiles = "ButtonProfiles";
|
||||
private const string _ButtonExit = "ButtonExit";
|
||||
@@ -56,7 +57,7 @@ namespace Vocaluxe.Screens
|
||||
base.Init();
|
||||
|
||||
_ThemeStatics = new string[] { "StaticMenuBar", _StaticWarningProfiles };
|
||||
_ThemeButtons = new string[] { _ButtonSing, _ButtonParty, _ButtonOptions, _ButtonProfiles, _ButtonExit };
|
||||
_ThemeButtons = new string[] { _ButtonSing, _ButtonParty, _ButtonStatistics, _ButtonOptions, _ButtonProfiles, _ButtonExit };
|
||||
_ThemeTexts = new string[] { _TextRelease, _TextWarningProfiles };
|
||||
}
|
||||
|
||||
@@ -118,6 +119,12 @@ namespace Vocaluxe.Screens
|
||||
_LeaveScreen();
|
||||
}
|
||||
|
||||
if (_Buttons[_ButtonStatistics].Selected)
|
||||
{
|
||||
CGraphics.FadeTo(EScreen.Statistics);
|
||||
_LeaveScreen();
|
||||
}
|
||||
|
||||
if (_Buttons[_ButtonOptions].Selected)
|
||||
{
|
||||
CGraphics.FadeTo(EScreen.Options);
|
||||
@@ -165,6 +172,12 @@ namespace Vocaluxe.Screens
|
||||
_LeaveScreen();
|
||||
}
|
||||
|
||||
if (_Buttons[_ButtonStatistics].Selected)
|
||||
{
|
||||
CGraphics.FadeTo(EScreen.Statistics);
|
||||
_LeaveScreen();
|
||||
}
|
||||
|
||||
if (_Buttons[_ButtonOptions].Selected)
|
||||
{
|
||||
CGraphics.FadeTo(EScreen.Options);
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using Vocaluxe.Base;
|
||||
using VocaluxeLib;
|
||||
using VocaluxeLib.Menu;
|
||||
|
||||
namespace Vocaluxe.Screens
|
||||
{
|
||||
public class CScreenStatistics : CMenu
|
||||
{
|
||||
protected override int _ScreenVersion
|
||||
{
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
private const int _NumTiles = 4;
|
||||
private const int _NumMostSung = 2;
|
||||
private const int _NumRecent = 5;
|
||||
private const int _NumDifficulties = 3;
|
||||
private const int _NumBest = 3;
|
||||
private const int _NumPlayers = 4;
|
||||
|
||||
private const string _StaticTileSelection = "StaticTileSelection";
|
||||
private const string _TextSongsValue = "TextSongsValue";
|
||||
private const string _TextSungValue = "TextSungValue";
|
||||
private const string _TextPlayersValue = "TextPlayersValue";
|
||||
private const string _TextCoverage = "TextCoverage";
|
||||
private const string _ProgressBarCoverage = "ProgressBarCoverage";
|
||||
|
||||
private string[] _StaticTile;
|
||||
private string[] _TextMostSung;
|
||||
private string[] _TextRecentDate;
|
||||
private string[] _TextRecentSong;
|
||||
private string[] _TextRecentScore;
|
||||
private string[,] _TextBestName;
|
||||
private string[,] _TextBestScore;
|
||||
private string[,] _StaticBestMedal;
|
||||
private string[] _StaticAvatar;
|
||||
private string[] _TextPlayerName;
|
||||
private string[] _TextPlayerAverage;
|
||||
|
||||
private int _SelectedTile;
|
||||
|
||||
public override EMusicType CurrentMusicType
|
||||
{
|
||||
get { return EMusicType.Background; }
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
|
||||
_StaticTile = Enumerable.Range(1, _NumTiles).Select(i => "StaticTile" + i).ToArray();
|
||||
_TextMostSung = Enumerable.Range(1, _NumMostSung).Select(i => "TextMostSung" + i).ToArray();
|
||||
_TextRecentDate = Enumerable.Range(1, _NumRecent).Select(i => "TextRecentDate" + i).ToArray();
|
||||
_TextRecentSong = Enumerable.Range(1, _NumRecent).Select(i => "TextRecentSong" + i).ToArray();
|
||||
_TextRecentScore = Enumerable.Range(1, _NumRecent).Select(i => "TextRecentScore" + i).ToArray();
|
||||
_StaticAvatar = Enumerable.Range(1, _NumPlayers).Select(i => "StaticAvatar" + i).ToArray();
|
||||
_TextPlayerName = Enumerable.Range(1, _NumPlayers).Select(i => "TextPlayerName" + i).ToArray();
|
||||
_TextPlayerAverage = Enumerable.Range(1, _NumPlayers).Select(i => "TextPlayerAverage" + i).ToArray();
|
||||
|
||||
_TextBestName = new string[_NumDifficulties, _NumBest];
|
||||
_TextBestScore = new string[_NumDifficulties, _NumBest];
|
||||
_StaticBestMedal = new string[_NumDifficulties, _NumBest];
|
||||
for (var d = 0; d < _NumDifficulties; d++)
|
||||
{
|
||||
for (var i = 0; i < _NumBest; i++)
|
||||
{
|
||||
_TextBestName[d, i] = "TextBestName" + (d + 1) + (i + 1);
|
||||
_TextBestScore[d, i] = "TextBestScore" + (d + 1) + (i + 1);
|
||||
_StaticBestMedal[d, i] = "StaticBestMedal" + (d + 1) + (i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
_ThemeStatics = _StaticTile.Concat(_StaticAvatar).Concat(_StaticBestMedal.Cast<string>()).Concat(new[] { _StaticTileSelection }).ToArray();
|
||||
_ThemeTexts = new[] { _TextSongsValue, _TextSungValue, _TextPlayersValue, _TextCoverage }
|
||||
.Concat(_TextMostSung).Concat(_TextRecentDate).Concat(_TextRecentSong).Concat(_TextRecentScore)
|
||||
.Concat(_TextBestName.Cast<string>()).Concat(_TextBestScore.Cast<string>())
|
||||
.Concat(_TextPlayerName).Concat(_TextPlayerAverage).ToArray();
|
||||
_ThemeProgressBars = new[] { _ProgressBarCoverage };
|
||||
}
|
||||
|
||||
public override void LoadTheme(string xmlPath)
|
||||
{
|
||||
base.LoadTheme(xmlPath);
|
||||
|
||||
foreach (var avatar in _StaticAvatar)
|
||||
{
|
||||
_Statics[avatar].Aspect = EAspect.Crop;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnShow()
|
||||
{
|
||||
base.OnShow();
|
||||
|
||||
_UpdateOverview();
|
||||
_UpdateRecent();
|
||||
_UpdateBest();
|
||||
_UpdatePlayers();
|
||||
_SelectTile(_SelectedTile);
|
||||
}
|
||||
|
||||
public override bool HandleInput(SKeyEvent keyEvent)
|
||||
{
|
||||
base.HandleInput(keyEvent);
|
||||
|
||||
if (keyEvent.KeyPressed)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (keyEvent.Key)
|
||||
{
|
||||
case Keys.Left:
|
||||
case Keys.Right:
|
||||
_SelectTile(_SelectedTile ^ 1);
|
||||
break;
|
||||
|
||||
case Keys.Up:
|
||||
case Keys.Down:
|
||||
_SelectTile(_SelectedTile ^ 2);
|
||||
break;
|
||||
|
||||
case Keys.Enter:
|
||||
_OpenDetail();
|
||||
break;
|
||||
|
||||
case Keys.Escape:
|
||||
case Keys.Back:
|
||||
CGraphics.FadeTo(EScreen.Main);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool HandleMouse(SMouseEvent mouseEvent)
|
||||
{
|
||||
base.HandleMouse(mouseEvent);
|
||||
|
||||
for (var i = 0; i < _NumTiles; i++)
|
||||
{
|
||||
if (CHelper.IsInBounds(_Statics[_StaticTile[i]].Rect, mouseEvent))
|
||||
{
|
||||
_SelectTile(i);
|
||||
if (mouseEvent.LB)
|
||||
{
|
||||
_OpenDetail();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mouseEvent.RB)
|
||||
{
|
||||
CGraphics.FadeTo(EScreen.Main);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool UpdateGame()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private void _SelectTile(int tile)
|
||||
{
|
||||
_SelectedTile = tile;
|
||||
var selection = _Statics[_StaticTileSelection];
|
||||
var rect = _Statics[_StaticTile[tile]].Rect;
|
||||
selection.X = rect.X;
|
||||
selection.Y = rect.Y;
|
||||
selection.W = rect.W;
|
||||
selection.H = rect.H;
|
||||
}
|
||||
|
||||
private void _OpenDetail()
|
||||
{
|
||||
CScreenStatisticsDetail.Page = (EStatisticsPage)_SelectedTile;
|
||||
CGraphics.FadeTo(EScreen.StatisticsDetail);
|
||||
}
|
||||
|
||||
private void _UpdateOverview()
|
||||
{
|
||||
_Texts[_TextSongsValue].Text = CStatistics.NumSongs.ToString("N0");
|
||||
_Texts[_TextSungValue].Text = CStatistics.NumSungSongs.ToString("N0");
|
||||
_Texts[_TextPlayersValue].Text = CStatistics.NumPlayers.ToString("N0");
|
||||
_Texts[_TextCoverage].Text = string.Format(CLanguage.Translate("TR_SCREENSTATISTICS_COVERAGE"), CStatistics.Coverage);
|
||||
_ProgressBars[_ProgressBarCoverage].Progress = CStatistics.Coverage / 100f;
|
||||
|
||||
var mostSung = CStatistics.GetMostSung(_NumMostSung);
|
||||
for (var i = 0; i < _NumMostSung; i++)
|
||||
{
|
||||
_Texts[_TextMostSung[i]].Visible = i < mostSung.Count;
|
||||
if (i < mostSung.Count)
|
||||
{
|
||||
_Texts[_TextMostSung[i]].Text = mostSung[i].NumPlayed + "× " + mostSung[i].Artist + " - " + mostSung[i].Title;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _UpdateRecent()
|
||||
{
|
||||
var scores = CDataBase.LoadRecentScores(_NumRecent);
|
||||
for (var i = 0; i < _NumRecent; i++)
|
||||
{
|
||||
var isEntry = i < scores.Count;
|
||||
_Texts[_TextRecentDate[i]].Visible = isEntry;
|
||||
_Texts[_TextRecentScore[i]].Visible = isEntry;
|
||||
_Texts[_TextRecentSong[i]].Visible = isEntry || i == 0;
|
||||
|
||||
if (!isEntry)
|
||||
{
|
||||
_Texts[_TextRecentSong[i]].Text = "TR_SCREENSTATISTICS_NODATA";
|
||||
continue;
|
||||
}
|
||||
|
||||
_Texts[_TextRecentDate[i]].Text = CStatistics.FormatDate(scores[i].Date, false);
|
||||
_Texts[_TextRecentSong[i]].Text = scores[i].Artist + " - " + scores[i].Title + " · " + scores[i].PlayerName;
|
||||
_Texts[_TextRecentScore[i]].Text = scores[i].Score.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
private void _UpdateBest()
|
||||
{
|
||||
for (var d = 0; d < _NumDifficulties; d++)
|
||||
{
|
||||
var scores = CDataBase.LoadTopScores((EGameDifficulty)d, _NumBest);
|
||||
for (var i = 0; i < _NumBest; i++)
|
||||
{
|
||||
var isEntry = i < scores.Count;
|
||||
_Texts[_TextBestName[d, i]].Visible = isEntry;
|
||||
_Texts[_TextBestScore[d, i]].Visible = isEntry;
|
||||
_Statics[_StaticBestMedal[d, i]].Visible = isEntry;
|
||||
if (isEntry)
|
||||
{
|
||||
_Texts[_TextBestName[d, i]].Text = scores[i].PlayerName;
|
||||
_Texts[_TextBestScore[d, i]].Text = scores[i].Score.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _UpdatePlayers()
|
||||
{
|
||||
var players = CDataBase.LoadPlayerStatistics();
|
||||
for (var i = 0; i < _NumPlayers; i++)
|
||||
{
|
||||
var isEntry = i < players.Count;
|
||||
_Texts[_TextPlayerName[i]].Visible = isEntry;
|
||||
_Texts[_TextPlayerAverage[i]].Visible = isEntry;
|
||||
_Statics[_StaticAvatar[i]].Visible = false;
|
||||
if (!isEntry)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_Texts[_TextPlayerName[i]].Text = string.Format(CLanguage.Translate("TR_SCREENSTATISTICS_PLAYERSONGS"), players[i].PlayerName, players[i].NumSongs);
|
||||
_Texts[_TextPlayerAverage[i]].Text = string.Format(CLanguage.Translate("TR_SCREENSTATISTICS_AVERAGE"), Math.Round(players[i].AverageScore).ToString("N0"));
|
||||
|
||||
var avatar = CStatistics.GetAvatar(players[i].PlayerName);
|
||||
_Statics[_StaticAvatar[i]].Texture = avatar;
|
||||
_Statics[_StaticAvatar[i]].Visible = avatar != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,460 @@
|
||||
#region license
|
||||
// This file is part of Vocaluxe.
|
||||
//
|
||||
// Vocaluxe is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Vocaluxe is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Vocaluxe. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using Vocaluxe.Base;
|
||||
using VocaluxeLib;
|
||||
using VocaluxeLib.Menu;
|
||||
using VocaluxeLib.Songs;
|
||||
|
||||
namespace Vocaluxe.Screens
|
||||
{
|
||||
public enum EStatisticsPage
|
||||
{
|
||||
Overview,
|
||||
Recent,
|
||||
Best,
|
||||
Players
|
||||
}
|
||||
|
||||
public class CScreenStatisticsDetail : CMenu
|
||||
{
|
||||
protected override int _ScreenVersion
|
||||
{
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
private sealed class CRow
|
||||
{
|
||||
public string[] Cells;
|
||||
public int Rank;
|
||||
public float Bar = -1f;
|
||||
public string Player;
|
||||
public bool AvatarBeforeMain;
|
||||
}
|
||||
|
||||
private const int _NumRows = 12;
|
||||
private const int _NumColumns = 4;
|
||||
private const int _NumListEntries = 100;
|
||||
private const int _NumRecentEntries = 1000;
|
||||
|
||||
private const string _TextTitle = "TextTitle";
|
||||
private const string _TextFilter = "TextFilter";
|
||||
private const string _TextSummary = "TextSummary";
|
||||
|
||||
private static readonly string[] _OverviewFilters =
|
||||
{ "TR_SCREENSTATISTICS_MOSTSUNG", "TR_SCREENSTATISTICS_GENRES", "TR_SCREENSTATISTICS_LANGUAGES", "TR_SCREENSTATISTICS_NEVERSUNG" };
|
||||
private static readonly string[] _PlayerFilters = { "TR_SCREENSTATISTICS_MOSTSONGS", "TR_SCREENSTATISTICS_BESTAVERAGE" };
|
||||
private static readonly string[] _RankColors = { "Top1Color", "Top2Color", "Top3Color" };
|
||||
|
||||
public static EStatisticsPage Page { get; set; } = EStatisticsPage.Overview;
|
||||
|
||||
private string[] _TextHeader;
|
||||
private string[,] _TextCell;
|
||||
private string[] _StaticRank;
|
||||
private string[] _StaticAvatar;
|
||||
private string[] _ProgressBarRow;
|
||||
private float _AvatarMainX;
|
||||
private float _AvatarPlayerX;
|
||||
private float _MainColumnX;
|
||||
private float _MainColumnW;
|
||||
private float _ListLeft;
|
||||
|
||||
private readonly List<CRow> _Rows = new List<CRow>();
|
||||
private string[] _Headers = new string[_NumColumns];
|
||||
private List<string> _PlayerNames = new List<string>();
|
||||
private List<CSong> _NeverSung = new List<CSong>();
|
||||
private int _NumFilters = 1;
|
||||
private int _Filter;
|
||||
private int _Offset;
|
||||
|
||||
public override EMusicType CurrentMusicType
|
||||
{
|
||||
get { return EMusicType.Background; }
|
||||
}
|
||||
|
||||
public override void Init()
|
||||
{
|
||||
base.Init();
|
||||
|
||||
_TextHeader = Enumerable.Range(1, _NumColumns).Select(c => "TextHeader" + c).ToArray();
|
||||
_StaticRank = Enumerable.Range(1, _NumRows).Select(r => "StaticRank" + r).ToArray();
|
||||
_StaticAvatar = Enumerable.Range(1, _NumRows).Select(r => "StaticAvatar" + r).ToArray();
|
||||
_ProgressBarRow = Enumerable.Range(1, _NumRows).Select(r => "ProgressBarRow" + r).ToArray();
|
||||
_TextCell = new string[_NumRows, _NumColumns];
|
||||
for (var r = 0; r < _NumRows; r++)
|
||||
{
|
||||
for (var c = 0; c < _NumColumns; c++)
|
||||
{
|
||||
_TextCell[r, c] = "TextRow" + (r + 1) + "Column" + (c + 1);
|
||||
}
|
||||
}
|
||||
|
||||
_ThemeTexts = new[] { _TextTitle, _TextFilter, _TextSummary }.Concat(_TextHeader).Concat(_TextCell.Cast<string>()).ToArray();
|
||||
_ThemeStatics = _StaticRank.Concat(_StaticAvatar).ToArray();
|
||||
_ThemeProgressBars = _ProgressBarRow;
|
||||
}
|
||||
|
||||
public override void LoadTheme(string xmlPath)
|
||||
{
|
||||
base.LoadTheme(xmlPath);
|
||||
|
||||
foreach (var avatar in _StaticAvatar)
|
||||
{
|
||||
_Statics[avatar].Aspect = EAspect.Crop;
|
||||
}
|
||||
|
||||
_AvatarMainX = _Statics[_StaticAvatar[0]].X;
|
||||
_AvatarPlayerX = _Texts[_TextCell[0, 2]].X - (_Texts[_TextCell[0, 1]].X - _AvatarMainX);
|
||||
_MainColumnX = _Texts[_TextCell[0, 1]].X;
|
||||
_MainColumnW = _Texts[_TextCell[0, 1]].W;
|
||||
_ListLeft = _Statics[_StaticRank[0]].X;
|
||||
}
|
||||
|
||||
public override void OnShow()
|
||||
{
|
||||
base.OnShow();
|
||||
|
||||
_Filter = 0;
|
||||
_Offset = 0;
|
||||
_PlayerNames = CDataBase.LoadPlayerStatistics().Select(player => player.PlayerName).ToList();
|
||||
_NeverSung = CStatistics.GetNeverSung(_NumListEntries);
|
||||
_Update();
|
||||
}
|
||||
|
||||
public override bool HandleInput(SKeyEvent keyEvent)
|
||||
{
|
||||
base.HandleInput(keyEvent);
|
||||
|
||||
if (keyEvent.KeyPressed)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (keyEvent.Key)
|
||||
{
|
||||
case Keys.Left:
|
||||
_ChangeFilter(-1);
|
||||
break;
|
||||
|
||||
case Keys.Right:
|
||||
_ChangeFilter(1);
|
||||
break;
|
||||
|
||||
case Keys.Up:
|
||||
_Scroll(-1);
|
||||
break;
|
||||
|
||||
case Keys.Down:
|
||||
_Scroll(1);
|
||||
break;
|
||||
|
||||
case Keys.PageUp:
|
||||
_Scroll(-_NumRows);
|
||||
break;
|
||||
|
||||
case Keys.PageDown:
|
||||
_Scroll(_NumRows);
|
||||
break;
|
||||
|
||||
case Keys.Escape:
|
||||
case Keys.Back:
|
||||
case Keys.Enter:
|
||||
CGraphics.FadeTo(EScreen.Statistics);
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool HandleMouse(SMouseEvent mouseEvent)
|
||||
{
|
||||
base.HandleMouse(mouseEvent);
|
||||
|
||||
if (mouseEvent.Wheel != 0)
|
||||
{
|
||||
_Scroll(mouseEvent.Wheel);
|
||||
}
|
||||
|
||||
if (mouseEvent.LB && CHelper.IsInBounds(_Texts[_TextFilter].Rect, mouseEvent))
|
||||
{
|
||||
_ChangeFilter(1);
|
||||
}
|
||||
|
||||
if (mouseEvent.RB)
|
||||
{
|
||||
CGraphics.FadeTo(EScreen.Statistics);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool UpdateGame()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
private void _ChangeFilter(int step)
|
||||
{
|
||||
if (_NumFilters <= 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_Filter = (_Filter + step + _NumFilters) % _NumFilters;
|
||||
_Offset = 0;
|
||||
_Update();
|
||||
}
|
||||
|
||||
private void _Scroll(int step)
|
||||
{
|
||||
_Offset = Math.Max(0, Math.Min(Math.Max(0, _Rows.Count - _NumRows), _Offset + step));
|
||||
_UpdateRows();
|
||||
}
|
||||
|
||||
private void _Update()
|
||||
{
|
||||
_Rows.Clear();
|
||||
_Texts[_TextSummary].Text = string.Empty;
|
||||
string filterText;
|
||||
|
||||
switch (Page)
|
||||
{
|
||||
case EStatisticsPage.Overview:
|
||||
_Texts[_TextTitle].Text = "TR_SCREENSTATISTICS_OVERVIEW";
|
||||
_NumFilters = _OverviewFilters.Length;
|
||||
filterText = _OverviewFilters[_Filter];
|
||||
_Texts[_TextSummary].Text = string.Format(CLanguage.Translate("TR_SCREENSTATISTICS_SUMMARY"), CStatistics.NumSongs.ToString("N0"),
|
||||
CStatistics.NumSungSongs.ToString("N0"), CStatistics.Coverage, CStatistics.NumPlayed.ToString("N0"));
|
||||
_FillOverview();
|
||||
break;
|
||||
|
||||
case EStatisticsPage.Recent:
|
||||
_Texts[_TextTitle].Text = "TR_SCREENSTATISTICS_RECENT";
|
||||
_NumFilters = _PlayerNames.Count + 1;
|
||||
filterText = _Filter == 0 ? "TR_SCREENSTATISTICS_ALLPLAYERS" : _PlayerNames[_Filter - 1];
|
||||
_FillRecent();
|
||||
break;
|
||||
|
||||
case EStatisticsPage.Best:
|
||||
_Texts[_TextTitle].Text = "TR_SCREENSTATISTICS_BEST";
|
||||
_NumFilters = 3;
|
||||
filterText = Enum.GetName(typeof(EGameDifficulty), (EGameDifficulty)_Filter);
|
||||
_FillBest();
|
||||
break;
|
||||
|
||||
default:
|
||||
_Texts[_TextTitle].Text = "TR_SCREENSTATISTICS_PLAYERS";
|
||||
_NumFilters = _PlayerFilters.Length;
|
||||
filterText = _PlayerFilters[_Filter];
|
||||
_FillPlayers();
|
||||
break;
|
||||
}
|
||||
|
||||
_Texts[_TextFilter].Text = _NumFilters > 1 ? "< " + CLanguage.Translate(filterText) + " >" : string.Empty;
|
||||
_UpdateRows();
|
||||
}
|
||||
|
||||
private void _FillOverview()
|
||||
{
|
||||
switch (_Filter)
|
||||
{
|
||||
case 0:
|
||||
_Headers = new[] { "#", "TR_SCREENSTATISTICS_COL_SONG", "TR_SCREENSTATISTICS_COL_GENRE", "TR_SCREENSTATISTICS_COL_TIMES" };
|
||||
var mostSung = CStatistics.GetMostSung(_NumListEntries);
|
||||
var maxPlayed = mostSung.Count > 0 ? mostSung[0].NumPlayed : 1;
|
||||
for (var i = 0; i < mostSung.Count; i++)
|
||||
{
|
||||
var song = mostSung[i];
|
||||
_Rows.Add(new CRow
|
||||
{
|
||||
Cells = new[] { (i + 1).ToString(), song.Artist + " - " + song.Title, song.Genres.FirstOrDefault() ?? string.Empty, song.NumPlayed + "×" },
|
||||
Rank = i + 1,
|
||||
Bar = song.NumPlayed / (float)maxPlayed
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 2:
|
||||
_Headers = new[] { string.Empty, _Filter == 1 ? "TR_SCREENSTATISTICS_COL_GENRE" : "TR_SCREENSTATISTICS_COL_LANGUAGE", "TR_SCREENSTATISTICS_COL_SUNG", "%" };
|
||||
foreach (var category in _Filter == 1 ? CStatistics.GetGenres() : CStatistics.GetLanguages())
|
||||
{
|
||||
_Rows.Add(new CRow
|
||||
{
|
||||
Cells = new[] { string.Empty, category.Name, category.NumSung.ToString("N0") + " / " + category.NumSongs.ToString("N0"), category.Coverage + " %" },
|
||||
Bar = category.Coverage / 100f
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
_Headers = new[] { string.Empty, "TR_SCREENSTATISTICS_COL_SONG", "TR_SCREENSTATISTICS_COL_GENRE", "TR_SCREENSTATISTICS_COL_YEAR" };
|
||||
foreach (var song in _NeverSung)
|
||||
{
|
||||
_Rows.Add(new CRow { Cells = new[] { string.Empty, song.Artist + " - " + song.Title, string.Join(", ", song.Genres), song.Year } });
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void _FillRecent()
|
||||
{
|
||||
_Headers = new[] { "TR_SCREENSTATISTICS_COL_DATE", "TR_SCREENSTATISTICS_COL_SONG", "TR_SCREENSTATISTICS_COL_PLAYER", "TR_SCREENSTATISTICS_COL_SCORE" };
|
||||
var scores = CDataBase.LoadRecentScores(_NumRecentEntries).Where(score => _Filter == 0 || score.PlayerName == _PlayerNames[_Filter - 1]).Take(_NumListEntries);
|
||||
foreach (var score in scores)
|
||||
{
|
||||
_Rows.Add(new CRow
|
||||
{
|
||||
Cells = new[] { CStatistics.FormatDate(score.Date, true), score.Artist + " - " + score.Title, score.PlayerName, score.Score.ToString() },
|
||||
Bar = score.Score / (float)CSettings.MaxScore,
|
||||
Player = score.PlayerName
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void _FillBest()
|
||||
{
|
||||
_Headers = new[] { "#", "TR_SCREENSTATISTICS_COL_SONG", "TR_SCREENSTATISTICS_COL_PLAYER", "TR_SCREENSTATISTICS_COL_SCORE" };
|
||||
var rank = 1;
|
||||
foreach (var score in CDataBase.LoadTopScores((EGameDifficulty)_Filter, _NumListEntries))
|
||||
{
|
||||
_Rows.Add(new CRow
|
||||
{
|
||||
Cells = new[] { rank.ToString(), score.Artist + " - " + score.Title, score.PlayerName, score.Score.ToString() },
|
||||
Rank = rank++,
|
||||
Bar = score.Score / (float)CSettings.MaxScore,
|
||||
Player = score.PlayerName
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void _FillPlayers()
|
||||
{
|
||||
var players = CDataBase.LoadPlayerStatistics();
|
||||
var rank = 1;
|
||||
if (_Filter == 0)
|
||||
{
|
||||
_Headers = new[] { "#", "TR_SCREENSTATISTICS_COL_PLAYER", "TR_SCREENSTATISTICS_COL_AVERAGE", "TR_SCREENSTATISTICS_COL_SONGS" };
|
||||
var maxSongs = players.Count > 0 ? players.Max(player => player.NumSongs) : 1;
|
||||
foreach (var player in players.OrderByDescending(player => player.NumSongs).ThenByDescending(player => player.AverageScore))
|
||||
{
|
||||
_Rows.Add(new CRow
|
||||
{
|
||||
Cells = new[]
|
||||
{
|
||||
rank.ToString(), player.PlayerName,
|
||||
string.Format(CLanguage.Translate("TR_SCREENSTATISTICS_PLAYERAVERAGEDETAIL"), Math.Round(player.AverageScore).ToString("N0"), player.BestScore.ToString()),
|
||||
string.Format(CLanguage.Translate("TR_SCREENSTATISTICS_SONGCOUNT"), player.NumSongs.ToString("N0"))
|
||||
},
|
||||
Rank = rank++,
|
||||
Bar = player.NumSongs / (float)maxSongs,
|
||||
Player = player.PlayerName,
|
||||
AvatarBeforeMain = true
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_Headers = new[] { "#", "TR_SCREENSTATISTICS_COL_PLAYER", "TR_SCREENSTATISTICS_COL_SONGS", "TR_SCREENSTATISTICS_COL_AVERAGE" };
|
||||
foreach (var player in players.OrderByDescending(player => player.AverageScore).ThenByDescending(player => player.NumSongs))
|
||||
{
|
||||
_Rows.Add(new CRow
|
||||
{
|
||||
Cells = new[]
|
||||
{
|
||||
rank.ToString(), player.PlayerName,
|
||||
string.Format(CLanguage.Translate("TR_SCREENSTATISTICS_PLAYERDETAIL"), player.NumSongs.ToString("N0"), player.BestScore.ToString()),
|
||||
string.Format(CLanguage.Translate("TR_SCREENSTATISTICS_AVERAGE"), Math.Round(player.AverageScore).ToString("N0"))
|
||||
},
|
||||
Rank = rank++,
|
||||
Bar = (float)player.AverageScore / CSettings.MaxScore,
|
||||
Player = player.PlayerName,
|
||||
AvatarBeforeMain = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void _UpdateRows()
|
||||
{
|
||||
for (var c = 0; c < _NumColumns; c++)
|
||||
{
|
||||
_Texts[_TextHeader[c]].Text = _Headers[c];
|
||||
}
|
||||
|
||||
var mainX = string.IsNullOrEmpty(_Headers[0]) ? _ListLeft : _MainColumnX;
|
||||
_Texts[_TextHeader[1]].X = mainX;
|
||||
for (var r = 0; r < _NumRows; r++)
|
||||
{
|
||||
_Texts[_TextCell[r, 1]].X = mainX;
|
||||
_Texts[_TextCell[r, 1]].W = _MainColumnW + _MainColumnX - mainX;
|
||||
}
|
||||
|
||||
for (var r = 0; r < _NumRows; r++)
|
||||
{
|
||||
var index = _Offset + r;
|
||||
var row = index < _Rows.Count ? _Rows[index] : null;
|
||||
|
||||
for (var c = 0; c < _NumColumns; c++)
|
||||
{
|
||||
var text = _Texts[_TextCell[r, c]];
|
||||
var isNoData = r == 0 && c == 1 && _Rows.Count == 0;
|
||||
if (row != null)
|
||||
{
|
||||
text.Text = row.Cells[c];
|
||||
}
|
||||
else
|
||||
{
|
||||
text.Text = isNoData ? "TR_SCREENSTATISTICS_NODATA" : string.Empty;
|
||||
}
|
||||
text.Visible = row != null || isNoData;
|
||||
}
|
||||
|
||||
var rank = _Statics[_StaticRank[r]];
|
||||
rank.Visible = row != null && row.Rank >= 1 && row.Rank <= _RankColors.Length;
|
||||
if (rank.Visible && row != null)
|
||||
{
|
||||
SColorF color;
|
||||
if (CBase.Themes.GetColor(_RankColors[row.Rank - 1], PartyModeId, out color))
|
||||
{
|
||||
rank.Color = color;
|
||||
}
|
||||
}
|
||||
|
||||
var bar = _ProgressBars[_ProgressBarRow[r]];
|
||||
bar.Visible = row != null && row.Bar >= 0f;
|
||||
if (bar.Visible && row != null && Math.Abs(bar.Progress - row.Bar) > 0.0001f)
|
||||
{
|
||||
bar.Progress = row.Bar;
|
||||
}
|
||||
|
||||
var avatar = _Statics[_StaticAvatar[r]];
|
||||
var texture = row != null && row.Player != null ? CStatistics.GetAvatar(row.Player) : null;
|
||||
avatar.Texture = texture;
|
||||
avatar.Visible = texture != null;
|
||||
avatar.X = row != null && row.AvatarBeforeMain ? _AvatarMainX : _AvatarPlayerX;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -244,6 +244,7 @@
|
||||
<Compile Include="Base\CCover.cs" />
|
||||
<Compile Include="Base\CCursor.cs" />
|
||||
<Compile Include="Base\CDataBase.cs" />
|
||||
<Compile Include="Base\CStatistics.cs" />
|
||||
<Compile Include="Base\CDraw.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\PortAudio\CPt1.cs" />
|
||||
<Compile Include="Lib\Sound\Playback\PortAudio\CSyncTimer.cs" />
|
||||
@@ -343,6 +344,8 @@
|
||||
<Compile Include="Screens\CScreenOptionsGame.cs" />
|
||||
<Compile Include="Screens\CScreenOptionsLyrics.cs" />
|
||||
<Compile Include="Screens\CScreenParty.cs" />
|
||||
<Compile Include="Screens\CScreenStatistics.cs" />
|
||||
<Compile Include="Screens\CScreenStatisticsDetail.cs" />
|
||||
<Compile Include="Screens\CScreenProfiles.cs" />
|
||||
<Compile Include="Screens\CScreenScore.cs" />
|
||||
<Compile Include="Screens\CScreenSing.cs" />
|
||||
|
||||
@@ -430,6 +430,8 @@ namespace VocaluxeLib
|
||||
Names,
|
||||
Credits,
|
||||
Party,
|
||||
Statistics,
|
||||
StatisticsDetail,
|
||||
|
||||
CountEntry //Leave this as last entry and never use it!
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user