diff options
Diffstat (limited to 'src/base')
-rw-r--r-- | src/base/UConfig.pas | 18 | ||||
-rw-r--r-- | src/base/UDraw.pas | 119 | ||||
-rw-r--r-- | src/base/UGraphic.pas | 13 | ||||
-rw-r--r-- | src/base/UMain.pas | 43 | ||||
-rw-r--r-- | src/base/UParty.pas | 1021 | ||||
-rw-r--r-- | src/base/USingScores.pas | 57 | ||||
-rw-r--r-- | src/base/UThemes.pas | 36 |
7 files changed, 974 insertions, 333 deletions
diff --git a/src/base/UConfig.pas b/src/base/UConfig.pas index f6dc69a5..a24242e8 100644 --- a/src/base/UConfig.pas +++ b/src/base/UConfig.pas @@ -58,7 +58,7 @@ unit UConfig; // not possible to use the version-numbers in this uses-clause. // Example: // interface -// uses +// uses // versions, // include this file // {$IF USE_UNIT_XYZ}xyz;{$IFEND} // Error: USE_UNIT_XYZ not defined // const @@ -68,13 +68,13 @@ unit UConfig; // // Even if this file was an include-file no constants could be declared // before the interface's uses clause. -// In FPC macros {$DEFINE VER:= 3} could be used to declare the version-numbers +// In FPC macros {$DEFINE VER:= 3} could be used to declare the version-numbers // but this is incompatible to Delphi. In addition macros do not allow expand -// arithmetic expressions. Although you can define +// arithmetic expressions. Although you can define // {$DEFINE FPC_VER:= FPC_VERSION*1000000+FPC_RELEASE*1000+FPC_PATCH} // the following check would fail: // {$IF FPC_VERSION_INT >= 002002000} -// would fail because FPC_VERSION_INT is interpreted as a string. +// would fail because FPC_VERSION_INT is interpreted as a string. // // PLEASE consider this if you use version numbers in $IF compiler- // directives. Otherwise you might break portability. @@ -88,7 +88,7 @@ interface {$ENDIF} {$I switches.inc} - + uses SysUtils; @@ -151,7 +151,7 @@ const FPC_RELEASE = 0; FPC_PATCH = 0; {$ENDIF} - + FPC_VERSION_INT = (FPC_VERSION * VERSION_MAJOR) + (FPC_RELEASE * VERSION_MINOR) + (FPC_PATCH * VERSION_RELEASE); @@ -185,13 +185,13 @@ const {$ENDIF} - {$IFDEF HaveProjectM} + {$IFDEF HaveProjectM} PROJECTM_VERSION = (PROJECTM_VERSION_MAJOR * VERSION_MAJOR) + (PROJECTM_VERSION_MINOR * VERSION_MINOR) + (PROJECTM_VERSION_RELEASE * VERSION_RELEASE); {$ENDIF} - {$IFDEF HavePortaudio} + {$IFDEF HavePortaudio} PORTAUDIO_VERSION = (PORTAUDIO_VERSION_MAJOR * VERSION_MAJOR) + (PORTAUDIO_VERSION_MINOR * VERSION_MINOR) + (PORTAUDIO_VERSION_RELEASE * VERSION_RELEASE); @@ -229,4 +229,4 @@ begin ' Build'; end; -end. +end.
\ No newline at end of file diff --git a/src/base/UDraw.pas b/src/base/UDraw.pas index 1783986f..47863f62 100644 --- a/src/base/UDraw.pas +++ b/src/base/UDraw.pas @@ -258,19 +258,21 @@ begin // So we exploit this behavior a bit - we give NrLines the playernumber, keep it in playernumber - and then we set NrLines to zero // This could also come quite in handy when we do the duet mode, cause just the notes for the player that has to sing should be drawn then // BUT this is not implemented yet, all notes are drawn! :D + if (ScreenSing.settings.NotesVisible and (1 shl NrLines) <> 0) then + begin - PlayerNumber := NrLines + 1; // Player 1 is 0 - NrLines := 0; + PlayerNumber := NrLines + 1; // Player 1 is 0 + NrLines := 0; -// exploit done + // exploit done - glColor3f(1, 1, 1); - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glColor3f(1, 1, 1); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - lTmpA := (Right-Left); - lTmpB := (Lines[NrLines].Line[Lines[NrLines].Current].End_ - Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start); + lTmpA := (Right-Left); + lTmpB := (Lines[NrLines].Line[Lines[NrLines].Current].End_ - Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start); if ( lTmpA > 0 ) and ( lTmpB > 0 ) then TempR := lTmpA / lTmpB @@ -285,16 +287,17 @@ begin begin if NoteType <> ntFreestyle then begin - if Ini.EffectSing = 0 then - // If Golden note Effect of then Change not Color - begin - case NoteType of - ntNormal: glColor4f(1, 1, 1, 1); // We set alpha to 1, cause we can control the transparency through the png itself - ntGolden: glColor4f(1, 1, 0.3, 1); // no stars, paint yellow -> glColor4f(1, 1, 0.3, 0.85); - we could + + if Ini.EffectSing = 0 then + // If Golden note Effect of then Change not Color + begin + case NoteType of + ntNormal: glColor4f(1, 1, 1, 1); // We set alpha to 1, cause we can control the transparency through the png itself + ntGolden: glColor4f(1, 1, 0.3, 1); // no stars, paint yellow -> glColor4f(1, 1, 0.3, 0.85); - we could end; // case - end //Else all Notes same Color - else - glColor4f(1, 1, 1, 1); // We set alpha to 1, cause we can control the transparency through the png itself + end //Else all Notes same Color + else + glColor4f(1, 1, 1, 1); // We set alpha to 1, cause we can control the transparency through the png itself // left part Rec.Left := (Start-Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start) * TempR + Left + 0.5 + 10*ScreenX; @@ -309,35 +312,35 @@ begin glTexCoord2f(1, 0); glVertex2f(Rec.Right, Rec.Top); glEnd; - //We keep the postion of the top left corner b4 it's overwritten + //We keep the postion of the top left corner b4 it's overwritten GoldenStarPos := Rec.Left; - //done + //done - // middle part - Rec.Left := Rec.Right; - Rec.Right := (Start+Length-Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start) * TempR + Left - NotesW - 0.5 + 10*ScreenX; + // middle part + Rec.Left := Rec.Right; + Rec.Right := (Start+Length-Lines[NrLines].Line[Lines[NrLines].Current].Note[0].Start) * TempR + Left - NotesW - 0.5 + 10*ScreenX; - glBindTexture(GL_TEXTURE_2D, Tex_plain_Mid[PlayerNumber].TexNum); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); - glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); - glTexCoord2f(round((Rec.Right-Rec.Left)/32), 1); glVertex2f(Rec.Right, Rec.Bottom); - glTexCoord2f(round((Rec.Right-Rec.Left)/32), 0); glVertex2f(Rec.Right, Rec.Top); - glEnd; + glBindTexture(GL_TEXTURE_2D, Tex_plain_Mid[PlayerNumber].TexNum); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); + glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); + glTexCoord2f(round((Rec.Right-Rec.Left)/32), 1); glVertex2f(Rec.Right, Rec.Bottom); + glTexCoord2f(round((Rec.Right-Rec.Left)/32), 0); glVertex2f(Rec.Right, Rec.Top); + glEnd; // right part Rec.Left := Rec.Right; Rec.Right := Rec.Right + NotesW; - glBindTexture(GL_TEXTURE_2D, Tex_plain_Right[PlayerNumber].TexNum); - glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); - glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); - glTexCoord2f(1, 1); glVertex2f(Rec.Right, Rec.Bottom); - glTexCoord2f(1, 0); glVertex2f(Rec.Right, Rec.Top); - glEnd; + glBindTexture(GL_TEXTURE_2D, Tex_plain_Right[PlayerNumber].TexNum); + glBegin(GL_QUADS); + glTexCoord2f(0, 0); glVertex2f(Rec.Left, Rec.Top); + glTexCoord2f(0, 1); glVertex2f(Rec.Left, Rec.Bottom); + glTexCoord2f(1, 1); glVertex2f(Rec.Right, Rec.Bottom); + glTexCoord2f(1, 0); glVertex2f(Rec.Right, Rec.Top); + glEnd; // Golden Star Patch if (NoteType = ntGolden) and (Ini.EffectSing=1) then @@ -345,13 +348,14 @@ begin GoldenRec.SaveGoldenStarsRec(GoldenStarPos, Rec.Top, Rec.Right, Rec.Bottom); end; - end; // if not FreeStyle - end; // with - end; // for - end; // with + end; // if not FreeStyle + end; // with + end; // for + end; // with - glDisable(GL_BLEND); - glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + end; end; // draw sung notes @@ -481,7 +485,7 @@ var W, H: real; lTmpA, lTmpB: real; begin - if (Player[PlayerIndex].ScoreTotalInt >= 0) then + if (ScreenSing.settings.NotesVisible and (1 shl PlayerIndex) <> 0) then begin glColor4f(1, 1, 1, sqrt((1+sin( AudioPlayback.Position * 3))/4)/ 2 + 0.5 ); glEnable(GL_TEXTURE_2D); @@ -683,20 +687,25 @@ begin // draw note-lines - if (PlayersPlay = 1) and (Ini.NoteLines = 1) then + // to-do : needs fix when party mode works w/ 2 screens + if (PlayersPlay = 1) and (Ini.NoteLines = 1) and (ScreenSing.settings.NotesVisible and (1) <> 0) then SingDrawNoteLines(NR.Left + 10*ScreenX, Skin_P2_NotesB - 105, NR.Right + 10*ScreenX, 15); if ((PlayersPlay = 2) or (PlayersPlay = 4)) and (Ini.NoteLines = 1) then begin - SingDrawNoteLines(NR.Left + 10*ScreenX, Skin_P1_NotesB - 105, NR.Right + 10*ScreenX, 15); - SingDrawNoteLines(NR.Left + 10*ScreenX, Skin_P2_NotesB - 105, NR.Right + 10*ScreenX, 15); + if (ScreenSing.settings.NotesVisible and (1 shl 0) <> 0) then + SingDrawNoteLines(Nr.Left + 10*ScreenX, Skin_P1_NotesB - 105, Nr.Right + 10*ScreenX, 15); + if (ScreenSing.settings.NotesVisible and (1 shl 1) <> 0) then + SingDrawNoteLines(Nr.Left + 10*ScreenX, Skin_P2_NotesB - 105, Nr.Right + 10*ScreenX, 15); end; - if ((PlayersPlay = 3) or (PlayersPlay = 6)) and (Ini.NoteLines = 1) then - begin - SingDrawNoteLines(NR.Left + 10*ScreenX, 120, NR.Right + 10*ScreenX, 12); - SingDrawNoteLines(NR.Left + 10*ScreenX, 245, NR.Right + 10*ScreenX, 12); - SingDrawNoteLines(NR.Left + 10*ScreenX, 370, NR.Right + 10*ScreenX, 12); + if ((PlayersPlay = 3) or (PlayersPlay = 6)) and (Ini.NoteLines = 1) then begin + if (ScreenSing.settings.NotesVisible and (1 shl 0) <> 0) then + SingDrawNoteLines(Nr.Left + 10*ScreenX, 120, Nr.Right + 10*ScreenX, 12); + if (ScreenSing.settings.NotesVisible and (1 shl 1) <> 0) then + SingDrawNoteLines(Nr.Left + 10*ScreenX, 245, Nr.Right + 10*ScreenX, 12); + if (ScreenSing.settings.NotesVisible and (1 shl 2) <> 0) then + SingDrawNoteLines(Nr.Left + 10*ScreenX, 370, Nr.Right + 10*ScreenX, 12); end; // draw Lyrics @@ -937,7 +946,7 @@ begin end; // Draw Lyrics - ScreenSingModi.Lyrics.Draw(LyricsState.MidBeat); + //ScreenSingModi.Lyrics.Draw(LyricsState.MidBeat); // TODO: Lyrics helper // oscilloscope | the thing that moves when you yell into your mic (imho) diff --git a/src/base/UGraphic.pas b/src/base/UGraphic.pas index b0e5a7d8..f658c800 100644 --- a/src/base/UGraphic.pas +++ b/src/base/UGraphic.pas @@ -77,6 +77,7 @@ uses UScreenPartyOptions, UScreenPartyWin, UScreenPartyPlayer, + UScreenPartyRounds, {Stats Screens} UScreenStatMain, UScreenStatDetail, @@ -133,12 +134,13 @@ var ScreenSongJumpto: TScreenSongJumpto; //Party Screens - ScreenSingModi: TScreenSingModi; + //ScreenSingModi: TScreenSingModi; ScreenPartyNewRound: TScreenPartyNewRound; ScreenPartyScore: TScreenPartyScore; ScreenPartyWin: TScreenPartyWin; ScreenPartyOptions: TScreenPartyOptions; ScreenPartyPlayer: TScreenPartyPlayer; + ScreenPartyRounds: TScreenPartyRounds; //StatsScreens ScreenStatMain: TScreenStatMain; @@ -747,8 +749,8 @@ begin // Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen Edit Header', 3); Log.BenchmarkStart(3); ScreenOpen := TScreenOpen.Create; Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen Open', 3); Log.BenchmarkStart(3); - ScreenSingModi := TScreenSingModi.Create; - Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen Sing with Modi support', 3); Log.BenchmarkStart(3); + //ScreenSingModi := TScreenSingModi.Create; + //Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen Sing with Modi support', 3); Log.BenchmarkStart(3); ScreenSongMenu := TScreenSongMenu.Create; Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen SongMenu', 3); Log.BenchmarkStart(3); ScreenSongJumpto := TScreenSongJumpto.Create; @@ -769,6 +771,8 @@ begin Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen PartyOptions', 3); Log.BenchmarkStart(3); ScreenPartyPlayer := TScreenPartyPlayer.Create; Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen PartyPlayer', 3); Log.BenchmarkStart(3); + ScreenPartyRounds := TScreenPartyRounds.Create; + Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen PartyRounds', 3); Log.BenchmarkStart(3); ScreenStatMain := TScreenStatMain.Create; Log.BenchmarkEnd(3); Log.LogBenchmark('====> Screen Stat Main', 3); Log.BenchmarkStart(3); ScreenStatDetail := TScreenStatDetail.Create; @@ -805,7 +809,7 @@ begin ScreenEdit.Destroy; ScreenEditConvert.Destroy; ScreenOpen.Destroy; - ScreenSingModi.Destroy; + //ScreenSingModi.Destroy; ScreenSongMenu.Destroy; ScreenSongJumpto.Destroy; ScreenPopupCheck.Destroy; @@ -816,6 +820,7 @@ begin ScreenPartyWin.Destroy; ScreenPartyOptions.Destroy; ScreenPartyPlayer.Destroy; + ScreenPartyRounds.Destroy; ScreenStatMain.Destroy; ScreenStatDetail.Destroy; end; diff --git a/src/base/UMain.pas b/src/base/UMain.pas index d5e0ccb3..7b082c57 100644 --- a/src/base/UMain.pas +++ b/src/base/UMain.pas @@ -35,6 +35,7 @@ interface uses SysUtils, + SDL; var @@ -89,6 +90,14 @@ uses USongs, UThemes, UParty, + ULuaCore, + UHookableEvent, + ULuaGl, + ULuaLog, + ULuaTexture, + ULuaTextGL, + ULuaParty, + ULuaScreenSing, UTime; procedure Main; @@ -124,6 +133,10 @@ begin SDL_Init(SDL_INIT_VIDEO or SDL_INIT_TIMER); SDL_EnableUnicode(1); + // create luacore first so other classes can register their events + LuaCore := TLuaCore.Create; + + USTime := TTime.Create; VideoBGTimer := TRelativeTimer.Create; @@ -227,13 +240,6 @@ begin Log.BenchmarkEnd(1); Log.LogBenchmark('Loading PluginManager', 1); - // Party Mode Manager - Log.BenchmarkStart(1); - Log.LogStatus('PartySession Manager', 'Initialization'); - PartySession := TPartySession.Create; //Load PartySession - Log.BenchmarkEnd(1); - Log.LogBenchmark('Loading PartySession Manager', 1); - // Graphics Log.BenchmarkStart(1); Log.LogStatus('Initialize 3D', 'Initialization'); @@ -278,6 +284,29 @@ begin Log.LogBenchmark('Initializing Joystick', 1); end; + // Lua + Log.BenchmarkStart(1); + Party := TPartyGame.Create; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Initializing Party Manager', 1); + + Log.BenchmarkStart(1); + LuaCore.RegisterModule('Log', ULuaLog_Lib_f); + LuaCore.RegisterModule('Gl', ULuaGl_Lib_f); + LuaCore.RegisterModule('TextGl', ULuaTextGl_Lib_f); + LuaCore.RegisterModule('Party', ULuaParty_Lib_f); + LuaCore.RegisterModule('ScreenSing', ULuaScreenSing_Lib_f); + + Log.BenchmarkEnd(1); + Log.LogBenchmark('Initializing LuaCore', 1); + + Log.BenchmarkStart(1); + LuaCore.LoadPlugins; + Log.BenchmarkEnd(1); + Log.LogBenchmark('Loading Lua Plugins', 1); + + LuaCore.DumpPlugins; + Log.BenchmarkEnd(0); Log.LogBenchmark('Loading Time', 0); diff --git a/src/base/UParty.pas b/src/base/UParty.pas index 52eb5a05..94580241 100644 --- a/src/base/UParty.pas +++ b/src/base/UParty.pas @@ -34,355 +34,938 @@ interface {$I switches.inc} uses - ModiSDK; + ULua; type - TRoundInfo = record - Plugin: word; - Winner: byte; + { array holds ids of modes or Party_Round_Random + its length defines the number of rounds + it is used as argument for TPartyGame.StartParty } + ARounds = array of integer; + + { element of APartyTeamRanking returned by TPartyGame.GetTeamRanking + and parameter for TPartyGame.SetWinner } + TParty_TeamRanking = record + Team: Integer; //< id of team + Rank: Integer; //< 1 to Length(Teams) e.g. 1 is for placed first end; + AParty_TeamRanking = array of TParty_TeamRanking; //< returned by TPartyGame.GetTeamRanking - TeamOrderEntry = record - TeamNum: byte; - Score: byte; + TParty_RoundList = record + Index: integer; + Name: UTF8String; end; + AParty_ModeList = array of TParty_RoundList; + + { record used by TPartyGame to store round specific data } + TParty_Round = record + Mode: Integer; + AlreadyPlayed: Boolean; //< true if round was already played + Ranking: AParty_TeamRanking; + RankingSet: Boolean; //< true if Self.Ranking is already set + end; + + TParty_ModeInfo = record + Name: String; // name of this mode + Parent: Integer; // Id of owning plugin + + CanNonParty: Boolean; //< is playable when not in party mode + CanParty: Boolean; //< is playable in party mode + + // one bit in the following settings stands for + // a player or team count + // PlayerCount = 2 or 4 indicates that the mode is playable with 2 and 3 players per team + // TeamCount = 1 or 2 or 4 or 8 or 16 or 32 indicates that the mode is playable with 1 to 6 teams + PlayerCount: Integer; //< playable with one, two, three etc. players per team + TeamCount: Integer; //< playable with one, two, three etc. different teams + + + Functions: record // lua functions that will be called at specific events + BeforeSongSelect: String; // default actions are executed if functions = nil + AfterSongSelect: String; + + BeforeSing: String; + OnSing: String; + AfterSing: String; + end; + end; + + { used by TPartyGame to store player specific data } + TParty_PlayerInfo = record + Name: String; //< Playername + TimesPlayed: Integer; //< How often this Player has Sung + end; + + { used by TPartyGame to store team specific data } + TParty_TeamInfo = record + Name: String; //< name of the Team + Score: Word; //< current score + JokersLeft: Integer; //< jokers this team has left - TeamOrderArray = array[0..5] of byte; + NextPlayer: Integer; //Id of the player that plays the next (the current) song - TPartyPlugin = record - ID: byte; - TimesPlayed: byte; + Players: array of TParty_PlayerInfo; end; - TPartySession = class + TPartyGame = class private - function GetRandomPlayer(Team: byte): byte; - function GetRandomPlugin(Plugins: array of TPartyPlugin): byte; - function IsWinner(Player, Winner: byte): boolean; + bPartyGame: boolean; //< are we playing party or standard mode + CurRound: Integer; //< indicates which of the elements of Rounds is played next (at the moment) + + bPartyStarted: Boolean; + + TimesPlayed: array of Integer; //< times every mode was played in current party game (for random mode calculation) + procedure GenScores; + function GetRandomMode: integer; + function GetRandomPlayer(Team: integer): integer; + + { returns true if a mode is playable with current playerconfig } + function ModePlayable(I: integer): boolean; + + function CallLua(Parent: Integer; Func: String):Boolean; + + procedure SetRankingByScore; public - Teams: TTeamInfo; - Rounds: array of TRoundInfo; - CurRound: byte; + //Teams: TTeamInfo; + Rounds: array of TParty_Round; //< holds info which modes are played in this party game (if started) + Teams: array of TParty_TeamInfo; //< holds info of teams playing in current round (private for easy manipulation of lua functions) + + Modes: array of TParty_ModeInfo; //< holds info of registred party modes + + property CurrentRound: Integer read CurRound; constructor Create; - procedure StartNewParty(NumRounds: byte); - procedure StartRound; - procedure EndRound; - function GetTeamOrder: TeamOrderArray; - function GetWinnerString(Round: byte): UTF8String; + { set the attributes of Info to default values } + procedure DefaultModeInfo(var Info: TParty_ModeInfo); + + { registers a new mode, returns true on success + (mode name does not already exist) } + function RegisterMode(Info: TParty_ModeInfo): Boolean; + + { returns true if modes are available for + players and teams that are currently set + up. if there are no teams set up it returns + if there are any party modes available } + function ModesAvailable: Boolean; + + { returns an array with the name of all available modes (that + are playable with current player configuration } + function GetAvailableModes: AParty_ModeList; + + { clears all party specific data previously stored } + procedure Clear; + + { adds a team to the team array, returning its id + can only be called when game is not already started } + function AddTeam(Name: String): Integer; + + { adds a player to the player array, returning its id + can only be called when game is not already started } + function AddPlayer(Team: Integer; Name: String): Integer; + + { starts a new PartyGame, returns true on success + before a call of this function teams and players + has to be added by AddTeam and AddPlayer } + + function StartGame(Rounds: ARounds): Boolean; + + { sets the winner(s) of current round + returns true on success } + function SetRanking(Ranking: AParty_TeamRanking): Boolean; + + { increases round counter by 1 and clears all round specific information; + returns the number of the current round or -1 if last round has already + been played } + function NextRound: integer; + + { indicates that current round has already been played } + procedure RoundPlayed; + + { true if in a Party Game (not in standard mode) } + property PartyGame: Boolean read BPartyGame; + + + { returns true if last round was already played } + function GameFinished: Boolean; + + { call plugins defined function and/or default procedure + only default procedure is called when no function is defined by plugin + if plugins function returns true then default is called after plugins + function was executed} + procedure CallBeforeSongSelect; + procedure CallAfterSongSelect; + procedure CallBeforeSing; + procedure CallOnSing; + procedure CallAfterSing; + + { returns an array[1..6] of TParty_TeamRanking. + the index stands for the placing, + team is the team number (in the team array) + rank is correct rank if some teams have the + same score. + } + function GetTeamRanking: AParty_TeamRanking; + + { returns a string like "Team 1 (and Team 2) win" } + function GetWinnerString(Round: integer): UTF8String; + + destructor Destroy; end; +const + { minimal amount of teams for party mode } + Party_Teams_Min = 2; + + { maximal amount of teams for party mode } + Party_Teams_Max = 3; + + { minimal amount of players for party mode } + Party_Players_Min = 1; + + { maximal amount of players for party mode } + Party_Players_Max = 4; + + { amount of jokers each team gets at the beginning of the game } + Party_Count_Jokers = 5; + + { to indicate that element (mode) should set randomly in ARounds array } + Party_Round_Random = -1; + + { values for TParty_TeamRanking.Rank } + PR_First = 1; + PR_Second = 2; + PR_Third = 3; + + StandardModus = 0; //Modus Id that will be played in non-party mode + var - PartySession: TPartySession; + Party: TPartyGame; implementation uses - UDLLManager, UGraphic, - UNote, ULanguage, - ULog; + ULog, + ULuaCore, + UDisplay, + USong, + UNote, + SysUtils; -constructor TPartySession.Create; +//------------- +// Just the constructor +//------------- +constructor TPartyGame.Create; begin inherited; + + Clear; end; -//---------- -// Returns a number of a random plugin -//---------- -function TPartySession.GetRandomPlugin(Plugins: array of TPartyPlugin): byte; +destructor TPartyGame.Destroy; +begin + inherited; +end; + +{ clears all party specific data previously stored } +procedure TPartyGame.Clear; + var + I: Integer; +begin + bPartyGame := false; // no party game + CurRound := low(integer); + + bPartyStarted := false; //game not startet + + SetLength(Teams, 0); //remove team info + SetLength(Rounds, 0); //remove round info + + // clear times played + for I := 0 to High(TimesPlayed) do + TimesPlayed[I] := 0; +end; + +{ private: some intelligent randomnes for plugins } +function TPartyGame.GetRandomMode: integer; +var + LowestTP: integer; + NumPwithLTP: integer; + I: integer; + R: integer; +begin + Result := 0; //If there are no matching modes, play first modus + LowestTP := high(Integer); + NumPwithLTP := 0; + + // search for the plugins less played yet + for I := 0 to high(Modes) do + begin + if (ModePlayable(I)) then + begin + if (TimesPlayed[I] < lowestTP) then + begin + lowestTP := TimesPlayed[I]; + NumPwithLTP := 1; + end + else if (TimesPlayed[I] = lowestTP) then + begin + Inc(NumPwithLTP); + end; + end; + end; + + // create random number + R := Random(NumPwithLTP); + + // select the random mode from the modes with less timesplayed + for I := 0 to high(Modes) do + begin + if (TimesPlayed[I] = lowestTP) and (ModePlayable(I)) then + begin + //Plugin found + if (R = 0) then + begin + Result := I; + Inc(TimesPlayed[I]); + Break; + end; + + Dec(R); + end; + end; +end; + +{ private: GetRandomPlayer - returns a random player + that does not play to often ;) } +function TPartyGame.GetRandomPlayer(Team: integer): integer; var - LowestTP: byte; - NumPwithLTP: word; - I: integer; - R: word; + I, R: integer; + lowestTP: Integer; + NumPwithLTP: Integer; begin - LowestTP := high(byte); + LowestTP := high(Integer); NumPwithLTP := 0; + Result := 0; - //Search for Plugins not often played yet - for I := 0 to high(Plugins) do + // search for players that have less played yet + for I := 0 to High(Teams[Team].Players) do begin - if (Plugins[I].TimesPlayed < lowestTP) then + if (Teams[Team].Players[I].TimesPlayed < lowestTP) then begin - lowestTP := Plugins[I].TimesPlayed; + lowestTP := Teams[Team].Players[I].TimesPlayed; NumPwithLTP := 1; end - else if (Plugins[I].TimesPlayed = lowestTP) then + else if (Teams[Team].Players[I].TimesPlayed = lowestTP) then begin Inc(NumPwithLTP); end; end; - //Create random no + // create random number R := Random(NumPwithLTP); - //Search for random plugin - for I := 0 to high(Plugins) do + // search for selected random player + for I := 0 to High(Teams[Team].Players) do begin - if Plugins[I].TimesPlayed = LowestTP then + if Teams[Team].Players[I].TimesPlayed = lowestTP then begin - //Plugin found if (R = 0) then - begin - Result := Plugins[I].ID; - Inc(Plugins[I].TimesPlayed); + begin // found selected player + Result := I; Break; end; + Dec(R); end; end; end; //---------- -//StartNewParty - Reset and prepares for new party +//GenScores - inc scores for cur. round //---------- -procedure TPartySession.StartNewParty(NumRounds: byte); +procedure TPartyGame.GenScores; var - Plugins: array of TPartyPlugin; - TeamMode: boolean; - Len: integer; - I, J: integer; + I: Integer; +begin + if (Length(Teams) = 2) then + begin // score generation for 2 teams, winner gets 1 point + for I := 0 to High(Rounds[CurRound].Ranking) do + if (Rounds[CurRound].Ranking[I].Rank = PR_First) then + Inc(Teams[Rounds[CurRound].Ranking[I].Team].Score); + end + else if (Length(Teams) = 3) then + begin // score generation for 3 teams, + // winner gets 3 points 2nd gets 1 point + for I := 0 to High(Rounds[CurRound].Ranking) do + if (Rounds[CurRound].Ranking[I].Rank = PR_First) then + Inc(Teams[Rounds[CurRound].Ranking[I].Team].Score, 3) + else if (Rounds[CurRound].Ranking[I].Rank = PR_Second) then + Inc(Teams[Rounds[CurRound].Ranking[I].Team].Score); + end +end; + +{ set the attributes of Info to default values } +procedure TPartyGame.DefaultModeInfo(var Info: TParty_ModeInfo); begin - //Set current round to 1 - CurRound := 255; + Info.Name := 'undefined'; + Info.Parent := -1; //< not loaded by plugin (e.g. Duell) + Info.CanNonParty := false; + Info.CanParty := false; + Info.PlayerCount := High(Integer); //< no restrictions either on player count + Info.TeamCount := High(Integer); //< nor on team count + Info.Functions.BeforeSongSelect := ''; //< use default functions + Info.Functions.AfterSongSelect := ''; + Info.Functions.BeforeSing := ''; + Info.Functions.OnSing := ''; + Info.Functions.AfterSing := ''; +end; - PlayersPlay := Teams.NumTeams; +{ registers a new mode, returns true on success + (mode name does not already exist) } +function TPartyGame.RegisterMode(Info: TParty_ModeInfo): Boolean; + var + Len: integer; + LowerName: String; + I: integer; +begin + Result := false; - //Get team-mode and set joker, also set TimesPlayed - TeamMode := true; - for I := 0 to Teams.NumTeams - 1 do + if (Info.Name <> 'undefined') then begin - if Teams.Teaminfo[I].NumPlayers < 2 then + // search for a plugin w/ same name + LowerName := lowercase(Info.Name); // case sensitive search + for I := 0 to high(Modes) do + if (LowerName = lowercase(Modes[I].Name)) then + exit; //< no success (name already exist) + + // add new mode to array and append and clear a new TimesPlayed element + Len := Length(Modes); + SetLength(Modes, Len + 1); + SetLength(TimesPlayed, Len + 1); + + Modes[Len] := Info; + TimesPlayed[Len] := 0; + + Result := True; + end; +end; + +{ returns true if a mode is playable with current playerconfig } +function TPartyGame.ModePlayable(I: integer): boolean; + var + J: integer; +begin + if (Length(Teams) = 0) then + Result := true + else + begin + if (Modes[I].TeamCount and (1 shl (Length(Teams) - 1)) <> 0) then begin - TeamMode := false; + Result := true; + + for J := 0 to High(Teams) do + Result := Result and (Modes[I].PlayerCount and (1 shl (Length(Teams[J].Players) - 1)) <> 0); + end + else + Result := false; + end; +end; + +{ returns true if modes are available for + players and teams that are currently set + up. if there are no teams set up it returns + if there are any party modes available } +function TPartyGame.ModesAvailable: Boolean; + var + I: integer; + CountTeams: integer; +begin + CountTeams := Length(Teams); + if CountTeams = 0 then + begin + Result := (Length(Modes) > 0); + end + else + begin + Result := false; + for I := 0 to High(Modes) do + begin + Result := ModePlayable(I); + + if Result then + Exit; end; - //Set player attributes - for J := 0 to Teams.TeamInfo[I].NumPlayers-1 do + end; +end; + +{ returns an array with the name of all available modes (that + are playable with current player configuration } +function TPartyGame.GetAvailableModes: AParty_ModeList; + var + I: integer; + Len: integer; +begin + Len := 0; + SetLength(Result, Len + 1); + Result[Len].Index := Party_Round_Random; + Result[Len].Name := Language.Translate('MODE_RANDOM_NAME'); + + for I := 0 to High(Modes) do + if (ModePlayable(I)) then begin - Teams.TeamInfo[I].Playerinfo[J].TimesPlayed := 0; + Inc(Len); + SetLength(Result, Len + 1); + Result[Len].Index := I; + Result[Len].Name := Language.Translate('MODE_' + Uppercase(Modes[I].Name) + '_NAME'); end; - Teams.Teaminfo[I].Joker := Round(NumRounds * 0.7); - Teams.Teaminfo[I].Score := 0; +end; + +{ adds a team to the team array, returning its id + can only be called when game is not already started } +function TPartyGame.AddTeam(Name: String): Integer; +begin + Result := -1; + if (not bPartyStarted) and (Length(Name) > 0) and (Length(Teams) < Party_Teams_Max) then + begin + Result := Length(Teams); + SetLength(Teams, Result + 1); + + Teams[Result].Name := Name; + Teams[Result].Score := 0; + Teams[Result].JokersLeft := Party_Count_Jokers; + Teams[Result].NextPlayer := -1; end; +end; + +{ adds a player to the player array, returning its id + can only be called when game is not already started } +function TPartyGame.AddPlayer(Team: Integer; Name: String): Integer; +begin + Result := -1; - //Fill plugin array - SetLength(Plugins, 0); - for I := 0 to high(DLLMan.Plugins) do + if (not bPartyStarted) and (Team >= 0) and (Team <= High(Teams)) and (Length(Teams[Team].Players) < Party_Players_Max) and (Length(Name) > 0) then begin - if TeamMode or (not DLLMan.Plugins[I].TeamModeOnly) then - begin - //Add only those plugins playable with current PlayerConfiguration - Len := Length(Plugins); - SetLength(Plugins, Len + 1); - Plugins[Len].ID := I; - Plugins[Len].TimesPlayed := 0; - end; + // append element to players array + Result := Length(Teams[Team].Players); + SetLength(Teams[Team].Players, Result + 1); + + // fill w/ data + Teams[Team].Players[Result].Name := Name; + Teams[Team].Players[Result].TimesPlayed := 0; end; +end; + +{ starts a new PartyGame, returns true on success + before a call of this function teams and players + has to be added by AddTeam and AddPlayer } +function TPartyGame.StartGame(Rounds: ARounds): Boolean; + var + I: integer; +begin + Result := false; - //Set rounds - if (Length(Plugins) >= 1) then + if (not bPartyStarted) and (Length(Rounds) > 0) and (Length(Teams) >= Party_Teams_Min) then begin - SetLength (Rounds, NumRounds); - for I := 0 to NumRounds - 1 do + // check teams for minimal player count + for I := 0 to High(Teams) do + if (Length(Teams[I].Players) < Party_Players_Min) then + exit; + + // create rounds array + SetLength(Self.Rounds, Length(Rounds)); + + for I := 0 to High(Rounds) do begin - PartySession.Rounds[I].Plugin := GetRandomPlugin(Plugins); - PartySession.Rounds[I].Winner := 255; + // copy round or select a random round + if (Rounds[I] <> Party_Round_Random) and (Rounds[I] >= 0) and (Rounds[I] <= High(Modes)) then + Self.Rounds[I].Mode := Rounds[I] + else + Self.Rounds[I].Mode := GetRandomMode; + + Self.Rounds[I].AlreadyPlayed := false; + Self.Rounds[I].RankingSet := false; + + SetLength(Self.Rounds[I].Ranking, 0); end; + + // get the party started!11 + bPartyStarted := true; + bPartyGame := true; + CurRound := low(integer); //< set not to -1 to indicate that party game is not finished + + // first round + NextRound; + + Result := True; + end; +end; + +{ sets the winner(s) of current round + returns true on success } +function TPartyGame.SetRanking(Ranking: AParty_TeamRanking): Boolean; + var + I, J: Integer; + TeamExists: Integer; + Len: Integer; + Temp: TParty_TeamRanking; +begin + if (bPartyStarted) and (CurRound >= 0) and (CurRound <= High(Rounds)) then + begin + Rounds[CurRound].Ranking := Ranking; + Result := true; + + // look for teams that don't exist + TeamExists := 0; + for I := 0 to High(Rounds[CurRound].Ranking) do + TeamExists := TeamExists or (1 shl (Rounds[CurRound].Ranking[I].Team-1)); + + // create teams that don't exist + Len := Length(Rounds[CurRound].Ranking); + for I := 0 to High(Teams) do + if (TeamExists and (1 shl I) = 0) then + begin + Inc(Len); + SetLength(Rounds[CurRound].Ranking, Len); + Rounds[CurRound].Ranking[Len-1].Team := I + 1; + Rounds[CurRound].Ranking[Len-1].Rank := Length(Teams); + end; + + // we may remove rankings from invalid teams here to + // but at the moment this is not necessary, because the + // functions this function is called from don't create + // invalid rankings + + // bubble sort rankings by team + J := High(Rounds[CurRound].Ranking); + repeat + for I := 0 to J - 1 do + if (Rounds[CurRound].Ranking[I].Team > Rounds[CurRound].Ranking[I+1].Team) then + begin + Temp := Rounds[CurRound].Ranking[I]; + Rounds[CurRound].Ranking[I] := Rounds[CurRound].Ranking[I+1]; + Rounds[CurRound].Ranking[I+1] := Temp; + end; + Dec(J); + until J <= 0; + + //set rounds RankingSet to true + Rounds[CurRound].RankingSet := true; end else - SetLength (Rounds, 0); + Result := false; end; -{** - * Returns a random player to play next round - *} -function TPartySession.GetRandomPlayer(Team: byte): byte; -var - I, R: integer; - LowestTP: byte; - NumPwithLTP: byte; +{ sets ranking of current round by score saved in players array } +procedure TPartyGame.SetRankingByScore; + var + I, J: Integer; + Rank: Integer; + Ranking: AParty_TeamRanking; + Scores: array of Integer; + TmpRanking: TParty_TeamRanking; + TmpScore: Integer; begin - LowestTP := high(byte); - NumPwithLTP := 0; - Result := 0; - - //Search for players that have not often played yet - for I := 0 to Teams.Teaminfo[Team].NumPlayers - 1 do + if (Length(Player) = Length(Teams)) then begin - if (Teams.Teaminfo[Team].Playerinfo[I].TimesPlayed < lowestTP) then + SetLength(Ranking, Length(Teams)); + SetLength(Scores, Length(Teams)); + + // fill ranking array + for I := 0 to High(Ranking) do begin - lowestTP := Teams.Teaminfo[Team].Playerinfo[I].TimesPlayed; - NumPwithLTP := 1; - end - else if (Teams.Teaminfo[Team].Playerinfo[I].TimesPlayed = lowestTP) then + Ranking[I].Team := I; + Ranking[I].Rank := 0; + Scores[I] := Player[I].ScoreTotalInt; + end; + + // bubble sort by score + J := High(Ranking); + repeat + for I := 0 to J - 1 do + if (Scores[I] < Scores[I+1]) then + begin + TmpRanking := Ranking[I]; + Ranking[I] := Ranking[I+1]; + Ranking[I+1] := TmpRanking; + + TmpScore := Scores[I]; + Scores[I] := Scores[I+1]; + Scores[I+1] := TmpScore; + end; + Dec(J); + until J <= 0; + + // set rank field + Rank := 1; //first rank has id 1 + for I := 0 to High(Ranking) do begin - Inc(NumPwithLTP); + Ranking[I].Rank := Rank; + + if (I < High(Ranking)) and (Scores[I] <> Scores[I+1]) then + Inc(Rank); // next rank if next team has different score end; + end + else + SetLength(Ranking, 0); + + SetRanking(Ranking); +end; + +{ increases round counter by 1 and clears all round specific information; + returns the number of the current round or -1 if last round has already + been played } +function TPartyGame.NextRound: integer; + var I: Integer; +begin + // some lines concerning the previous round + if (CurRound >= 0) then + begin + Rounds[CurRound].AlreadyPlayed := true; + + GenScores; end; - //Create random number - R := Random(NumPwithLTP); + // increase round counter + Inc(CurRound); + if (CurRound < -1) then // we start first round + CurRound := 0; - //Search for random player - for I := 0 to Teams.Teaminfo[Team].NumPlayers - 1 do + if (CurRound > High(Rounds)) then + CurRound := -1; //< last round played + + Result := CurRound; + + // some lines concerning the next round + if (CurRound >= 0) then begin - if Teams.Teaminfo[Team].Playerinfo[I].TimesPlayed = lowestTP then - begin - //Player found - if (R = 0) then - begin - Result := I; - Break; - end; - - Dec(R); - end; + // select player + for I := 0 to High(Teams) do + Teams[I].NextPlayer := GetRandomPlayer(I); end; end; -{** - * Prepares ScreenSingModi for next round and loads plugin - *} -procedure TPartySession.StartRound; -var - I: integer; +{ indicates that current round has already been played } +procedure TPartyGame.RoundPlayed; begin - if ((CurRound < high(Rounds)) or (CurRound = high(CurRound))) then + if (bPartyStarted) and (CurRound >= 0) and (CurRound <= High(Rounds)) then begin - // Increase Current Round but not beyond its limit - // CurRound is set to 255 to begin with! - // Ugly solution if you ask me. - if CurRound < high(CurRound) then - Inc(CurRound) - else - CurRound := 0; + // set rounds ranking by score if it was not set by plugin + if (not Rounds[CurRound].RankingSet) then + SetRankingByScore; + + Rounds[CurRound].AlreadyPlayed := True; + end; +end; + +{ returns true if last round was already played } +function TPartyGame.GameFinished: Boolean; +begin + Result := (bPartyStarted and (CurRound = -1)); +end; - Rounds[CurRound].Winner := 255; - DllMan.LoadPlugin(Rounds[CurRound].Plugin); +{ private: calls the specified function Func from lua plugin Parent + if both exist. + return true if default function should be called + (function or plugin does not exist, or function returns + true) } +function TPartyGame.CallLua(Parent: Integer; Func: String):Boolean; + var + P: TLuaPlugin; +begin + // call default function by default + Result := true; - //Select Players - for I := 0 to Teams.NumTeams - 1 do - Teams.Teaminfo[I].CurPlayer := GetRandomPlayer(I); + // check for core plugin and empty function name + if (Parent >= 0) and (Length(Func) > 0) then + begin + // get plugin that registred the mode + P := LuaCore.GetPluginById(Parent); - //Set ScreenSingModie Variables - ScreenSingModi.TeamInfo := Teams; + if (P <> nil) then + begin + if (P.CallFunctionByName(Func, 0, 1)) then + // check result + Result := (lua_toboolean(P.LuaState, 1)); + end; end; end; -//---------- -//EndRound - Get Winner from ScreenSingModi and Save Data to RoundArray -//---------- -procedure TPartySession.EndRound; -var - I: Integer; +{ call plugins defined function and/or default procedure + only default procedure is called when no function is defined by plugin + if plugins function returns true then default is called after plugins + function was executed} +procedure TPartyGame.CallBeforeSongSelect; begin - //Copy Winner - Rounds[CurRound].Winner := ScreenSingModi.Winner; - //Set Scores - GenScores; + if (CurRound >= 0) then + begin + // we set screen song to party mode + // plugin should not have to do this if it + // don't want default procedure to be executed + ScreenSong.Mode := smPartyMode; + + with Modes[Rounds[CurRound].Mode] do + if (CallLua(Parent, Functions.BeforeSongSelect)) then + begin // execute default function: + // display song select screen + Display.FadeTo(@ScreenSong); + end; + end; +end; - //Increase TimesPlayed 4 all Players - For I := 0 to Teams.NumTeams-1 do - Inc(Teams.Teaminfo[I].Playerinfo[Teams.Teaminfo[I].CurPlayer].TimesPlayed); +procedure TPartyGame.CallAfterSongSelect; +begin + if (CurRound >= 0) then + begin + with Modes[Rounds[CurRound].Mode] do + if (CallLua(Parent, Functions.AfterSongSelect)) then + begin // execute default function: + // display sing screen + ScreenSong.StartSong; + end; + end; end; -//---------- -//IsWinner - returns true if the player's bit is set in the winner byte -//---------- -function TPartySession.IsWinner(Player, Winner: byte): boolean; -var - Mask: byte; +procedure TPartyGame.CallBeforeSing; begin - Mask := 1 shl Player; - Result := (Winner and Mask) <> 0; + if (CurRound >= 0) then + begin + with Modes[Rounds[CurRound].Mode] do + if (CallLua(Parent, Functions.BeforeSing)) then + begin // execute default function: + + //nothing atm + { to-do : compartmentalize TSingScreen.OnShow into + functions for init of a specific part of + sing screen. + these functions should be called here before + sing screen is shown, or it should be called + by plugin if it wants to define a custom + singscreen start up. } + + //set correct playersplay + if (bPartyGame) then + PlayersPlay := Length(Teams); + end; + end; end; -//---------- -//GenScores - increase scores for current round -//---------- -procedure TPartySession.GenScores; -var - I: byte; +procedure TPartyGame.CallOnSing; begin - for I := 0 to Teams.NumTeams - 1 do + if (CurRound >= 0) then begin - if isWinner(I, Rounds[CurRound].Winner) then - Inc(Teams.Teaminfo[I].Score); + with Modes[Rounds[CurRound].Mode] do + if (CallLua(Parent, Functions.OnSing)) then + begin // execute default function: + + //nothing atm + end; end; end; -//---------- -//GetTeamOrder - returns the placement of each Team [First Position of Array is Teamnum of first placed Team, ...] -//---------- -function TPartySession.GetTeamOrder: TeamOrderArray; -var - I, J: integer; - ATeams: array [0..5] of TeamOrderEntry; - TempTeam: TeamOrderEntry; +procedure TPartyGame.CallAfterSing; begin - // TODO: PartyMode: Write this in another way, so that teams with the same score get the same place - //Fill Team array - for I := 0 to Teams.NumTeams - 1 do + if (CurRound >= 0) then + begin + with Modes[Rounds[CurRound].Mode] do + if (CallLua(Parent, Functions.AfterSing)) then + begin // execute default function: + + if (bPartyGame) then + // display party score screen + Display.FadeTo(@ScreenPartyScore) + else //display standard score screen + Display.FadeTo(@ScreenScore); + end; + end; +end; + +{ returns an array[1..6] of integer. the index stands for the placing, + value is the team number (in the team array) } +function TPartyGame.GetTeamRanking: AParty_TeamRanking; + var + I, J: Integer; + Temp: TParty_TeamRanking; + Rank: Integer; +begin + SetLength(Result, Length(Teams)); + + // fill ranking array + for I := 0 to High(Result) do begin - ATeams[I].Teamnum := I; - ATeams[I].Score := Teams.Teaminfo[I].Score; + Result[I].Team := I; + Result[I].Rank := 0; end; - //Sort teams - for J := 0 to Teams.NumTeams - 1 do - for I := 1 to Teams.NumTeams - 1 do - if ATeams[I].Score > ATeams[I-1].Score then + // bubble sort by score + J := High(Result); + repeat + for I := 0 to J - 1 do + if (Teams[Result[I].Team].Score < Teams[Result[I+1].Team].Score) then begin - TempTeam := ATeams[I-1]; - ATeams[I-1] := ATeams[I]; - ATeams[I] := TempTeam; + Temp := Result[I]; + Result[I] := Result[I+1]; + Result[I+1] := Temp; end; + Dec(J); + until J <= 0; - //Copy to Result - for I := 0 to Teams.NumTeams-1 do - Result[I] := ATeams[I].TeamNum; + // set rank field + Rank := 1; //first rank has id 1 + for I := 0 to High(Result) do + begin + Result[I].Rank := Rank; + + if (I < High(Result)) and (Teams[Result[I].Team].Score <> Teams[Result[I+1].Team].Score) then + Inc(Rank); // next rank if next team has different score + end; end; -//---------- -//GetWinnerString - Get string with WinnerTeam Name, when there is more than one Winner than Connect with and or , -//---------- -function TPartySession.GetWinnerString(Round: byte): UTF8String; +{ returns a string like "Team 1 (and Team 2) win" + if Round is in range from 0 to high(Rounds) then + result is name of winners of specified round. + if Round is -1 the result is name of winners of + the whole party game} +function TPartyGame.GetWinnerString(Round: integer): UTF8String; var Winners: array of UTF8String; - I: integer; + I: integer; + Ranking: AParty_TeamRanking; begin - Result := Language.Translate('PARTY_NOBODY'); + Result := ''; + Ranking := nil; - if (Round > High(Rounds)) then - exit; - - if (Rounds[Round].Winner = 0) then + if (Round >= 0) and (Round <= High(Rounds)) then begin - exit; - end; + if (not Rounds[Round].AlreadyPlayed) then + Result := Language.Translate('PARTY_NOTPLAYEDYET') + else + Ranking := Rounds[Round].Ranking; + end + else if (Round = -1) then + Ranking := GetTeamRanking; - if (Rounds[Round].Winner = 255) then - begin - Result := Language.Translate('PARTY_NOTPLAYEDYET'); - exit; - end; - SetLength(Winners, 0); - for I := 0 to Teams.NumTeams - 1 do + if (Ranking <> nil) then begin - if isWinner(I, Rounds[Round].Winner) then + SetLength(Winners, 0); + for I := 0 to High(Ranking) do begin - SetLength(Winners, Length(Winners) + 1); - Winners[high(Winners)] := Teams.TeamInfo[I].Name; + if (Ranking[I].Rank = PR_First) and (Ranking[I].Team >= 0) and (Ranking[I].Team <= High(Teams)) then + begin + SetLength(Winners, Length(Winners) + 1); + Winners[high(Winners)] := UTF8String(Teams[Ranking[I].Team].Name); + end; end; + + if (Length(Winners) > 0) then + Result := Language.Implode(Winners); end; - Result := Language.Implode(Winners); + + if (Length(Result) = 0) then + Result := Language.Translate('PARTY_NOBODY'); end; end. diff --git a/src/base/USingScores.pas b/src/base/USingScores.pas index f280900e..6fdfaeb6 100644 --- a/src/base/USingScores.pas +++ b/src/base/USingScores.pas @@ -129,7 +129,7 @@ type //----------- TSingScores = class private - Positions: aScorePosition; + aPositions: aScorePosition; aPlayers: aScorePlayer; oPositionCount: byte; oPlayerCount: byte; @@ -187,6 +187,7 @@ type property PositionCount: byte read oPositionCount; property PlayerCount: byte read oPlayerCount; property Players: aScorePlayer read aPlayers; + property Positions: aScorePosition read aPositions; // constructor just sets some standard settings constructor Create; @@ -286,7 +287,7 @@ procedure TSingScores.AddPosition(const pPosition: PScorePosition); begin if (PositionCount < MaxPositions) then begin - Positions[PositionCount] := pPosition^; + aPositions[PositionCount] := pPosition^; Inc(oPositionCount); end; end; @@ -606,7 +607,7 @@ var for I := 0 to PositionCount - 1 do begin - if ((Positions[I].PlayerCount and bPlayerCount) <> 0) then + if ((aPositions[I].PlayerCount and bPlayerCount) <> 0) then Inc(Result); end; end; @@ -620,7 +621,7 @@ var for I := 0 to PositionCount - 1 do begin - if ((Positions[I].PlayerCount and bPlayerCount) <> 0) then + if ((aPositions[I].PlayerCount and bPlayerCount) <> 0) then begin if (bPlayer = 0) then begin @@ -806,13 +807,13 @@ begin Progress := TimeDiff / Settings.Phase1Time; - W := Positions[PIndex].PUW * Sin(Progress/2*Pi); - H := Positions[PIndex].PUH * Sin(Progress/2*Pi); + W := aPositions[PIndex].PUW * Sin(Progress/2*Pi); + H := aPositions[PIndex].PUH * Sin(Progress/2*Pi); - X := Positions[PIndex].PUStartX + (Positions[PIndex].PUW - W)/2; - Y := Positions[PIndex].PUStartY + (Positions[PIndex].PUH - H)/2; + X := aPositions[PIndex].PUStartX + (aPositions[PIndex].PUW - W)/2; + Y := aPositions[PIndex].PUStartY + (aPositions[PIndex].PUH - H)/2; - FontSize := Round(Progress * Positions[PIndex].PUFontSize); + FontSize := Round(Progress * aPositions[PIndex].PUFontSize); FontOffset := (H - FontSize) / 2; Alpha := 1; end @@ -822,20 +823,20 @@ begin // phase 2 - the moving Progress := (TimeDiff - Settings.Phase1Time) / Settings.Phase2Time; - W := Positions[PIndex].PUW; - H := Positions[PIndex].PUH; + W := aPositions[PIndex].PUW; + H := aPositions[PIndex].PUH; - PosDiff := Positions[PIndex].PUTargetX - Positions[PIndex].PUStartX; + PosDiff := aPositions[PIndex].PUTargetX - aPositions[PIndex].PUStartX; if PosDiff > 0 then PosDiff := PosDiff + W; - X := Positions[PIndex].PUStartX + PosDiff * sqr(Progress); + X := aPositions[PIndex].PUStartX + PosDiff * sqr(Progress); - PosDiff := Positions[PIndex].PUTargetY - Positions[PIndex].PUStartY; + PosDiff := aPositions[PIndex].PUTargetY - aPositions[PIndex].PUStartY; if PosDiff < 0 then - PosDiff := PosDiff + Positions[PIndex].BGH; - Y := Positions[PIndex].PUStartY + PosDiff * sqr(Progress); + PosDiff := PosDiff + aPositions[PIndex].BGH; + Y := aPositions[PIndex].PUStartY + PosDiff * sqr(Progress); - FontSize := Positions[PIndex].PUFontSize; + FontSize := aPositions[PIndex].PUFontSize; FontOffset := (H - FontSize) / 2; Alpha := 1 - 0.3 * Progress; end @@ -868,24 +869,24 @@ begin // set positions etc. Alpha := 0.7 - 0.7 * Progress; - W := Positions[PIndex].PUW; - H := Positions[PIndex].PUH; + W := aPositions[PIndex].PUW; + H := aPositions[PIndex].PUH; - PosDiff := Positions[PIndex].PUTargetX - Positions[PIndex].PUStartX; + PosDiff := aPositions[PIndex].PUTargetX - aPositions[PIndex].PUStartX; if (PosDiff > 0) then PosDiff := W else PosDiff := 0; - X := Positions[PIndex].PUTargetX + PosDiff * Progress; + X := aPositions[PIndex].PUTargetX + PosDiff * Progress; - PosDiff := Positions[PIndex].PUTargetY - Positions[PIndex].PUStartY; + PosDiff := aPositions[PIndex].PUTargetY - aPositions[PIndex].PUStartY; if (PosDiff < 0) then - PosDiff := -Positions[PIndex].BGH + PosDiff := -aPositions[PIndex].BGH else PosDiff := 0; - Y := Positions[PIndex].PUTargetY - PosDiff * (1 - Progress); + Y := aPositions[PIndex].PUTargetY - PosDiff * (1 - Progress); - FontSize := Positions[PIndex].PUFontSize; + FontSize := aPositions[PIndex].PUFontSize; FontOffset := (H - FontSize) / 2; end else @@ -922,7 +923,7 @@ begin glDisable(GL_BLEND); // set font style and size - SetFontStyle(Positions[PIndex].PUFont); + SetFontStyle(aPositions[PIndex].PUFont); SetFontItalic(false); SetFontSize(FontSize); SetFontReflection(false, 0); @@ -958,7 +959,7 @@ begin // only draw if player is on cur screen if (((Players[Index].Position and 128) = 0) = (ScreenAct = 1)) and Players[Index].Visible then begin - Position := @Positions[Players[Index].Position and 127]; + Position := @aPositions[Players[Index].Position and 127]; // draw scorebg glEnable(GL_TEXTURE_2D); @@ -1010,7 +1011,7 @@ begin Players[index].RBVisible and Players[index].Visible) then begin - Position := @Positions[Players[Index].Position and 127]; + Position := @aPositions[Players[Index].Position and 127]; if (Enabled and Players[Index].Enabled) then begin diff --git a/src/base/UThemes.pas b/src/base/UThemes.pas index 4322815e..2ecaa9f4 100644 --- a/src/base/UThemes.pas +++ b/src/base/UThemes.pas @@ -648,17 +648,17 @@ type SelectLevel: TThemeSelectSlide; SelectPlayList: TThemeSelectSlide; SelectPlayList2: TThemeSelectSlide; - SelectRounds: TThemeSelectSlide; - SelectTeams: TThemeSelectSlide; - SelectPlayers1: TThemeSelectSlide; - SelectPlayers2: TThemeSelectSlide; - SelectPlayers3: TThemeSelectSlide; {ButtonNext: TThemeButton; ButtonPrev: TThemeButton;} end; TThemePartyPlayer = class(TThemeBasic) + SelectTeams: TThemeSelectSlide; + SelectPlayers1: TThemeSelectSlide; + SelectPlayers2: TThemeSelectSlide; + SelectPlayers3: TThemeSelectSlide; + Team1Name: TThemeButton; Player1Name: TThemeButton; Player2Name: TThemeButton; @@ -681,6 +681,11 @@ type ButtonPrev: TThemeButton;} end; + TThemePartyRounds = class(TThemeBasic) + SelectRoundCount: TThemeSelectSlide; + SelectRound: array [0..6] of TThemeSelectSlide; + end; + //Stats Screens TThemeStatMain = class(TThemeBasic) ButtonScores: TThemeButton; @@ -756,6 +761,7 @@ type PartyWin: TThemePartyWin; PartyOptions: TThemePartyOptions; PartyPlayer: TThemePartyPlayer; + PartyRounds: TThemePartyRounds; //Stats Screens: StatMain: TThemeStatMain; @@ -886,6 +892,7 @@ begin PartyScore := TThemePartyScore.Create; PartyOptions := TThemePartyOptions.Create; PartyPlayer := TThemePartyPlayer.Create; + PartyRounds := TThemePartyRounds.Create; //Stats Screens: StatMain := TThemeStatMain.Create; @@ -1435,17 +1442,17 @@ begin ThemeLoadSelectSlide(PartyOptions.SelectLevel, 'PartyOptionsSelectLevel'); ThemeLoadSelectSlide(PartyOptions.SelectPlayList, 'PartyOptionsSelectPlayList'); ThemeLoadSelectSlide(PartyOptions.SelectPlayList2, 'PartyOptionsSelectPlayList2'); - ThemeLoadSelectSlide(PartyOptions.SelectRounds, 'PartyOptionsSelectRounds'); - ThemeLoadSelectSlide(PartyOptions.SelectTeams, 'PartyOptionsSelectTeams'); - ThemeLoadSelectSlide(PartyOptions.SelectPlayers1, 'PartyOptionsSelectPlayers1'); - ThemeLoadSelectSlide(PartyOptions.SelectPlayers2, 'PartyOptionsSelectPlayers2'); - ThemeLoadSelectSlide(PartyOptions.SelectPlayers3, 'PartyOptionsSelectPlayers3'); - {ThemeLoadButton (ButtonNext, 'ButtonNext'); ThemeLoadButton (ButtonPrev, 'ButtonPrev');} //Party Player ThemeLoadBasic(PartyPlayer, 'PartyPlayer'); + + ThemeLoadSelectSlide(PartyPlayer.SelectTeams, 'PartyPlayerSelectTeams'); + ThemeLoadSelectSlide(PartyPlayer.SelectPlayers1, 'PartyPlayerSelectPlayers1'); + ThemeLoadSelectSlide(PartyPlayer.SelectPlayers2, 'PartyPlayerSelectPlayers2'); + ThemeLoadSelectSlide(PartyPlayer.SelectPlayers3, 'PartyPlayerSelectPlayers3'); + ThemeLoadButton(PartyPlayer.Team1Name, 'PartyPlayerTeam1Name'); ThemeLoadButton(PartyPlayer.Player1Name, 'PartyPlayerPlayer1Name'); ThemeLoadButton(PartyPlayer.Player2Name, 'PartyPlayerPlayer2Name'); @@ -1464,6 +1471,13 @@ begin ThemeLoadButton(PartyPlayer.Player11Name, 'PartyPlayerPlayer11Name'); ThemeLoadButton(PartyPlayer.Player12Name, 'PartyPlayerPlayer12Name'); + // Party Rounds + ThemeLoadBasic(PartyRounds, 'PartyRounds'); + + ThemeLoadSelectSlide(PartyRounds.SelectRoundCount, 'PartyRoundsSelectRoundCount'); + for I := 0 to High(PartyRounds.SelectRound) do + ThemeLoadSelectSlide(PartyRounds.SelectRound[I], 'PartyRoundsSelectRound' + IntToStr(I + 1)); + {ThemeLoadButton(ButtonNext, 'PartyPlayerButtonNext'); ThemeLoadButton(ButtonPrev, 'PartyPlayerButtonPrev');} |