594 lines
19 KiB
Plaintext
594 lines
19 KiB
Plaintext
/**
|
|
* Time Attack Rounds mode
|
|
* Quick script ordered by Bergie for Telialigaen
|
|
*/
|
|
// #RequireContext CSmMode
|
|
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
|
|
|
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
|
#Const Version "2023-09-25"
|
|
#Const ScriptName "Modes/TrackMania/TM_TimeAttackRounds_Online.Script.txt"
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
// Libraries
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
#Include "TextLib" as TL
|
|
#Include "MathLib" as ML
|
|
#Include "Libs/Nadeo/CMGame/Utils/Task.Script.txt" as Task
|
|
#Include "Libs/Nadeo/Trackmania/Modes/TimeAttack/StateManager.Script.txt" as StateMgr
|
|
#Include "Libs/Nadeo/Trackmania/Modes/TrophyRanking.Script.txt" as TrophyRanking
|
|
#Include "Libs/Nadeo/Trackmania/MainMenu/Constants.Script.txt" as MenuConsts
|
|
#Include "Libs/Nadeo/CMGame/Modes/Utils.Script.txt" as ModeUtils
|
|
#Include "Libs/Nadeo/CMGame/Utils/Semver.Script.txt" as Semver
|
|
|
|
// UI from Race
|
|
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/Checkpoint_Server.Script.txt" as UIModules_Checkpoint
|
|
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/ScoresTable_Server.Script.txt" as UIModules_ScoresTable
|
|
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/PauseMenuOnline_Server.Script.txt" as UIModules_PauseMenu_Online
|
|
#Include "Libs/Nadeo/Trackmania/Modes/TimeAttack/UIModules/EndMatchTrophy_Server.Script.txt" as UIModules_EndMatchTrophy
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
// Settings
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
#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/Trackmania/Modes/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 == "");
|
|
***
|
|
|
|
***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::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_MapsPerMatch;
|
|
***
|
|
|
|
***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);
|
|
|
|
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_StartRound***
|
|
***
|
|
StartTime = Now + Race::C_SpawnDuration;
|
|
UpdateScoresTableFooterAndTimeLimit(StartTime, S_TimeLimit, S_MapsPerMatch);
|
|
Race::SortScores(Race::C_Sort_BestRaceTime);
|
|
***
|
|
|
|
***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_MapsPerMatch != S_MapsPerMatch ) {
|
|
Map_TimeLimit = S_TimeLimit;
|
|
Map_MapsPerMatch = S_MapsPerMatch;
|
|
UpdateScoresTableFooterAndTimeLimit(StartTime, S_TimeLimit, S_MapsPerMatch);
|
|
}
|
|
if (Net_ScriptEnvironment != S_ScriptEnvironment) {
|
|
Net_ScriptEnvironment = S_ScriptEnvironment;
|
|
}
|
|
***
|
|
|
|
***Rounds_CheckStopRound***
|
|
***
|
|
// If time limit is reached
|
|
if (EndTime > 0 && Now >= EndTime) {
|
|
MB_StopRound();
|
|
Round_Skipped = False;
|
|
}
|
|
// If forced end round or round skipped after pause
|
|
if (Round_ForceEndRound || Round_SkipPauseRound) {
|
|
MB_StopRound();
|
|
Round_Skipped = False;
|
|
}
|
|
***
|
|
|
|
***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);
|
|
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);
|
|
|
|
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;
|
|
} |