aboutsummaryrefslogtreecommitdiffstats
path: root/src/base/UParty.pas
diff options
context:
space:
mode:
Diffstat (limited to 'src/base/UParty.pas')
-rw-r--r--src/base/UParty.pas1021
1 files changed, 802 insertions, 219 deletions
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.