/**
 *	Team mode
 */

#Extends "Modes/TrackMania/RoundsBase.Script.txt"

#Const  CompatibleMapTypes  "Race"
#Const	Version		"2013-10-31"
#Const	ScriptName	"Team.Script.txt"

#Include "TextLib" as TL

// ---------------------------------- //
// Settings
// ---------------------------------- //
#Setting S_PointsLimit 			5
#Setting S_MaxPointsPerRound	6		as _("Max points :")	///< The maxium number of points attirbuted to the first player to cross the finish line
#Setting S_PointsGap			1		as _("Points gap :")	///< The number of points lead a team must have to win the map
#Setting S_UsePlayerClublinks	False	as _("Use Clublinks :")	///< Use the players clublinks, or otherwise use the default teams

// ---------------------------------- //
// Constants
// ---------------------------------- //
#Const C_BlueBotsNb	0
#Const C_RedBotsNb	0

// ---------------------------------- //
// Globales
// ---------------------------------- //

// ---------------------------------- //
// Extend
// ---------------------------------- //
***LogVersion***
***
MB_LogVersion(ScriptName, Version);
***

***StartServer***
***
// ---------------------------------- //
// Initialize mode
UseClans = True;
MB_UsePlayerClublinks = S_UsePlayerClublinks;

// ---------------------------------- //
// Create scores table
ST2::SetStyle("LibST_TMBaseTeams");
ST2::SetModeIcon("Icons128x32_1|RT_Team");
MB_SetScoresTableStyleFromXml(S_ScoresTableStylePath);
***

***InitMap***
***
// ---------------------------------- //
// Initialize scores
Scores_Clear();
ST2::ClearScores();

// ---------------------------------- //
// Set scores table for race
ST2::DestroyCol("LibST_TMBestTime");
ST2::CreateCol("LibST_TMPrevRaceDeltaPoints", "", "", 3., 60.);
ST2::SetColTextSize("LibST_TMPrevRaceDeltaPoints", 1.5);
ST2::CreateCol("LibST_TMPrevTime", "", "--:--.---", 8., 70.);
ST2::SetColTextAlign("LibST_TMPrevTime", CMlControl::AlignHorizontal::Right);
ST2::Build("TM");
***

***StartMap***
***
// ---------------------------------- //
// Initialize map
Users_SetNbFakeUsers(C_BlueBotsNb, C_RedBotsNb);
MB_WarmUp(-1);
SetFooterText();

Clublink::DefineTeamAuto();
Clublink::SyncUpdate();
***

***StartRound***
***
ST2::ClearScores();
CutOffTimeLimit = -1;
Clublink::DefineTeamAuto();
***

***PlayLoop***
***
// ---------------------------------- //
// Manage events
foreach (Event in PendingEvents) {
	PassOn(Event);
	XmlRpc::PassOn(Event);
	
	// ---------------------------------- //
	// Waypoint
	if (Event.Type == CTmModeEvent::EType::WayPoint) {
		if (Event.IsEndRace) {
			if (Event.Player.Score != Null) {
				if (Event.Player.Score.BestRace.Compare(Event.Player.CurRace, CTmResult::ETmRaceResultCriteria::Time) <= 0) {
					Event.Player.Score.BestRace = Event.Player.CurRace;
				}
			}
			Event.Player.Score.PrevRace = Event.Player.CurRace;
			ComputeLatestRaceScores();
			Scores_Sort(CTmMode::ETmScoreSortOrder::PrevRace_Time);
			TM2::EndRace(Event.Player);
			
			// ---------------------------------- //
			// Start the countdown if it's the first player to finish
			if (CutOffTimeLimit <= 0) {
				CutOffTimeLimit = GetFinishTimeout();
			}
		}
		if (Event.IsEndLap) {
			if (Event.Player.Score != Null) {
				if (Event.Player.Score.BestLap.Compare(Event.Player.CurLap, CTmResult::ETmRaceResultCriteria::Time) <= 0) {
					Event.Player.Score.BestLap = Event.Player.CurLap;
				}
			}
		}
	}
	// ---------------------------------- //
	// GiveUp
	else if (Event.Type == CTmModeEvent::EType::GiveUp) {
		TM2::WaitRace(Event.Player);
	}
}
***

***EndRound***
***
TM2::WaitRaceAll();
CutOffTimeLimit = -1;

