diff options
-rw-r--r-- | Game/Code/Classes/UDataBase.pas | 258 | ||||
-rw-r--r-- | Game/Code/Screens/UScreenStatDetail.pas | 72 | ||||
-rw-r--r-- | Game/Code/Screens/UScreenStatMain.pas | 12 |
3 files changed, 182 insertions, 160 deletions
diff --git a/Game/Code/Classes/UDataBase.pas b/Game/Code/Classes/UDataBase.pas index e00965c4..cfe36ca9 100644 --- a/Game/Code/Classes/UDataBase.pas +++ b/Game/Code/Classes/UDataBase.pas @@ -16,23 +16,35 @@ uses USongs, //DataBaseSystem - Class including all DB Methods //-------------------- type - TStatResult = record - case Typ: Byte of - 0: (Singer: ShortString; - Score: Word; - Difficulty: Byte; - SongArtist: ShortString; - SongTitle: ShortString); + TStatType = ( + stBestScores, // Best Scores + stBestSingers, // Best Singers + stMostSungSong, // Most sung Songs + stMostPopBand // Most popular Band + ); - 1: (Player: ShortString; - AverageScore: Word); - - 2: (Artist: ShortString; - Title: ShortString; - TimesSung: Word); - - 3: (ArtistName: ShortString; - TimesSungTot: Word); + TStatResult = record + case Typ: TStatType of + stBestScores: ( + Singer: ShortString; + Score: Word; + Difficulty: Byte; + SongArtist: ShortString; + SongTitle: ShortString + ); + stBestSingers: ( + Player: ShortString; + AverageScore: Word + ); + stMostSungSong: ( + Artist: ShortString; + Title: ShortString; + TimesSung: Word + ); + stMostPopBand: ( + ArtistName: ShortString; + TimesSungTot: Word + ); end; AStatResult = array of TStatResult; @@ -68,8 +80,8 @@ type procedure AddScore(Song: TSong; Level: integer; const Name: WideString; Score: integer); procedure WriteScore(Song: TSong); - function GetStats(var Stats: AStatResult; Typ, Count: Byte; Page: Cardinal; Reversed: Boolean): Boolean; - function GetTotalEntrys(Typ: Byte): Cardinal; + function GetStats(var Stats: AStatResult; Typ: TStatType; Count: Byte; Page: Cardinal; Reversed: Boolean): Boolean; + function GetTotalEntrys(Typ: TStatType): Cardinal; function GetStatReset: TDateTime; end; @@ -88,10 +100,9 @@ const cUS_Songs = 'us_songs'; cUS_Statistics_Info = 'us_statistics_info'; -//-------------------- -//Create - Opens Database and Create Tables if not Exist -//-------------------- - +(** + * Opens Database and Create Tables if not Exist + *) procedure TDataBaseSystem.Init(const Filename: string); begin if Assigned(ScoreDB) then @@ -105,6 +116,14 @@ begin ScoreDB := TSQLiteDatabase.Create(Filename); fFilename := Filename; + // SQLite does not handle VARCHAR(n) or INT(n) as expected. + // Texts do not have a restricted length, no matter which type is used, + // so use the native TEXT type. INT(n) is always INTEGER. + // In addition, SQLiteTable3 will fail if other types than the native SQLite + // types are used (especially FieldAsInteger). Also take care to write the + // types in upper-case letters although SQLite does not care about this - + // SQLiteTable3 is very sensitive in this regard. + ScoreDB.ExecSQL('CREATE TABLE IF NOT EXISTS ['+cUS_Scores+'] (' + '[SongID] INTEGER NOT NULL, ' + '[Difficulty] INTEGER NOT NULL, ' + @@ -139,9 +158,9 @@ begin end; -//-------------------- -//Destroy - Frees Database -//-------------------- +(** + * Frees Database + *) destructor TDataBaseSystem.Destroy; begin Log.LogInfo('TDataBaseSystem.Free', 'TDataBaseSystem.Destroy'); @@ -149,12 +168,12 @@ begin inherited; end; -//-------------------- -//ReadScore - Read Scores into SongArray -//-------------------- +(** + * Read Scores into SongArray + *) procedure TDataBaseSystem.ReadScore(Song: TSong); var - TableData: TSqliteTable; + TableData: TSQLiteUniTable; Difficulty: Integer; begin if not Assigned(ScoreDB) then @@ -164,7 +183,7 @@ begin try // Search Song in DB - TableData := ScoreDB.GetTable( + TableData := ScoreDB.GetUniTable( 'SELECT [Difficulty], [Player], [Score] FROM ['+cUS_Scores+'] ' + 'WHERE [SongID] = (' + 'SELECT [ID] FROM ['+cUS_Songs+'] ' + @@ -182,18 +201,18 @@ begin while (not TableData.EOF) do begin // Add one Entry to Array - Difficulty := StrToIntDef(TableData.FieldAsString(TableData.FieldIndex['Difficulty']), -1); + Difficulty := TableData.FieldAsInteger(TableData.FieldIndex['Difficulty']); if ((Difficulty >= 0) and (Difficulty <= 2)) and (Length(Song.Score[Difficulty]) < 5) then begin SetLength(Song.Score[Difficulty], Length(Song.Score[Difficulty]) + 1); Song.Score[Difficulty, High(Song.Score[Difficulty])].Name := - UTF8Decode(TableData.FieldAsString(TableData.FieldIndex['Player'])); + UTF8Decode(TableData.FieldByName['Player']); Song.Score[Difficulty, High(Song.Score[Difficulty])].Score := - StrtoInt(TableData.FieldAsString(TableData.FieldIndex['Score'])); + TableData.FieldAsInteger(TableData.FieldIndex['Score']); end; - + TableData.Next; end; // while @@ -208,9 +227,9 @@ begin TableData.Free; end; -//-------------------- -//AddScore - Add one new Score to DB -//-------------------- +(** + * Adds one new score to DB + *) procedure TDataBaseSystem.AddScore(Song: TSong; Level: integer; const Name: WideString; Score: integer); var ID: Integer; @@ -219,7 +238,7 @@ begin if not Assigned(ScoreDB) then Exit; - //Prevent 0 Scores from being added + // Prevent 0 Scores from being added if (Score <= 0) then Exit; @@ -231,43 +250,47 @@ begin 'SELECT [ID] FROM ['+cUS_Songs+'] ' + 'WHERE [Artist] = ? AND [Title] = ?', [UTF8Encode(Song.Artist), UTF8Encode(Song.Title)]); - if ID = 0 then //Song doesn't exist -> Create + if (ID = 0) then begin - ScoreDB.ExecSQL ( + // Create song if it does not exist + ScoreDB.ExecSQL( 'INSERT INTO ['+cUS_Songs+'] ' + '([ID], [Artist], [Title], [TimesPlayed]) VALUES ' + '(NULL, ?, ?, 0);', [UTF8Encode(Song.Artist), UTF8Encode(Song.Title)]); - ID := ScoreDB.GetTableValue( - 'SELECT [ID] FROM ['+cUS_Songs+'] ' + - 'WHERE [Artist] = ? AND [Title] = ?', - [UTF8Encode(Song.Artist), UTF8Encode(Song.Title)]); - if ID = 0 then //Could not Create Table - Exit; + // Get song-ID + ID := ScoreDB.GetLastInsertRowID(); end; - //Create new Entry + // Create new entry ScoreDB.ExecSQL( 'INSERT INTO ['+cUS_Scores+'] ' + '([SongID] ,[Difficulty], [Player], [Score]) VALUES ' + '(?, ?, ?, ?);', [ID, Level, UTF8Encode(Name), Score]); - //Delete Last Position when there are more than 5 Entrys - Fixes Crash when there are > 5 ScoreEntrys + // 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 + 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.FieldAsString(TableData.FieldIndex['Player']), - TableData.FieldAsString(TableData.FieldIndex['Score'])]); + '[Player] = ? AND ' + + '[Score] = ' + TableData.FieldByName['Score'], + [TableData.FieldByName['Player']]); TableData.Next; end; @@ -279,16 +302,17 @@ begin TableData.Free; end; -//-------------------- -//WriteScore - Not needed with new System; But used for Increment Played Count -//-------------------- +(** + * Not needed with new System. + * Used for increment played count + *) procedure TDataBaseSystem.WriteScore(Song: TSong); begin if not Assigned(ScoreDB) then Exit; try - //Increase TimesPlayed + // Increase TimesPlayed ScoreDB.ExecSQL( 'UPDATE ['+cUS_Songs+'] ' + 'SET [TimesPlayed] = [TimesPlayed] + 1 ' + @@ -299,18 +323,14 @@ begin end; end; -//-------------------- -//GetStats - Write some Stats to Array, Returns True if Chossen Page has Entrys -//Case Typ of -//0 - Best Scores -//1 - Best Singers -//2 - Most sung Songs -//3 - Most popular Band -//-------------------- -function TDataBaseSystem.GetStats(var Stats: AStatResult; Typ, Count: Byte; Page: Cardinal; Reversed: Boolean): Boolean; +(** + * Writes some stats to array. + * Returns true if choosen page has entrys + *) +function TDataBaseSystem.GetStats(var Stats: AStatResult; Typ: TStatType; Count: Byte; Page: Cardinal; Reversed: Boolean): Boolean; var Query: String; - TableData: TSqliteTable; + TableData: TSQLiteUniTable; begin Result := False; @@ -320,37 +340,37 @@ begin if (Length(Stats) < Count) then Exit; - {Todo: Add Prevention that only Players with more than 5 Scores are Selected at Typ 2} + {Todo: Add Prevention that only players with more than 5 scores are selected at type 2} - //Create Query + // Create query case Typ of - 0: begin + stBestScores: begin Query := 'SELECT [Player], [Difficulty], [Score], [Artist], [Title] FROM ['+cUS_Scores+'] ' + 'INNER JOIN ['+cUS_Songs+'] ON ([SongID] = [ID]) ORDER BY [Score]'; end; - 1: begin + stBestSingers: begin Query := 'SELECT [Player], ROUND(AVG([Score])) FROM ['+cUS_Scores+'] ' + 'GROUP BY [Player] ORDER BY AVG([Score])'; end; - 2: begin + stMostSungSong: begin Query := 'SELECT [Artist], [Title], [TimesPlayed] FROM ['+cUS_Songs+'] ' + 'ORDER BY [TimesPlayed]'; end; - 3: begin + stMostPopBand: begin Query := 'SELECT [Artist], SUM([TimesPlayed]) FROM ['+cUS_Songs+'] ' + 'GROUP BY [Artist] ORDER BY SUM([TimesPlayed])'; end; end; - //Add Order Direction + // Add order direction Query := Query + IfThen(Reversed, ' ASC', ' DESC'); - //Add Limit + // Add limit Query := Query + ' LIMIT ' + InttoStr(Count * Page) + ', ' + InttoStr(Count) + ';'; - //Execute Query + // Execute query try - TableData := ScoreDB.GetTable(Query); + TableData := ScoreDB.GetUniTable(Query); except on E: Exception do begin @@ -359,42 +379,38 @@ begin end; end; - //if result is empty -> Exit - if (TableData.RowCount < 1) then + if (TableData.EOF) then + begin + TableData.Free; Exit; + end; - //Copy Result to Stats Array + // Copy result to stats array while not TableData.EOF do begin Stats[TableData.Row].Typ := Typ; case Typ of - 0:begin - Stats[TableData.Row].Singer := UTF8Decode(TableData.Fields[0]); - - Stats[TableData.Row].Difficulty := StrtoIntDef(TableData.Fields[1], 0); - - Stats[TableData.Row].Score := StrtoIntDef(TableData.Fields[2], 0){TableData.FieldAsInteger(2)}; - Stats[TableData.Row].SongArtist := UTF8Decode(TableData.Fields[3]); - Stats[TableData.Row].SongTitle := UTF8Decode(TableData.Fields[4]); - end; - - 1:begin - Stats[TableData.Row].Player := UTF8Decode(TableData.Fields[0]); - Stats[TableData.Row].AverageScore := StrtoIntDef(TableData.Fields[1], 0); - end; - - 2:begin - Stats[TableData.Row].Artist := UTF8Decode(TableData.Fields[0]); - Stats[TableData.Row].Title := UTF8Decode(TableData.Fields[1]); - Stats[TableData.Row].TimesSung := StrtoIntDef(TableData.Fields[2], 0); - end; - - 3:begin - Stats[TableData.Row].ArtistName := UTF8Decode(TableData.Fields[0]); - Stats[TableData.Row].TimesSungTot := StrtoIntDef(TableData.Fields[1], 0); - end; - + stBestScores: begin + Stats[TableData.Row].Singer := UTF8Decode(TableData.Fields[0]); + Stats[TableData.Row].Difficulty := TableData.FieldAsInteger(1); + Stats[TableData.Row].Score := TableData.FieldAsInteger(2); + Stats[TableData.Row].SongArtist := UTF8Decode(TableData.Fields[3]); + Stats[TableData.Row].SongTitle := UTF8Decode(TableData.Fields[4]); + end; + stBestSingers: begin + Stats[TableData.Row].Player := UTF8Decode(TableData.Fields[0]); + Stats[TableData.Row].AverageScore := TableData.FieldAsInteger(1); + end; + stMostSungSong: begin + Stats[TableData.Row].Artist := UTF8Decode(TableData.Fields[0]); + Stats[TableData.Row].Title := UTF8Decode(TableData.Fields[1]); + Stats[TableData.Row].TimesSung := TableData.FieldAsInteger(2); + end; + stMostPopBand: begin + Stats[TableData.Row].ArtistName := UTF8Decode(TableData.Fields[0]); + Stats[TableData.Row].TimesSungTot := TableData.FieldAsInteger(1); + end; end; TableData.Next; @@ -404,10 +420,10 @@ begin Result := True; end; -//-------------------- -//GetTotalEntrys - Get Total Num of entrys for a Stats Query -//-------------------- -function TDataBaseSystem.GetTotalEntrys(Typ: Byte): Cardinal; +(** + * Gets total number of entrys for a stats query + *) +function TDataBaseSystem.GetTotalEntrys(Typ: TStatType): Cardinal; var Query: String; begin @@ -417,12 +433,16 @@ begin Exit; try - //Create Query + // Create query case Typ of - 0: Query := 'SELECT COUNT([SongID]) FROM ['+cUS_Scores+'];'; - 1: Query := 'SELECT COUNT(DISTINCT [Player]) FROM ['+cUS_Scores+'];'; - 2: Query := 'SELECT COUNT([ID]) FROM ['+cUS_Songs+'];'; - 3: Query := 'SELECT COUNT(DISTINCT [Artist]) FROM ['+cUS_Songs+'];'; + stBestScores: + Query := 'SELECT COUNT([SongID]) FROM ['+cUS_Scores+'];'; + stBestSingers: + Query := 'SELECT COUNT(DISTINCT [Player]) FROM ['+cUS_Scores+'];'; + stMostSungSong: + Query := 'SELECT COUNT([ID]) FROM ['+cUS_Songs+'];'; + stMostPopBand: + Query := 'SELECT COUNT(DISTINCT [Artist]) FROM ['+cUS_Songs+'];'; end; Result := ScoreDB.GetTableValue(Query); @@ -432,13 +452,13 @@ begin end; -//-------------------- -//GetStatReset - Get reset date of statistic data -//-------------------- +(** + * Gets reset date of statistic data + *) function TDataBaseSystem.GetStatReset: TDateTime; var Query: string; - TableData: TSQLiteTable; + TableData: TSQLiteUniTable; begin Result := 0; @@ -449,7 +469,7 @@ begin try Query := 'SELECT [ResetTime] FROM ['+cUS_Statistics_Info+'];'; - TableData := ScoreDB.GetTable(Query); + TableData := ScoreDB.GetUniTable(Query); Result := StrToDateTime(TableData.Fields[0]); except on E: Exception do Log.LogError(E.Message, 'TDataBaseSystem.GetStatReset'); diff --git a/Game/Code/Screens/UScreenStatDetail.pas b/Game/Code/Screens/UScreenStatDetail.pas index 98faed03..b9a3b2df 100644 --- a/Game/Code/Screens/UScreenStatDetail.pas +++ b/Game/Code/Screens/UScreenStatDetail.pas @@ -5,12 +5,19 @@ interface {$I switches.inc} uses - UMenu, SDL, SysUtils, UDisplay, UMusic, UIni, UThemes; + UMenu, + SDL, + SysUtils, + UDisplay, + UMusic, + UIni, + UDataBase, + UThemes; type TScreenStatDetail = class(TMenu) public - Typ: Byte; + Typ: TStatType; Page: CardinaL; Count: Byte; Reversed: Boolean; @@ -30,14 +37,11 @@ type implementation -{Stat Screens: - 0 - Best Scores - 1 - Best Singers - 2 - Most sung Songs - 3 - Most popular Band -} - -uses UGraphic, UDataBase, ULanguage, math, ULog; +uses + UGraphic, + ULanguage, + math, + ULog; function TScreenStatDetail.ParseInput(PressedKey: Cardinal; CharCode: WideChar; PressedDown: Boolean): Boolean; begin @@ -138,7 +142,7 @@ begin AddButtonText(14, 20, Theme.Options.Description[7]); Interaction := 0; - Typ := 0; + Typ := TStatType(0); end; procedure TScreenStatDetail.onShow; @@ -148,8 +152,10 @@ begin //Set Tot Entrys and PAges TotEntrys := DataBase.GetTotalEntrys(Typ); TotPages := Ceil(TotEntrys / Count); + //Show correct Title SetTitle; + //Show First Page Reversed := False; SetPage(0); @@ -157,14 +163,13 @@ end; procedure TScreenStatDetail.SetTitle; begin - //Set Title - Case Reversed of - True: Text[Count].Text := Theme.StatDetail.DescriptionR[Typ]; - False: Text[Count].Text := Theme.StatDetail.Description[Typ]; - end; + if Reversed then + Text[Count].Text := Theme.StatDetail.DescriptionR[Ord(Typ)] + else + Text[Count].Text := Theme.StatDetail.Description[Ord(Typ)]; end; -Procedure TScreenStatDetail.SetPage(NewPage: Cardinal); +procedure TScreenStatDetail.SetPage(NewPage: Cardinal); var Result: AStatResult; I: Integer; @@ -176,16 +181,16 @@ begin begin Page := NewPage; - FormatStr := Theme.StatDetail.FormatStr[Typ]; + FormatStr := Theme.StatDetail.FormatStr[Ord(Typ)]; //refresh Texts - For I := 0 to Count-1 do + for I := 0 to Count-1 do begin try case Typ of - 0:begin //Best Scores + stBestScores: begin //Best Scores //Set Texts - if (Result[I].Score>0) then + if (Result[I].Score > 0) then Text[I].Text := Format(FormatStr, [Result[I].Singer, Result[I].Score, Theme.ILevel[Result[I].Difficulty], @@ -195,18 +200,18 @@ begin Text[I].Text := ''; end; - 1:begin //Best Singers + stBestSingers: begin //Best Singers //Set Texts - if (Result[I].AverageScore>0) then + if (Result[I].AverageScore > 0) then Text[I].Text := Format(FormatStr, [Result[I].Player, Result[I].AverageScore]) else Text[I].Text := ''; end; - 2:begin //Popular Songs + stMostSungSong: begin //Popular Songs //Set Texts - if (Result[I].Artist<>'') then + if (Result[I].Artist <> '') then Text[I].Text := Format(FormatStr, [Result[I].Artist, Result[I].Title, Result[I].TimesSung]) @@ -214,9 +219,9 @@ begin Text[I].Text := ''; end; - 3:begin //Popular Bands + stMostPopBand: begin //Popular Bands //Set Texts - if (Result[I].ArtistName<>'') then + if (Result[I].ArtistName <> '') then Text[I].Text := Format(FormatStr, [Result[I].ArtistName, Result[I].TimesSungtot]) else @@ -229,19 +234,16 @@ begin end; end; - if (Page + 1 = TotPages) AND (TotEntrys Mod Count <> 0) then - PerPage := (TotEntrys Mod Count) + if (Page + 1 = TotPages) and (TotEntrys mod Count <> 0) then + PerPage := (TotEntrys mod Count) else PerPage := Count; - Text[Count+1].Text := Format(Theme.StatDetail.PageStr, [Page + 1, - TotPages, - PerPage, - TotEntrys]); + Text[Count+1].Text := Format(Theme.StatDetail.PageStr, + [Page + 1, TotPages, PerPage, TotEntrys]); //Show correct Title SetTitle; - end; end; @@ -250,7 +252,7 @@ end; procedure TScreenStatDetail.SetAnimationProgress(Progress: real); var I: Integer; begin - For I := 0 to high(Button) do + for I := 0 to High(Button) do Button[I].Texture.ScaleW := Progress; end; diff --git a/Game/Code/Screens/UScreenStatMain.pas b/Game/Code/Screens/UScreenStatMain.pas index 9d0349ed..9e581a82 100644 --- a/Game/Code/Screens/UScreenStatMain.pas +++ b/Game/Code/Screens/UScreenStatMain.pas @@ -81,7 +81,7 @@ begin else //One of the Stats Buttons Pressed begin AudioPlayback.PlaySound(SoundLib.Back); - ScreenStatDetail.Typ := Interaction; + ScreenStatDetail.Typ := TStatType(Interaction); FadeTo(@ScreenStatDetail); end; end; @@ -213,12 +213,12 @@ begin %3:d Count of Songs with Video (A3) %4:s Name of the most popular Song} A1 := Songs.SongList.Count; - A2 := Database.GetTotalEntrys(2); + A2 := Database.GetTotalEntrys(stMostSungSong); A3 := SongswithVid; SetLength(Result1, 1); - Database.GetStats(Result1, 2, 1, 0, False); + Database.GetStats(Result1, stMostSungSong, 1, 0, False); A4 := Result1[0].Artist; A5 := Result1[0].Title; @@ -237,13 +237,13 @@ begin %2:d Best Players Score %3:s Best Score Player (Result2) %4:d Best Score} - A1 := Database.GetTotalEntrys(1); + A1 := Database.GetTotalEntrys(stBestSingers); SetLength(Result1, 1); - Database.GetStats(Result1, 1, 1, 0, False); + Database.GetStats(Result1, stBestSingers, 1, 0, False); SetLength(Result2, 1); - Database.GetStats(Result2, 0, 1, 0, False); + Database.GetStats(Result2, stBestScores, 1, 0, False); try Overview := Overview + '\n \n' + Format(Formatstr, [A1, Result1[0].Player, Result1[0].AverageScore, Result2[0].Singer, Result2[0].Score]); |