/**
 *	Rounds mode
 */

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

#Const  CompatibleMapTypes  "Race"
#Const	Version		"2014-10-07"
#Const	ScriptName	"Rounds.Script.txt"

#Include "TextLib" as TL

// ---------------------------------- //
// Settings
// ---------------------------------- //
#Setting S_PointsLimit	50
#Setting S_UseTieBreak	True	as _("Use tie-break :")	///< Continue to play the map until the tie is broken

// ---------------------------------- //
// Constants
// ---------------------------------- //
#Const C_BotsNb 0
#Const Description _("""$fffIn $f00Rounds$fff mode, the goal is to win a maximum number of $f00points.

$fffThe rounds mode consists of $f00a series of races$fff.
When you finish a race in a good $f00position$fff, you get $f00points$fff, added to your total.

The $f00winner$fff is the first player whose total reaches the $f00point limit$fff (30 for example).""")

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

***InitServer***
***
declare PrevPointsLimit = -1;
***

***StartServer***
***
ST2::SetStyle("LibST_TMBaseSolo");
ST2::SetModeIcon("Icons128x32_1|RT_Rounds");
ST2::CreateCol("LibST_TMPrevRaceDeltaPoints", "", "0", 4., 60.);
ST2::CreateCol("LibST_TMPoints", "", "0", 4., 70.);
ST2::SetColTextAlign("LibST_TMPrevRaceDeltaPoints", CMlControl::AlignHorizontal::Right);
ST2::SetColTextAlign("LibST_TMPoints", CMlControl::AlignHorizontal::Right);
ST2::SetColTextSize("LibST_TMPrevRaceDeltaPoints", 1.5);
MB_SetScoresTableStyleFromXml(S_ScoresTableStylePath);
ST2::Build("TM");
***

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

***StartMap***
***
// ---------------------------------- //
// Initialize map
Users_SetNbFakeUsers(C_BotsNb, 0);

// ---------------------------------- //
// Warm up
declare WarmUpTimeLimit = -1;
if (S_UseAlternateRules) {
	G_RoundStartTime = Now + 3000;
	WarmUpTimeLimit = (GetFinishTimeout() - Now) / 1000;
}
MB_WarmUp(WarmUpTimeLimit);
***

***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::TotalPoints);
			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);
	}
}

// ---------------------------------- //
// Server info change
if (PrevPointsLimit != S_PointsLimit) {
	PrevPointsLimit = S_PointsLimit;
	ST2::SetFooterText(TL::Compose("%1 "^S_PointsLimit, _("Points limit : ")));
}
***

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

if (ForceEndRound) {
	ForcedEndRoundSequence();
} else {
	// Get the last round points
	ComputeLatestRaceScores();
	Scores_Sort(CTmMode::ETmScoreSortOrder::TotalPoints);
	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::TotalPoints);
	MB_Sleep(3000);
	UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal;
	UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing;
	
	if (MapIsOver()) MB_StopMap = True;
}
***

***EndMap***
***
// ---------------------------------- //
// Close ladder
Ladder_ComputeRank(CTmMode::ETmScoreSortOrder::TotalPoints);
MB_Ladder_CloseMatch();

Scores_Sort(CTmMode::ETmScoreSortOrder::TotalPoints);
if (Scores.existskey(0) &&  Scores[0].BestRace.Time > 0) {
	MB_VictoryMessage = TL::Compose(_("$<%1$> wins the map!"), Scores[0].User.Name);
}
***

// ---------------------------------- //
// 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;
		}
	}
	
	if (S_UseAlternateRules) {
		if (Map.TMObjective_IsLapRace && NbLaps > 0 && Map.TMObjective_NbLaps > 0) {
			return G_RoundStartTime + ((Map.TMObjective_AuthorTime / Map.TMObjective_NbLaps) * NbLaps) + FinishTimeout;
		} else {
			return G_RoundStartTime + Map.TMObjective_AuthorTime + FinishTimeout;
		}
	} else {
		return Now + FinishTimeout;
	}
	
	// Default value from TMO, TMS (not used)
	return Now + 15000;
}

// ---------------------------------- //
/// Compute the latest race scores
Void ComputeLatestRaceScores() {
	Scores_Sort(CTmMode::ETmScoreSortOrder::PrevRace_Time);
	
	// Only points for the first players
	if (S_UseAlternateRules) {
		declare Points = 1;
		
		foreach (Score in Scores) {
			if (Score.PrevRace.Time > 0) {
				Score.PrevRaceDeltaPoints = Points;
				if (Points > 0) Points -= 1;
			} else {
				Score.PrevRaceDeltaPoints = 0;
			}
		}
	} 
	// Points distributed between all players
	else {		
		declare I = 0;
		foreach (Score in Scores) {
			if (Score.PrevRace.Time > 0) {
				declare Points = 0;
				if (MB_PointsRepartition.count > 0) {
					if (MB_PointsRepartition.existskey(I)) {
						Points = MB_PointsRepartition[I];
					} else {
						Points = MB_PointsRepartition[MB_PointsRepartition.count - 1];
					}
				}
				Score.PrevRaceDeltaPoints = Points;
				I += 1;
			} else {
				Score.PrevRaceDeltaPoints = 0;
			}
		}
	}
}

// ---------------------------------- //
/// Compute the map scores
Void ComputeScores() {
	foreach (Score in Scores) {
		Score.Points += Score.PrevRaceDeltaPoints;
		Score.PrevRaceDeltaPoints = 0;
	}
}

// ---------------------------------- //
/** Check if we should go to the next map
 *
 *	@return		True if it is the case, false otherwise
 */
Boolean MapIsOver() {
	declare MaxScore = -1;
	declare Tie = False;
	foreach (Score in Scores) {
		if (Score.Points > MaxScore) {
			MaxScore = Score.Points;
			Tie = False;
		} else if (Score.Points == MaxScore) {
			Tie = True;
		}
	}
	
	if (S_UseTieBreak && Tie) return False;
	if (MaxScore >= S_PointsLimit) return True;
		
	return False;
}