From 13220c16acafe9e9c1cab5a59a6be4ce38b43754 Mon Sep 17 00:00:00 2001 From: s_alexander Date: Sun, 6 Dec 2009 12:13:34 +0000 Subject: Add Date to Score, Filter Players, Switch Difficulty - ID: 2901824 applied patch form sf.net bug tracker (id: 2901824) thanks to brunzelchen - Add a date-column to the score entries (UNIX-Timestamp); the date will be shown on Top5- und Stats Screen! - The Players on Top-5-Screen are now filtered: Every player with his best score! - You can switch through the difficulties on Top-5-Screen git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1972 b956fd51-792f-4845-bead-9b4dfca2ff2c --- game/languages/English.ini | 5 +- game/languages/German.ini | 5 +- game/themes/Deluxe.ini | 75 ++++++++++++++++++++-- src/base/UDataBase.pas | 127 +++++++++++++++++++++++--------------- src/base/USong.pas | 1 + src/base/UThemes.pas | 2 + src/screens/UScreenStatDetail.pas | 2 +- src/screens/UScreenTop5.pas | 78 ++++++++++++++++++++++- 8 files changed, 234 insertions(+), 61 deletions(-) diff --git a/game/languages/English.ini b/game/languages/English.ini index 9053328e..243415fb 100644 --- a/game/languages/English.ini +++ b/game/languages/English.ini @@ -233,6 +233,7 @@ SING_SCORE_ULTRASTAR=Ultrastar SING_TOP_5_CHARTS=top 5 Players SING_TOP_5_CHARTS_WHEREAMI=top 5 SING_TOP_5_CHARTS_CONTINUE=to song selection +SING_TOP_5_CHARTS_SWITCH_DIFFICULTY=switch difficulty POPUP_PERFECT=perfect! POPUP_AWESOME=awesome! @@ -361,6 +362,8 @@ STAT_OVERVIEW_INTRO=%0:s Statistics. \n Last Reset at %2:.2d.%1:.2d.%3:d STAT_OVERVIEW_SONG=%0:d Songs(%3:d with Video), whereof %1:d already were played and %2:d were not played yet.\n The most popular Song is %5:s from %4:s. STAT_OVERVIEW_PLAYER=Since the last Reset there were/was %0:d different Player(s).\n The best Player is %1:s with an average Score of %2:d Points.\n %3:s did the highest Score with %4:d Points. +STAT_FORMAT_DATE=%1:.2d.%0:.2d.%2:d + STAT_DETAIL=Statistics STAT_DETAIL_WHEREAMI=Detail Statistics @@ -371,7 +374,7 @@ STAT_PAGE=Seite %0:d of %1:d Pages\n (%2:d of %3:d Entrys) STAT_DESC_SCORES=HighScores STAT_DESC_SCORES_REVERSED=LowScores -STAT_FORMAT_SCORES=%0:s - %1:d [%2:s] \n (%3:s - %4:s) +STAT_FORMAT_SCORES=%0:s - %1:d [%2:s] %5:s \n (%3:s - %4:s) STAT_DESC_SINGERS=Best Singers STAT_DESC_SINGERS_REVERSED=Worst Singers diff --git a/game/languages/German.ini b/game/languages/German.ini index 7b29c494..4aae568f 100644 --- a/game/languages/German.ini +++ b/game/languages/German.ini @@ -233,6 +233,7 @@ SING_SCORE_ULTRASTAR=UltraStar SING_TOP_5_CHARTS=Top-5-Spieler SING_TOP_5_CHARTS_WHEREAMI=Top 5 SING_TOP_5_CHARTS_CONTINUE=Zur Songauswahl +SING_TOP_5_CHARTS_SWITCH_DIFFICULTY=Schwierigkeitsstufen POPUP_PERFECT=Perfekt! POPUP_AWESOME=Cool! @@ -361,6 +362,8 @@ STAT_OVERVIEW_INTRO=%0:s Statistiken. \n Letzter Reset am %1:.2d.%2:.2d.%3:d STAT_OVERVIEW_SONG=%0:d Songs(%3:d mit Video), davon wurden %1:d schon einmal gesungen und %2:d noch nicht. \n Der am häufigsten gesungene Song ist %5:s von %4:s. STAT_OVERVIEW_PLAYER=Seit dem letzten Reset haben %0:d verschiedene Spieler gesungen. \n Der beste Spieler ist %1:s mit %2:d Punkten. \n Die höchste Punktzahl, %4:d, wurde von %3:s erreicht. +STAT_FORMAT_DATE=%0:.2d.%1:.2d.%2:d + STAT_DETAIL=Statistiken STAT_DETAIL_WHEREAMI=Detaillierte Statistiken @@ -371,7 +374,7 @@ STAT_PAGE=Seite %0:d von %1:d \n (%2:d von %3:d Einträgen) STAT_DESC_SCORES=Highscores STAT_DESC_SCORES_REVERSED=Lowscores -STAT_FORMAT_SCORES=%0:s - %1:d [%2:s] \n (%3:s - %4:s) +STAT_FORMAT_SCORES=%0:s - %1:d [%2:s] %5:s \n (%3:s - %4:s) STAT_DESC_SINGERS=Beste Sänger STAT_DESC_SINGERS_REVERSED=Schlechteste Sänger diff --git a/game/themes/Deluxe.ini b/game/themes/Deluxe.ini index f4bee1ab..cf3ad368 100644 --- a/game/themes/Deluxe.ini +++ b/game/themes/Deluxe.ini @@ -725,7 +725,6 @@ Align = 2 Text = PARTY_SONG_WHEREAMI #variable statics end - # Jokers, 5 for each team, only shown in party Mode [SongStaticTeam1Joker1] Tex = Joker @@ -4442,6 +4441,24 @@ Size = 21 Align = 0 Text = SING_TOP_5_CHARTS_CONTINUE +[Top5Static5] +X = 530 +Y = 552 +W = 24 +H = 23 +Tex = ButtonNavi +Color = White +Type = Transparent + +[Top5Text5] +X = 560 +Y = 552 +Color = Black +Font = 0 +Size = 21 +Align = 0 +Text = SING_TOP_5_CHARTS_SWITCH_DIFFICULTY + [Top5TextName1] X = 150 Y = 190 @@ -4488,7 +4505,7 @@ Align = 0 Text = 5. Player5 [Top5TextScore1] -X = 680 +X = 560 Y = 190 Color = White Font = 0 @@ -4497,7 +4514,7 @@ Align = 2 Text = 00000 [Top5TextScore2] -X = 680 +X = 560 Y = 240 Color = White Font = 0 @@ -4506,7 +4523,7 @@ Align = 2 Text = 00000 [Top5TextScore3] -X = 680 +X = 560 Y = 290 Color = White Font = 0 @@ -4515,7 +4532,7 @@ Align = 2 Text = 00000 [Top5TextScore4] -X = 680 +X = 560 Y = 340 Color = White Font = 0 @@ -4524,7 +4541,53 @@ Align = 2 Text = 00000 [Top5TextScore5] -X = 680 +X = 560 +Y = 390 +Color = White +Font = 0 +Size = 42 +Align = 2 +Text = 00000 + + +[Top5TextDate1] +X = 760 +Y = 190 +Color = White +Font = 0 +Size = 42 +Align = 2 +Text = 00000 + +[Top5TextDate2] +X = 760 +Y = 240 +Color = White +Font = 0 +Size = 42 +Align = 2 +Text = 00000 + +[Top5TextDate3] +X = 760 +Y = 290 +Color = White +Font = 0 +Size = 42 +Align = 2 +Text = 00000 + +[Top5TextDate4] +X = 760 +Y = 340 +Color = White +Font = 0 +Size = 42 +Align = 2 +Text = 00000 + +[Top5TextDate5] +X = 760 Y = 390 Color = White Font = 0 diff --git a/src/base/UDataBase.pas b/src/base/UDataBase.pas index bdcbd30f..85b4b8e8 100644 --- a/src/base/UDataBase.pas +++ b/src/base/UDataBase.pas @@ -64,6 +64,7 @@ type Difficulty: byte; SongArtist: UTF8String; SongTitle: UTF8String; + Date: UTF8String; end; TStatResultBestSingers = class(TStatResult) @@ -85,7 +86,7 @@ type TimesSungTot: word; end; - + TDataBaseSystem = class private ScoreDB: TSQLiteDatabase; @@ -107,6 +108,7 @@ type procedure FreeStats(StatList: TList); function GetTotalEntrys(Typ: TStatType): cardinal; function GetStatReset: TDateTime; + function FormatDate(time_stamp: integer): UTF8String; end; var @@ -116,6 +118,7 @@ implementation uses DateUtils, + ULanguage, StrUtils, SysUtils, ULog; @@ -145,7 +148,7 @@ begin Log.LogStatus('Initializing database: "' + Filename.ToNative + '"', 'TDataBaseSystem.Init'); try - + // open database ScoreDB := TSQLiteDatabase.Create(Filename.ToUTF8); fFilename := Filename; @@ -192,7 +195,8 @@ begin '[SongID] INTEGER NOT NULL, ' + '[Difficulty] INTEGER NOT NULL, ' + '[Player] TEXT NOT NULL, ' + - '[Score] INTEGER NOT NULL' + + '[Score] INTEGER NOT NULL, ' + + '[Date] INTEGER NULL' + ');'); ScoreDB.ExecSQL('CREATE TABLE IF NOT EXISTS [' + cUS_Songs + '] (' + @@ -200,7 +204,7 @@ begin '[Artist] TEXT NOT NULL, ' + '[Title] TEXT NOT NULL, ' + '[TimesPlayed] INTEGER NOT NULL, ' + - '[Rating] INTEGER NULL' + + '[Rating] INTEGER NULL' + ');'); // convert data from 1.01 to 1.1 @@ -221,7 +225,15 @@ begin if not ScoreDB.ContainsColumn(cUS_Songs, 'Rating') then begin Log.LogInfo('Outdated song database found - adding column rating to "' + cUS_Songs + '"', 'TDataBaseSystem.Init'); - ScoreDB.ExecSQL('ALTER TABLE ' + cUS_Songs + ' ADD COLUMN Rating INTEGER NULL'); + ScoreDB.ExecSQL('ALTER TABLE ' + cUS_Songs + ' ADD COLUMN [Rating] INTEGER NULL'); + end; + + + //add column date to cUS-Scores + if not ScoreDB.ContainsColumn(cUS_Scores, 'Date') then + begin + Log.LogInfo('adding column date to "' + cUS_Scores + '"', 'TDataBaseSystem.Init'); + ScoreDB.ExecSQL('ALTER TABLE ' + cUS_Scores + ' ADD COLUMN [Date] INTEGER NULL'); end; except @@ -244,6 +256,27 @@ begin inherited; end; +(** + * Format a UNIX-Timestamp into DATE (If 0 then '') + *) +function TDataBaseSystem.FormatDate(time_stamp: integer): UTF8String; +var + Year, Month, Day: word; +begin + Result:=''; + try + if time_stamp<>0 then + begin + DecodeDate(UnixToDateTime(time_stamp), Year, Month, Day); + Result := Format(Language.Translate('STAT_FORMAT_DATE'), [Day, Month, Year]); + end; + except + on E: EConvertError do + Log.LogError('Error Parsing FormatString "STAT_FORMAT_DATE": ' + E.Message); + end; +end; + + (** * Read Scores into SongArray *) @@ -251,27 +284,28 @@ procedure TDataBaseSystem.ReadScore(Song: TSong); var TableData: TSQLiteUniTable; Difficulty: integer; + I: integer; + PlayerListed: boolean; begin if not Assigned(ScoreDB) then Exit; TableData := nil; - try // Search Song in DB TableData := ScoreDB.GetUniTable( - 'SELECT [Difficulty], [Player], [Score] FROM [' + cUS_Scores + '] ' + + 'SELECT [Difficulty], [Player], [Score], [Date] FROM [' + cUS_Scores + '] ' + 'WHERE [SongID] = (' + 'SELECT [ID] FROM [' + cUS_Songs + '] ' + 'WHERE [Artist] = ? AND [Title] = ? ' + 'LIMIT 1) ' + - 'ORDER BY [Score] DESC LIMIT 15', + 'ORDER BY [Score] DESC;', //no LIMIT! see filter below! [Song.Artist, Song.Title]); // Empty Old Scores - SetLength(Song.Score[0], 0); - SetLength(Song.Score[1], 0); - SetLength(Song.Score[2], 0); + SetLength(Song.Score[0], 0); //easy + SetLength(Song.Score[1], 0); //medium + SetLength(Song.Score[2], 0); //hard // Go through all Entrys while (not TableData.EOF) do @@ -281,12 +315,31 @@ begin if ((Difficulty >= 0) and (Difficulty <= 2)) and (Length(Song.Score[Difficulty]) < 5) then begin - SetLength(Song.Score[Difficulty], Length(Song.Score[Difficulty]) + 1); + //filter player + PlayerListed:=false; + if (Length(Song.Score[Difficulty])>0) then + begin + for I := 0 to Length(Song.Score[Difficulty]) - 1 do + begin + if (Song.Score[Difficulty, I].Name = TableData.FieldByName['Player']) then + begin + PlayerListed:=true; + break; + end; + end; + end; - Song.Score[Difficulty, High(Song.Score[Difficulty])].Name := + if not PlayerListed then + begin + SetLength(Song.Score[Difficulty], Length(Song.Score[Difficulty]) + 1); + + Song.Score[Difficulty, High(Song.Score[Difficulty])].Name := TableData.FieldByName['Player']; - Song.Score[Difficulty, High(Song.Score[Difficulty])].Score := + Song.Score[Difficulty, High(Song.Score[Difficulty])].Score := TableData.FieldAsInteger(TableData.FieldIndex['Score']); + Song.Score[Difficulty, High(Song.Score[Difficulty])].Date := + FormatDate(TableData.FieldAsInteger(TableData.FieldIndex['Date'])); + end; end; TableData.Next; @@ -314,9 +367,9 @@ begin if not Assigned(ScoreDB) then Exit; - // Prevent 0 Scores from being added - if (Score <= 0) then - Exit; + // Prevent 0 Scores from being added EDIT: ==> UScreenTop5.pas! + //if (Score <= 0) then + // Exit; TableData := nil; @@ -339,37 +392,10 @@ begin end; // Create new entry ScoreDB.ExecSQL( - 'INSERT INTO [' + cUS_Scores + '] ' + - '([SongID] ,[Difficulty], [Player], [Score]) VALUES ' + - '(?, ?, ?, ?);', - [ID, Level, Name, Score]); - - // Delete last position when there are more than 5 entrys. - // Fixes crash when there are > 5 ScoreEntrys - // Note: GetUniTable is not applicable here, as the results are used while - // table entries are deleted. - TableData := ScoreDB.GetTable( - 'SELECT [Player], [Score] FROM [' + cUS_Scores + '] ' + - 'WHERE [SongID] = ' + InttoStr(ID) + ' AND ' + - '[Difficulty] = ' + InttoStr(Level) + ' ' + - 'ORDER BY [Score] DESC LIMIT -1 OFFSET 5'); - - while (not TableData.EOF) do - begin - // Note: Score is an int-value, so in contrast to Player, we do not bind - // this value. Otherwise we had to convert the string to an int to avoid - // an automatic cast of this field to the TEXT type (although it might even - // work that way). - ScoreDB.ExecSQL( - 'DELETE FROM [' + cUS_Scores + '] ' + - 'WHERE [SongID] = ' + InttoStr(ID) + ' AND ' + - '[Difficulty] = ' + InttoStr(Level) +' AND ' + - '[Player] = ? AND ' + - '[Score] = ' + TableData.FieldByName['Score'], - [TableData.FieldByName['Player']]); - - TableData.Next; - end; + 'INSERT INTO [' + cUS_Scores + '] ' + + '([SongID] ,[Difficulty], [Player], [Score], [Date]) VALUES ' + + '(?, ?, ?, ?, ?);', + [ID, Level, Name, Score, DateTimeToUnix(Now())]); except on E: Exception do Log.LogError(E.Message, 'TDataBaseSystem.AddScore'); @@ -386,7 +412,7 @@ procedure TDataBaseSystem.WriteScore(Song: TSong); begin if not Assigned(ScoreDB) then Exit; - + try // Increase TimesPlayed ScoreDB.ExecSQL( @@ -421,7 +447,7 @@ begin // Create query case Typ of stBestScores: begin - Query := 'SELECT [Player], [Difficulty], [Score], [Artist], [Title] FROM [' + cUS_Scores + '] ' + + Query := 'SELECT [Player], [Difficulty], [Score], [Artist], [Title], [Date] FROM [' + cUS_Scores + '] ' + 'INNER JOIN [' + cUS_Songs + '] ON ([SongID] = [ID]) ORDER BY [Score]'; end; stBestSingers: begin @@ -471,6 +497,7 @@ begin Score := TableData.FieldAsInteger(2); SongArtist := TableData.Fields[3]; SongTitle := TableData.Fields[4]; + Date := FormatDate(TableData.FieldAsInteger(5)); end; end; stBestSingers: begin diff --git a/src/base/USong.pas b/src/base/USong.pas index f8698d43..c465f198 100644 --- a/src/base/USong.pas +++ b/src/base/USong.pas @@ -74,6 +74,7 @@ type TScore = record Name: UTF8String; Score: integer; + Date: UTF8String; end; { used to hold header tags that are not supported by this version of diff --git a/src/base/UThemes.pas b/src/base/UThemes.pas index c1a26927..4322815e 100644 --- a/src/base/UThemes.pas +++ b/src/base/UThemes.pas @@ -409,6 +409,7 @@ type TextNumber: AThemeText; TextName: AThemeText; TextScore: AThemeText; + TextDate: AThemeText; end; TThemeOptions = class(TThemeBasic) @@ -1176,6 +1177,7 @@ begin ThemeLoadTexts(Top5.TextNumber, 'Top5TextNumber'); ThemeLoadTexts(Top5.TextName, 'Top5TextName'); ThemeLoadTexts(Top5.TextScore, 'Top5TextScore'); + ThemeLoadTexts(Top5.TextDate, 'Top5TextDate'); // Options ThemeLoadBasic(Options, 'Options'); diff --git a/src/screens/UScreenStatDetail.pas b/src/screens/UScreenStatDetail.pas index 249626b0..1638cd85 100644 --- a/src/screens/UScreenStatDetail.pas +++ b/src/screens/UScreenStatDetail.pas @@ -234,7 +234,7 @@ begin if (Score > 0) then begin Text[I].Text := Format(FormatStr, - [Singer, Score, Theme.ILevel[Difficulty], SongArtist, SongTitle]); + [Singer, Score, Theme.ILevel[Difficulty], SongArtist, SongTitle, Date]); end; end; end; diff --git a/src/screens/UScreenTop5.pas b/src/screens/UScreenTop5.pas index f9c86643..b0795f54 100644 --- a/src/screens/UScreenTop5.pas +++ b/src/screens/UScreenTop5.pas @@ -47,11 +47,13 @@ type public TextLevel: integer; TextArtistTitle: integer; + DifficultyShow: integer; StaticNumber: array[1..5] of integer; TextNumber: array[1..5] of integer; TextName: array[1..5] of integer; TextScore: array[1..5] of integer; + TextDate: array[1..5] of integer; Fadeout: boolean; @@ -59,6 +61,7 @@ type function ParseInput(PressedKey: cardinal; CharCode: UCS4Char; PressedDown: boolean): boolean; override; function ParseMouse(MouseButton: integer; BtnDown: boolean; X, Y: integer): boolean; override; procedure OnShow; override; + procedure DrawScores(difficulty: integer); function Draw: boolean; override; end; @@ -98,6 +101,34 @@ begin Fadeout := true; end; end; + SDLK_RIGHT: + begin + inc(DifficultyShow); + if (DifficultyShow>2) then + DifficultyShow:=0; + DrawScores(DifficultyShow); + end; + SDLK_LEFT: + begin + dec(DifficultyShow); + if (DifficultyShow<0) then + DifficultyShow:=2; + DrawScores(DifficultyShow); + end; + SDLK_UP: + begin + inc(DifficultyShow); + if (DifficultyShow>2) then + DifficultyShow:=0; + DrawScores(DifficultyShow); + end; + SDLK_DOWN: + begin + dec(DifficultyShow); + if (DifficultyShow<0) then + DifficultyShow:=2; + DrawScores(DifficultyShow); + end; SDLK_SYSREQ: begin Display.SaveScreenShot; @@ -133,6 +164,7 @@ begin TextNumber[I+1] := AddText (Theme.Top5.TextNumber[I]); TextName[I+1] := AddText (Theme.Top5.TextName[I]); TextScore[I+1] := AddText (Theme.Top5.TextScore[I]); + TextDate[I+1] := AddText (Theme.Top5.TextDate[I]); end; end; @@ -141,10 +173,13 @@ procedure TScreenTop5.OnShow; var I: integer; PMax: integer; + sung: boolean; //score added? otherwise in wasn't sung! begin inherited; + sung := false; Fadeout := false; + DifficultyShow := Ini.Difficulty; //ReadScore(CurrentSong); @@ -152,9 +187,16 @@ begin if PMax = 4 then PMax := 5; for I := 0 to PMax do - DataBase.AddScore(CurrentSong, Ini.Difficulty, Ini.Name[I], Round(Player[I].ScoreTotalInt)); + begin + if (Round(Player[I].ScoreTotalInt) > 0) then + begin + DataBase.AddScore(CurrentSong, Ini.Difficulty, Ini.Name[I], Round(Player[I].ScoreTotalInt)); + sung:=true; + end; + end; - DataBase.WriteScore(CurrentSong); + if sung then + DataBase.WriteScore(CurrentSong); DataBase.ReadScore(CurrentSong); Text[TextArtistTitle].Text := CurrentSong.Artist + ' - ' + CurrentSong.Title; @@ -165,9 +207,11 @@ begin Text[TextNumber[I]].Visible := true; Text[TextName[I]].Visible := true; Text[TextScore[I]].Visible := true; + Text[TextDate[I]].Visible := true; Text[TextName[I]].Text := CurrentSong.Score[Ini.Difficulty, I-1].Name; Text[TextScore[I]].Text := IntToStr(CurrentSong.Score[Ini.Difficulty, I-1].Score); + Text[TextDate[I]].Text := CurrentSong.Score[Ini.Difficulty, I-1].Date; end; for I := Length(CurrentSong.Score[Ini.Difficulty]) + 1 to 5 do @@ -176,11 +220,41 @@ begin Text[TextNumber[I]].Visible := false; Text[TextName[I]].Visible := false; Text[TextScore[I]].Visible := false; + Text[TextDate[I]].Visible := false; end; Text[TextLevel].Text := IDifficulty[Ini.Difficulty]; end; +procedure TScreenTop5.DrawScores(difficulty: integer); +var + I: integer; +begin + for I := 1 to Length(CurrentSong.Score[difficulty]) do + begin + Static[StaticNumber[I]].Visible := true; + Text[TextNumber[I]].Visible := true; + Text[TextName[I]].Visible := true; + Text[TextScore[I]].Visible := true; + Text[TextDate[I]].Visible := true; + + Text[TextName[I]].Text := CurrentSong.Score[difficulty, I-1].Name; + Text[TextScore[I]].Text := IntToStr(CurrentSong.Score[difficulty, I-1].Score); + Text[TextDate[I]].Text := CurrentSong.Score[difficulty, I-1].Date; + end; + + for I := Length(CurrentSong.Score[difficulty]) + 1 to 5 do + begin + Static[StaticNumber[I]].Visible := false; + Text[TextNumber[I]].Visible := false; + Text[TextName[I]].Visible := false; + Text[TextScore[I]].Visible := false; + Text[TextDate[I]].Visible := false; + end; + + Text[TextLevel].Text := IDifficulty[difficulty]; +end; + function TScreenTop5.Draw: boolean; //var { -- cgit v1.2.3