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