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:
McSchneidarandFlorian Ostertag authored and GitHub committed 2026-10-17 05:17:36 +02:00
1 parent d03224f29b
commit 602417e773
17 files changed
+3853 -12

No files matched your search

+39
View File
@@ -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>
+39
View File
@@ -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
+15
View File
@@ -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);
+2
View File
@@ -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");
+133
View File
@@ -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;
}
}
}
+125
View File
@@ -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";
+15 -2
View File
@@ -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);
+286
View File
@@ -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;
}
}
}
}
+460
View File
@@ -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;
}
}
}
}
+3
View File
@@ -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" />
+2
View File
@@ -430,6 +430,8 @@ namespace VocaluxeLib
Names,
Credits,
Party,
Statistics,
StatisticsDetail,
CountEntry //Leave this as last entry and never use it!
}