// ---------------------------------- //
// Get the last round points
ComputeLatestRaceScores();
Scores_Sort(CTmMode::ETmScoreSortOrder::PrevRace_Time);
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound;
MB_Sleep(3000);
// ---------------------------------- //
// Add them to the total scores
ComputeScores();
Scores_Sort(CTmMode::ETmScoreSortOrder::PrevRace_Time);
MB_Sleep(3000);
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal;
UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing;

SetFooterText();
if (MapIsOver()) MB_StopMap = True;
***

***EndMap***
***
// ---------------------------------- //
// Ranking
declare WinningTeam = -1;
if (Clan1Score > Clan2Score) {
	WinningTeam = 1;
	MB_VictoryMessage = TL::Compose(_("$<%1$> wins the map!"), Teams[0].ColorizedName);
} else if (Clan2Score > Clan1Score) {
	WinningTeam = 2;
	MB_VictoryMessage = TL::Compose(_("$<%1$> wins the map!"), Teams[1].ColorizedName);
} else {
	MB_VictoryMessage = _("This map is a draw.");
}
foreach (Score in Scores) {
	if (WinningTeam == 1 || WinningTeam == 2) {
		if (Score.LadderClan == WinningTeam) {
			Score.Points = 2;
		} else if (Score.LadderClan == 3 - WinningTeam) {
			Score.Points = 1;
		} else {
			Score.Points = 0;
		}
	} else {
		if (Score.LadderClan == 1 || Score.LadderClan == 2) {
			Score.Points = 1;
		} else {
			Score.Points = 0;
		}
	}
	Score.PrevRaceDeltaPoints = 0;
}

// ---------------------------------- //
// Close ladder
Ladder_ComputeRank(CTmMode::ETmScoreSortOrder::TotalPoints);
Mode::Ladder_CloseMatch();

// ---------------------------------- //
// Set scores table for podium
ST2::DestroyCol("LibST_TMPrevRaceDeltaPoints");
ST2::DestroyCol("LibST_TMPrevTime");
ST2::CreateCol("LibST_TMBestTime", "", "--:--.---", 10., 70.);
ST2::SetColTextAlign("LibST_TMBestTime", CMlControl::AlignHorizontal::Right);
ST2::Build("TM");
SetFooterText();
Scores_Sort(CTmMode::ETmScoreSortOrder::BestRace_Time);
***

// ---------------------------------- //
// Functions
// ---------------------------------- //
// ---------------------------------- //
/** Get the time left to the players to finish the round after the first player
 *
 *	@return 		The time left in ms
 */
Integer GetFinishTimeout() {
	declare FinishTimeout = 0;
	
	if (S_FinishTimeout >= 0) {
		FinishTimeout = S_FinishTimeout * 1000;
	} else {
		FinishTimeout = 5000;
		if (Map.TMObjective_IsLapRace && NbLaps > 0 && Map.TMObjective_NbLaps > 0) {
			FinishTimeout += ((Map.TMObjective_AuthorTime / Map.TMObjective_NbLaps) * NbLaps) / 6;
		} else {
			FinishTimeout += Map.TMObjective_AuthorTime / 6;
		}
	}
	
	return Now + FinishTimeout;
}

// ---------------------------------- //
/// Update the text in the scores table footer
Void SetFooterText() {
	if (S_PointsGap > 1) {
		ST2::SetFooterText(TL::Compose("%1 "^S_PointsLimit^"  |  %2 "^S_PointsGap, _("Points limit : "), _("Points gap :")));
	} else {
		ST2::SetFooterText(TL::Compose("%1 "^S_PointsLimit, _("Points limit : ")));
	}
}

// ---------------------------------- //
/** Announce the round winner in the chat
 *
 *	@param	_TeamNum	The number of the team who won the round
 */
Void AnnounceWinner(Integer _TeamNum) {
	if (!Teams.existskey(_TeamNum)) {
		UIManager.UIAll.SendChat(_("This round is a draw."));
	} else {
		UIManager.UIAll.SendChat(TL::Compose(_("$<%1$> wins the round!"), Teams[_TeamNum].ColorizedName));
	}
}

