Initial commit of TimeAttackRounds mode based on SeedingTimeAttack mode
This commit is contained in:
		
							
								
								
									
										585
									
								
								TM_TimeAttackRounds_Online.Script.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										585
									
								
								TM_TimeAttackRounds_Online.Script.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,585 @@ | ||||
| /** | ||||
| *	Time Attack Rounds mode | ||||
| *   Quick script ordered by Bergie for Telialigaen | ||||
| */ | ||||
| // #RequireContext CSmMode | ||||
| #Extends "Libs/Nadeo/TMNext/TrackMania/Modes/TMNextRoundsBase.Script.txt" | ||||
|  | ||||
| #Const	CompatibleMapTypes	"TrackMania\\TM_Race,TM_Race" | ||||
| #Const	Version							"2022-01-22" | ||||
| #Const	ScriptName					"Modes/TrackMania/TM_TimeAttackRounds_Online.Script.txt" | ||||
|  | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| // Libraries | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| #Include "TextLib" as TL | ||||
| #Include "MathLib" as ML | ||||
| #Include "Libs/Nadeo/CommonLibs/Common/Task.Script.txt" as Task | ||||
| #Include "Libs/Nadeo/TMNext/TrackMania/Modes/TimeAttack/StateManager.Script.txt" as StateMgr | ||||
| #Include "Libs/Nadeo/TMNext/TrackMania/Modes/TrophyRanking.Script.txt" as TrophyRanking | ||||
| #Include "Libs/Nadeo/TMNext/TrackMania/Menu/Constants.Script.txt" as MenuConsts | ||||
| #Include "Libs/Nadeo/ModeLibs/Common/Utils.Script.txt" as ModeUtils | ||||
| #Include "Libs/Nadeo/CommonLibs/Common/Semver.Script.txt" as Semver | ||||
|  | ||||
| // UI from Race | ||||
| #Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/Checkpoint_Server.Script.txt" as UIModules_Checkpoint | ||||
| #Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/ScoresTable_Server.Script.txt" as UIModules_ScoresTable | ||||
| #Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/PauseMenuOnline_Server.Script.txt" as UIModules_PauseMenu_Online | ||||
| #Include "ManiaApps/Nadeo/TMNext/TrackMania/TimeAttack/UIModules/EndMatchTrophy_Server.Script.txt" as UIModules_EndMatchTrophy | ||||
|  | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| // Settings | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| #Setting S_UseTheWorstTimeForDNF True as ""  | ||||
| #Setting S_MalusTimeForDNF 10000 as "Time to add (or substract) in ms for who DNF"  | ||||
| #Setting S_MapsPerMatch 3 as _("Number of maps per match") ///< Number of maps to play before finishing the match | ||||
| #Setting S_TimeLimit 300 as _("Time limit") ///< Time limit before going to the next map | ||||
| #Setting S_WarmUpNb 0 as _("Number of warm up") | ||||
| #Setting S_WarmUpDuration 0 as _("Duration of one warm up") | ||||
| #Setting S_WarmUpTimeout -1 as _("Warm up timeout") | ||||
| #Setting S_ForceLapsNb 0 | ||||
| #Setting S_PointsLimit 0 as _("Points limit") | ||||
| #Setting S_RoundsPerMap 1 as _("Number of rounds per map") ///< Number of round to play on one map before going to the next one | ||||
| #Setting S_UseTieBreak False as _("Use tie-break")	///< Continue to play the map until the tie is broken | ||||
| #Setting S_PointsRepartition "10,9,8,7,6,5,4,3,2,1,0" | ||||
|  | ||||
|  | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| // Constants | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| #Const C_ModeName "Time Attack" | ||||
| //L16N [Time Attack] Description of the mode rules | ||||
| #Const Description _("$zIn $<$t$6F9Time Attack$> mode, the goal is to set the $<$t$6F9best time$>.\n\nYou have as many tries as you want, and you can $<$t$6F9retry$> when you want by pressing the respawn key.\n\nWhen the time is up, the $<$t$6F9winner$> is the player with the $<$t$6F9best time$>.") | ||||
|  | ||||
| #Const C_HudModulePath "" //< Path to the hud module | ||||
| #Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/TMNext/TrackMania/TimeAttack/TimeAttack.Script.txt" //< Url of the mania app | ||||
| #Const C_FakeUsersNb 0 | ||||
|  | ||||
| #Const C_UploadRecord True | ||||
| #Const C_DisplayRecordGhost True | ||||
| #Const C_DisplayRecordMedal True | ||||
| #Const C_CelebrateRecordGhost True | ||||
| #Const C_CelebrateRecordMedal True | ||||
| #Const C_DisplayWorldTop True | ||||
|  | ||||
| #Const C_PointsLimit_NotReached 0 | ||||
| #Const C_PointsLimit_Reached 1 | ||||
| #Const C_PointsLimit_Tie 2 | ||||
|  | ||||
| #Const C_TrophyTaskTimeout 5000 | ||||
| #Const C_TrophyAnimationDuration 4000 | ||||
| #Const C_TrophyDisplayDuration 7000 | ||||
|  | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| // Extends | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| ***Match_LogVersions*** | ||||
| *** | ||||
| Log::RegisterScript(ScriptName, Version); | ||||
| Log::RegisterScript(StateMgr::ScriptName, StateMgr::Version); | ||||
| Log::RegisterScript(Semver::ScriptName, Semver::Version); | ||||
| *** | ||||
|  | ||||
| ***Match_LoadLibraries*** | ||||
| *** | ||||
| StateMgr::Load(); | ||||
| *** | ||||
|  | ||||
| ***Match_UnloadLibraries*** | ||||
| *** | ||||
| StateMgr::Unload(); | ||||
| *** | ||||
|  | ||||
| ***Match_Settings*** | ||||
| *** | ||||
| MB_Settings_UseDefaultTimer = False; | ||||
| MB_Settings_UseDefaultHud = (C_HudModulePath == ""); | ||||
| MB_Settings_UseDefaultPodiumSequence = False; | ||||
| *** | ||||
|  | ||||
| ***Match_Rules*** | ||||
| *** | ||||
| ModeInfo::SetName(C_ModeName); | ||||
| ModeInfo::SetType(ModeInfo::C_Type_FreeForAll); | ||||
| ModeInfo::SetRules(Description); | ||||
| ModeInfo::SetStatusMessage(_("TYPE: Free for all\nOBJECTIVE: Set the best time on the track.")); | ||||
| *** | ||||
|  | ||||
| ***Match_LoadHud*** | ||||
| *** | ||||
| if (C_HudModulePath != "") Hud_Load(C_HudModulePath); | ||||
| *** | ||||
|  | ||||
| ***Match_AfterLoadHud*** | ||||
| *** | ||||
| UIManager.UIAll.ScoreTableOnlyManialink = True; | ||||
| UIModules_Checkpoint::SetRankMode(UIModules_Checkpoint::C_RankMode_BestRace); | ||||
| ClientManiaAppUrl = C_ManiaAppUrl; | ||||
| Race::SortScores(Race::C_Sort_BestRaceTime); | ||||
| UIModules_TimeGap::SetTimeGapMode(UIModules_TimeGap::C_TimeGapMode_BestRace); | ||||
| UIModules_PauseMenu_Online::SetHelp(Description); | ||||
| UIManager.UIAll.OverlayHideCountdown = True; | ||||
| UIManager.UIAll.OverlayHideSpectatorInfos = True; | ||||
| UIManager.UIAll.OverlayHideChrono = True; | ||||
| *** | ||||
|  | ||||
| ***Match_Yield*** | ||||
| *** | ||||
| foreach (Event in PendingEvents) { | ||||
| 	switch (Event.Type) { | ||||
| 		// Initialize players when they join the server | ||||
| 		case CSmModeEvent::EType::OnPlayerAdded: { | ||||
| 			StateMgr::InitializePlayer(Event.Player); | ||||
| 			CarRank::InitializePlayer(Event.Player); | ||||
| 			if (Event.Player != Null) { | ||||
| 				declare Boolean Match_CanForceTrophyRankUpdate for This; | ||||
| 				TrophyRanking::InitializeUser(Event.Player.User, Match_CanForceTrophyRankUpdate); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| StateMgr::Yield(); | ||||
| TrophyRanking::Yield(); | ||||
| *** | ||||
|  | ||||
| ***Match_StartServer*** | ||||
| *** | ||||
| // Initialize mode | ||||
| Clans::SetClansNb(0); | ||||
| GiveUpBehaviour_RespawnAfter = True; | ||||
| CrudeExtrapolation_AllowDelay = True; | ||||
| Race::SetRespawnBehaviour(Race::C_RespawnBehaviour_GiveUpBeforeFirstCheckpoint); | ||||
| StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]); | ||||
| WarmUp::SetAvailability(True); | ||||
| Race::SetupRecord( | ||||
| 	MenuConsts::C_ScopeType_Season, | ||||
| 	MenuConsts::C_ScopeType_PersonalBest, | ||||
| 	MenuConsts::C_GameMode_TimeAttack, | ||||
| 	"", | ||||
| 	C_UploadRecord, | ||||
| 	C_DisplayRecordGhost, | ||||
| 	C_DisplayRecordMedal, | ||||
| 	C_CelebrateRecordGhost, | ||||
| 	C_CelebrateRecordMedal, | ||||
| 	C_DisplayWorldTop | ||||
| ); | ||||
| CarRank::Reset(); | ||||
|  | ||||
| Scores::SaveInScore(Scores::C_Points_Match); | ||||
| UIModules_Record::SetSpecialVisibility(False); | ||||
| *** | ||||
|  | ||||
| ***Match_InitMatch*** | ||||
| *** | ||||
| declare Task::LibCommonTask_K_Task Match_TrophyTask; | ||||
| declare Integer Match_TrophyTaskEndTime; | ||||
| declare Integer Match_MatchDuration; | ||||
| declare Boolean Match_CanForceTrophyRankUpdate for This = False; | ||||
| declare netwrite Text Net_ScriptEnvironment for Teams[0] = S_ScriptEnvironment; | ||||
| *** | ||||
|  | ||||
| ***Match_AfterLoadMap*** | ||||
| *** | ||||
| Match_CanForceTrophyRankUpdate = True; | ||||
| *** | ||||
|  | ||||
| ***Match_InitMap*** | ||||
| *** | ||||
| declare Integer Map_ValidRoundsNb; | ||||
| declare Boolean Map_Skipped; | ||||
|  | ||||
| declare Integer Map_TimeLimit; | ||||
| declare Integer Map_MapStartTime; | ||||
| declare Integer Map_MalusTimeForDNF; | ||||
| declare Integer Map_MapsPerMatch; | ||||
| declare Boolean Map_UseTheWorstTimeForDNF; | ||||
| *** | ||||
|  | ||||
| ***Match_StartMap*** | ||||
| *** | ||||
| // Add bot when necessary | ||||
| //Users_SetNbFakeUsers(C_FakeUsersNb, 0); | ||||
|  | ||||
| // Warm up | ||||
| UIModules_ScoresTable::SetFooterInfo(_("Warmup")); | ||||
| GiveUpBehaviour_RespawnAfter = False; | ||||
| MB_WarmUp(S_WarmUpNb, S_WarmUpDuration * 1000, S_WarmUpTimeout * 1000); | ||||
| GiveUpBehaviour_RespawnAfter = True; | ||||
|  | ||||
| // Initialize race | ||||
| SetScoresTableScoreMode(False); | ||||
| StartTime = Now + Race::C_SpawnDuration; | ||||
|  | ||||
| // Spawn players for the race | ||||
| foreach (Player in Players) { | ||||
| 	Race::Start(Player, StartTime); | ||||
| } | ||||
|  | ||||
| StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]); | ||||
| CarRank::Update(CarRank::C_SortCriteria_BestRace); | ||||
| Race::EnableIntroDuringMatch(True); | ||||
|  | ||||
| Race::SortScores(Race::C_Sort_BestRaceTime); | ||||
|  | ||||
| declare netwrite Integer Net_SerialNeedToUpdate for Teams[0]; | ||||
| Net_SerialNeedToUpdate = 0; | ||||
|  | ||||
| Map_TimeLimit = S_TimeLimit; | ||||
| Map_MapStartTime = StartTime; | ||||
| Map_MapsPerMatch = S_MapsPerMatch; | ||||
| UpdateScoresTableFooterAndTimeLimit(StartTime, S_TimeLimit, S_MapsPerMatch); | ||||
| *** | ||||
|  | ||||
| ***Match_InitPlayLoop*** | ||||
| *** | ||||
| Round_Skipped = False; | ||||
| *** | ||||
|  | ||||
| ***Match_PlayLoop*** | ||||
| *** | ||||
| // Manage race events | ||||
| declare RacePendingEvents = Race::GetPendingEvents(); | ||||
| foreach (Event in RacePendingEvents) { | ||||
| 	Race::ValidEvent(Event); | ||||
| 	// Waypoint | ||||
| 	if (Event.Type == Events::C_Type_Waypoint) { | ||||
| 		if (Event.Player != Null) { | ||||
| 			declare Better = False; | ||||
| 			declare OldRank = 0; | ||||
| 			if (Event.IsEndRace) { | ||||
| 				// Computes old rank before changing Score | ||||
| 				foreach (Index => Score in Scores) { | ||||
| 					if (Score.Id == Event.Player.Score.Id) { | ||||
| 						if (Score.BestRaceTimes.count != 0) { | ||||
| 							OldRank = Index + 1; | ||||
| 						} else { | ||||
| 							OldRank = -123; | ||||
| 						} | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				// Change Score | ||||
| 				Better = Scores::UpdatePlayerBestRaceIfBetter(Event.Player); | ||||
| 				declare BetterLap = Scores::UpdatePlayerBestLapIfBetter(Event.Player); | ||||
| 				Scores::UpdatePlayerPrevRace(Event.Player);				 | ||||
| 			} else if (Event.IsEndLap) { | ||||
| 				declare BetterLap = Scores::UpdatePlayerBestLapIfBetter(Event.Player); | ||||
| 				if (Race::IsIndependentLaps()) { | ||||
| 					// Computes old rank before changing Score | ||||
| 					foreach (Index => Score in Scores) { | ||||
| 						if (Score.Id == Event.Player.Score.Id) { | ||||
| 							if (Score.BestLapTimes.count != 0) { | ||||
| 								OldRank = Index + 1; | ||||
| 							} else { | ||||
| 								OldRank = -123; | ||||
| 							} | ||||
| 							break; | ||||
| 						} | ||||
| 					} | ||||
| 					// Change Score | ||||
| 					Better = BetterLap; | ||||
| 					if (Better) Scores::UpdatePlayerBestRace(Event.Player); | ||||
| 					Scores::UpdatePlayerPrevLap(Event.Player); | ||||
| 				} | ||||
| 			} | ||||
| 			if (Better) { | ||||
| 				declare NewRank = 0; | ||||
| 				foreach (Index => Score in Scores) { | ||||
| 					if (Score.Id == Event.Player.Score.Id) { | ||||
| 						NewRank = Index + 1; | ||||
| 						break; | ||||
| 					} | ||||
| 				} | ||||
| 				if (OldRank != NewRank) { | ||||
| 					if (0 < NewRank && NewRank < 4) { | ||||
| 						foreach (Player in AllPlayers) { | ||||
| 							UIModules_DisplayMessage::SendLiveMessage_RankUpdate(Player, Event.Player.User, NewRank); | ||||
| 						} | ||||
| 					} else if (0 < NewRank) { | ||||
| 						UIModules_DisplayMessage::SendLiveMessage_RankUpdate(Event.Player, Event.Player.User, NewRank); | ||||
| 					} | ||||
| 				} | ||||
| 				 | ||||
| 				CarRank::ThrottleUpdate(CarRank::C_SortCriteria_BestRace); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Manage mode events | ||||
| foreach (Event in PendingEvents) { | ||||
| 	if (Event.HasBeenPassed || Event.HasBeenDiscarded) continue; | ||||
| 	Events::Invalid(Event); | ||||
| } | ||||
|  | ||||
| // Spawn players | ||||
| if (PlayersNbDead > 0) { //< Check for unspawned players only if at least one player is unspawned | ||||
| 	foreach (Player in Players) { | ||||
| 		if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned && Race::IsReadyToStart(Player)) { | ||||
| 			Race::Start(Player); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Update the map duration setting | ||||
| if (Map_TimeLimit != S_TimeLimit || Map_MalusTimeForDNF != S_MalusTimeForDNF || Map_MapsPerMatch != S_MapsPerMatch || Map_UseTheWorstTimeForDNF != S_UseTheWorstTimeForDNF) { | ||||
| 	Map_UseTheWorstTimeForDNF = S_UseTheWorstTimeForDNF; | ||||
| 	Map_MalusTimeForDNF = S_MalusTimeForDNF; | ||||
| 	Map_TimeLimit = S_TimeLimit; | ||||
| 	Map_MapsPerMatch = S_MapsPerMatch; | ||||
| 	UpdateScoresTableFooterAndTimeLimit(StartTime, S_TimeLimit, S_MapsPerMatch); | ||||
| } | ||||
| if (Net_ScriptEnvironment != S_ScriptEnvironment) { | ||||
| 	Net_ScriptEnvironment = S_ScriptEnvironment; | ||||
| } | ||||
| *** | ||||
|  | ||||
| ***Match_EndRound*** | ||||
| *** | ||||
| Race::StopSkipOutroAll(); | ||||
| EndTime = -1; | ||||
| StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]); | ||||
| CarRank::Update(CarRank::C_SortCriteria_CurrentRace); | ||||
|  | ||||
| if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.1.1")) { | ||||
| 	Scores::XmlRpc_SendScores(Scores::C_Section_PreEndRound, ""); | ||||
| } | ||||
|  | ||||
| if (Round_ForceEndRound || Round_SkipPauseRound || Round_Skipped) { | ||||
| 	// Cancel points | ||||
| 	foreach (Score in Scores) { | ||||
| 		Scores::SetPlayerRoundPoints(Score, 0); | ||||
| 	} | ||||
| 	// Do not launch the forced end round sequence after a pause | ||||
| 	if (!Round_SkipPauseRound) { | ||||
| 		ForcedEndRoundSequence(); | ||||
| 	} | ||||
| } else { | ||||
| 	Map_ValidRoundsNb += 1; | ||||
| 	ComputeLatestRaceScores(); | ||||
| 	UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible; | ||||
| 	UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound; | ||||
| 	MB_Sleep(3000); | ||||
| 	SetScoresTableScoreMode(True); | ||||
| 	MB_Sleep(3000); | ||||
| 	Scores::EndRound(); | ||||
| 	Race::SortScores(Race::C_Sort_TotalPoints); | ||||
| 	MB_Sleep(3000); | ||||
| 	Race::SortScores(Race::C_Sort_BestRaceTime); | ||||
| 	SetScoresTableScoreMode(False); | ||||
| 	UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal; | ||||
| 	UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing; | ||||
| 	 | ||||
| 	if (MapIsOver(S_UseTieBreak, S_PointsLimit, Map_ValidRoundsNb, S_RoundsPerMap)) { | ||||
| 		Map_Skipped = False; | ||||
| 		MB_StopMap(); | ||||
| 	} | ||||
| } | ||||
| *** | ||||
|  | ||||
|  | ||||
| ***Match_EndMap*** | ||||
| *** | ||||
| EndTime = -1; | ||||
|  | ||||
|  | ||||
| Scores::SetPlayerWinner(Scores::GetBestPlayer(Scores::C_Sort_MatchPoints)); | ||||
|  | ||||
| Match_MatchDuration = ML::Max(0, Now - Map_MapStartTime); | ||||
| StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]); | ||||
| Race::EnableIntroDuringMatch(False); | ||||
|  | ||||
| Race::SortScores(Race::C_Sort_BestRaceTime); | ||||
| TrophyRanking::UpdateUsersRank(); | ||||
| CarRank::Update(CarRank::C_SortCriteria_BestRace); | ||||
|  | ||||
| if (MatchIsOver(S_UseTieBreak, S_PointsLimit, MB_GetMapCount(), S_MapsPerMatch, S_RoundsPerMap, Map_Skipped)) MB_StopMatch(); | ||||
|  | ||||
| if (!MB_MapIsRunning() && MB_MatchIsRunning()) MB_SkipPodiumSequence(); | ||||
| *** | ||||
|  | ||||
| ***Match_BeforeUnloadMap*** | ||||
| *** | ||||
| Match_CanForceTrophyRankUpdate = False; | ||||
| *** | ||||
|  | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| // Functions | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| /// Select the scores table score mode | ||||
| Void SetScoresTableScoreMode(Boolean _IsPoints) { | ||||
| 	if (_IsPoints) UIModules_ScoresTable::SetScoreMode(UIModules_ScoresTable::C_Mode_Points); | ||||
| 	else UIModules_ScoresTable::SetScoreMode(UIModules_ScoresTable::C_Mode_BestTime); | ||||
| } | ||||
|  | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| /** Update the time limit | ||||
|  * | ||||
|  *	@param	_StartTime								The starting time of the map | ||||
|  *	@param	_NewTimeLimit							The time limit before going to the next map | ||||
|  */ | ||||
| Void UpdateScoresTableFooterAndTimeLimit(Integer _StartTime, Integer _NewTimeLimit, Integer _MapsPerMatch) { | ||||
| 	Log::Log("[UpdateScoresTableFooterAndTimeLimit] Update Settings Footer"); | ||||
| 	declare Text[] Parts; | ||||
| 	declare Text Message = ""; | ||||
| 	if (_NewTimeLimit <= 0) { | ||||
| 		if (Parts.count > 0) Message ^= "\n"; | ||||
| 		Message ^= """%{{{Parts.count + 1}}} -"""; | ||||
| 		//L16N [Rounds] Number of points to reach to win the match. | ||||
| 		Parts.add(_("Time Limit : ")); | ||||
| 		EndTime = -1; | ||||
| 		UIModules_ScoresTable::SetFooterInfo(TL::Compose("%1 -", _("Time Limit"))); | ||||
| 	} else { | ||||
| 		if (Parts.count > 0) Message ^= "\n"; | ||||
| 		Message ^= """%{{{Parts.count + 1}}}{{{TL::TimeToText(_NewTimeLimit*1000)}}}"""; | ||||
| 		//L16N [Rounds] Number of points to reach to win the match. | ||||
| 		Parts.add(_("Time Limit : ")); | ||||
| 		EndTime = _StartTime + (S_TimeLimit * 1000); | ||||
| 		UIModules_ScoresTable::SetFooterInfo(TL::Compose("%1 "^TL::TimeToText(_NewTimeLimit*1000), _("Time Limit"))); | ||||
| 	} | ||||
| 	if (_MapsPerMatch > 0) { | ||||
| 		if (Parts.count > 0) Message ^= "\n"; | ||||
| 		Message ^= """%{{{Parts.count + 1}}}{{{MB_GetMapCount()}}}/{{{_MapsPerMatch}}}"""; | ||||
| 		//L16N [Rounds] Number of maps played during the match. | ||||
| 		Parts.add(_("Maps : ")); | ||||
| 	} | ||||
| 	switch (Parts.count) { | ||||
| 		case 0: UIModules_ScoresTable::SetFooterInfo(Message); | ||||
| 		case 1: UIModules_ScoresTable::SetFooterInfo(TL::Compose(Message, Parts[0])); | ||||
| 		case 2: UIModules_ScoresTable::SetFooterInfo(TL::Compose(Message, Parts[0], Parts[1])); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| /** Get the right sort criteria for | ||||
|  *	the scores | ||||
|  * | ||||
|  *	@return														The sort criteria | ||||
|  */ | ||||
| Integer GetScoresSortCriteria() { | ||||
| 	if (Race::IsIndependentLaps()) return Race::C_Sort_BestLapTime; | ||||
| 	return Race::C_Sort_BestRaceTime; | ||||
| } | ||||
|  | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| /** Get the right sort criteria for | ||||
|  *	the ladder | ||||
|  * | ||||
|  *	@return														The sort criteria | ||||
|  */ | ||||
| Integer GetLadderSortCriteria() { | ||||
| 	if (Race::IsIndependentLaps()) return Scores::C_Sort_BestLapTime; | ||||
| 	return Scores::C_Sort_BestRaceTime; | ||||
| } | ||||
|  | ||||
| Void ComputeLatestRaceScores() { | ||||
| 	// Points distributed between all players | ||||
| 	declare Integer I = 0; | ||||
| 	declare Integer[] PointsRepartition = PointsRepartition::GetPointsRepartition(); | ||||
| 	foreach (Score in Scores) { | ||||
| 		if (Scores::GetPlayerBestRaceTime(Score) > 0) { | ||||
| 			declare Integer Points = 0; | ||||
| 			if (PointsRepartition.count > 0) { | ||||
| 				if (PointsRepartition.existskey(I)) { | ||||
| 					Points = PointsRepartition[I]; | ||||
| 				} else { | ||||
| 					Points = PointsRepartition[PointsRepartition.count - 1]; | ||||
| 				} | ||||
| 			} | ||||
| 			Scores::SetPlayerRoundPoints(Score, Points); | ||||
| 			I += 1; | ||||
| 		} else { | ||||
| 			Scores::SetPlayerRoundPoints(Score, 0); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| /** Check if the points limit was reached | ||||
|  * | ||||
|  *	@param	_UseTieBreak							Prevent ties or not | ||||
|  *	@param	_PointsLimit							Number of points to get to win the match | ||||
|  * | ||||
|  *	@return														C_PointsLimit_Reached if the points limit is reached | ||||
|  *																		C_PointsLimit_Tie if there is a tie | ||||
|  *																		C_PointsLimit_NotReached if the points limit is not reached | ||||
|  */ | ||||
|  Integer PointsLimitReached(Boolean _UseTieBreak, Integer _PointsLimit) { | ||||
| 	declare Integer MaxScore = -1; | ||||
| 	declare Boolean Tie = False; | ||||
| 	foreach (Score in Scores) { | ||||
| 		declare Integer Points = Scores::GetPlayerMatchPoints(Score); | ||||
| 		if (Points > MaxScore) { | ||||
| 			MaxScore = Points; | ||||
| 			Tie = False; | ||||
| 		} else if (Points == MaxScore) { | ||||
| 			Tie = True; | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	if (_UseTieBreak && Tie) return C_PointsLimit_Tie; //< There is a tie and it is not allowed | ||||
| 	if (_PointsLimit > 0 && MaxScore >= _PointsLimit) return C_PointsLimit_Reached; //< There is a points limit and it is reached | ||||
| 	return C_PointsLimit_NotReached; //< There is no points limit or the points limit is not reached | ||||
| } | ||||
|  | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| /** Check if we should go to the next map | ||||
|  * | ||||
|  *	@param	_UseTieBreak							Prevent ties or not | ||||
|  *	@param	_PointsLimit							Number of points to get to win the match | ||||
|  *	@param	_ValidRoundsNb						Number of valid rounds played | ||||
|  *	@param	_RoundsPerMap							Number of rounds to play to complete the map | ||||
|  * | ||||
|  *	@return		True if it is the case, false otherwise | ||||
|  */ | ||||
|  Boolean MapIsOver(Boolean _UseTieBreak, Integer _PointsLimit, Integer _ValidRoundsNb, Integer _RoundsPerMap) { | ||||
| 	declare Integer PointsLimitReached = PointsLimitReached(_UseTieBreak, _PointsLimit); | ||||
| 	 | ||||
| 	Log::Log("""[Rounds] MapIsOver() > _UseTieBreak: {{{_UseTieBreak}}} | _PointsLimit: {{{_PointsLimit}}} | _ValidRoundsNb: {{{_ValidRoundsNb}}} | _RoundsPerMap: {{{_RoundsPerMap}}} | PointsLimitReached: {{{PointsLimitReached}}}"""); | ||||
| 	 | ||||
| 	if (PointsLimitReached == C_PointsLimit_Reached) return True; //< There is a points limit and it is reached | ||||
| 	if (_RoundsPerMap > 0 && _ValidRoundsNb >= _RoundsPerMap) return True; //< There is a rounds limit and it is reached | ||||
| 	 | ||||
| 	return False; | ||||
| } | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| /** Check if we should go to the next match | ||||
|  * | ||||
|  *	@param	_UseTieBreak							Prevent ties or not | ||||
|  *	@param	_PointsLimit							Number of points to get to win the match | ||||
|  *	@param	_MapsPerMatch							Number of maps to play to complete a match | ||||
|  *	@param	_RoundsPerMap							Number of rounds to play to complete the map | ||||
|  * | ||||
|  *	@return		True if it is the case, false otherwise | ||||
|  */ | ||||
|  Boolean MatchIsOver(Boolean _UseTieBreak, Integer _PointsLimit, Integer _MapCount, Integer _MapsPerMatch, Integer _RoundsPerMap, Boolean _MapSkipped) { | ||||
| 	declare Integer PointsLimitReached = PointsLimitReached(_UseTieBreak, _PointsLimit); | ||||
| 	 | ||||
| 	Log::Log("""[Rounds] MatchIsOver() > _UseTieBreak: {{{_UseTieBreak}}} | _PointsLimit: {{{_PointsLimit}}} | _MapCount: {{{_MapCount}}} | _MapsPerMatch: {{{_MapsPerMatch}}} | _RoundsPerMap: {{{_RoundsPerMap}}} | PointsLimitReached: {{{PointsLimitReached}}} | _MapSkipped : {{{_MapSkipped}}}"""); | ||||
|  | ||||
| 	// If there is a point limit and it is reached, stop the match | ||||
| 	if (PointsLimitReached == C_PointsLimit_Reached) { | ||||
| 		return True; | ||||
| 	} | ||||
| 	// If there is an explicit maps limit ... | ||||
| 	else if (_MapsPerMatch >= 1) { | ||||
| 		if ( | ||||
| 			(_MapCount >= _MapsPerMatch && PointsLimitReached != C_PointsLimit_Tie) || //< ... stop the match if the maps limit is reached and the match is not a tie | ||||
| 			(_MapSkipped && _MapsPerMatch == 1 && _MapCount >= _MapsPerMatch) //< ... stop the match if the map was skipped and the match is played on only one map | ||||
| 		) { | ||||
| 			return True; | ||||
| 		} | ||||
| 	} | ||||
| 	// If there is a rounds limit but no maps limit, continue to play until another limit is reached | ||||
| 	else if (_RoundsPerMap >= 1) {	 | ||||
| 		return False; | ||||
| 	} | ||||
| 	// If there is neither a points limit nor a rounds limit, always stop the match at the end of the first map, even if there is a tie | ||||
| 	else { | ||||
| 		return True; | ||||
| 	} | ||||
| 	 | ||||
| 	// In all other cases continue to play | ||||
| 	return False; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user