// ---------------------------------- //
/// Compute the latest race scores
Void ComputeLatestRaceScores() {
	if (Scores.count <= 0) return;
	Scores_Sort(CTmMode::ETmScoreSortOrder::PrevRace_Time);
	
	// Each player finishing the race scores (MaxPoints - (Rank - 1))
	if (S_UseAlternateRules) {
		declare Points = Scores.count;
		foreach (Score in Scores) {
			if (Score.User.RequestsSpectate && Score.PrevRace.Time <= 0) {
				Points -= 1;
			}
		}
		if (Points > S_MaxPointsPerRound) Points = S_MaxPointsPerRound;
		
		declare TeamsScores = [0, 0];
		declare WinningTeam = -1;
		foreach (Score in Scores) {
			if (Score.TeamNum != 1 && Score.TeamNum != 2) continue;
			
			if (Score.PrevRace.Time > 0) {
				TeamsScores[Score.TeamNum - 1] += Points;
				Score.PrevRaceDeltaPoints = Points;
				if (Points > 0) Points -= 1;
			} else {
				Score.PrevRaceDeltaPoints = 0;
			}
		}
		
		if (TeamsScores[0] != TeamsScores[1]) {
			if (TeamsScores[0] > TeamsScores[1]) {
				WinningTeam = 0;
			} else {
				WinningTeam = 1;
			}
		}
		
		foreach (TeamIndex => Team in Teams) {
			declare PrevRaceDeltaPoints for Team = 0;
			if (TeamIndex == WinningTeam) {
				PrevRaceDeltaPoints = 1;
			} else {
				PrevRaceDeltaPoints = 0;
			}
		}
	} 
	// Give one points to all the players of a team from first place to the first player of the opposing team
	// eg: R, R, B, R, B, B -> 1, 1, 0, 0, 0, 0 -> 2 points for Red
	else {
		declare WinningTeam = -1;
		declare Points = 0;
		declare AheadOfOpponents = True;
		
		foreach (Score in Scores) {
			if (Score.TeamNum != 1 && Score.TeamNum != 2) continue;
			if (Score.PrevRace.Time <= 0) break;
			if (WinningTeam == -1) WinningTeam = Score.TeamNum - 1;
			
			if (Score.TeamNum - 1 == WinningTeam) {
				if (AheadOfOpponents) {
					Points += 1;
					Score.PrevRaceDeltaPoints = 1;
				}
			} else {
				AheadOfOpponents = False;
			}
		}
		
		if (Points > S_MaxPointsPerRound) Points = S_MaxPointsPerRound;
		
		foreach (TeamIndex => Team in Teams) {
			declare PrevRaceDeltaPoints for Team = 0;
			if (TeamIndex == WinningTeam) {
				PrevRaceDeltaPoints = Points;
			} else {
				PrevRaceDeltaPoints = 0;
			}
		}
	}
}

// ---------------------------------- //
/// Compute the map scores
Void ComputeScores() {
	declare PrevRaceDeltaPoints as ScoreTeam0 for Teams[0] = 0;
	declare PrevRaceDeltaPoints as ScoreTeam1 for Teams[1] = 0;
	
	declare WinningTeam = -1;
	if (ScoreTeam0 > 0 && ScoreTeam0 > ScoreTeam1) {
		WinningTeam = 0;
	} else if (ScoreTeam1 > 0 && ScoreTeam1 > ScoreTeam0) {
		WinningTeam = 1;
	}
	
	AnnounceWinner(WinningTeam);
	
	foreach (TeamIndex => Team in Teams) {
		declare PrevRaceDeltaPoints for Team = 0;	
		if (ClanScores.existskey(TeamIndex + 1)) {
			ClanScores[TeamIndex + 1] += PrevRaceDeltaPoints;
		}
	}
	
	foreach (Score in Scores) {
		Score.PrevRaceDeltaPoints = 0;
		if (Score.User.RequestsSpectate) {
			Score.LadderClan = -1;
		} else {
			Score.LadderClan = Score.TeamNum;
		}
	}
}

// ---------------------------------- //
/** Check if we should go to the next map
 *
 *	@return		True if it is the case, false otherwise
 */
Boolean MapIsOver() {
	declare PointsGap = S_PointsGap;
	if (PointsGap < 0) PointsGap = 0;
	
	if (Clan1Score >= S_PointsLimit && Clan1Score - Clan2Score >= PointsGap) {
		return True;
	} else if (Clan2Score >= S_PointsLimit && Clan2Score - Clan1Score >= PointsGap) {
		return True;
	}
	
	return False;
}