Compare commits

..

17 Commits

Author SHA1 Message Date
beu
dcd89d584d use SmallScoresTable to display delta 2025-05-08 09:36:08 +02:00
beu
da04fba158 fix round count with pause or skipped round 2025-02-16 01:20:17 +01:00
beu
a0496073da fix missing constant 2025-02-14 16:51:16 +01:00
beu
ab7dd77649 fix first eliminated score to be reseted 2025-02-09 10:49:51 +01:00
beu
b57aaa5179 fix useless update when cam 7 2025-01-30 16:56:31 +01:00
beu
26cc4f63d9 bump version 2025-01-18 14:03:59 +01:00
beu
80e4641eb9 prevent loosing life en skipping map 2025-01-18 14:03:37 +01:00
beu
e2dc0d5d73 improve wording 2025-01-18 14:03:20 +01:00
beu
b646019307 add support of eliminations based on number of remaining lives 2025-01-18 11:22:06 +01:00
beu
80003e1a39 do not increase round counter on skip 2025-01-18 10:36:06 +01:00
beu
deea33e0bd Various changes:
- Add a setting to display same time message, disabled by default
- Add a message when player is at 001 of the perfect time
- Display the perfect time message instantly
2024-11-29 17:36:35 +01:00
beu
4dea966448 fix sentences 2024-08-27 09:53:42 +02:00
beu
7891c33934 improve line visibility 2024-08-26 12:27:18 +02:00
beu
e6aa4049a1 Add alternative position 2024-08-25 19:29:52 +02:00
beu
5a9d8abc82 keep live ranking displayed longer after the round 2024-08-25 18:46:31 +02:00
beu
a3f042e7f7 fix possible live ranking crash 2024-08-25 18:42:23 +02:00
beu
fba93075b0 fix sorting of the Scorestable during the round 2024-08-25 18:42:05 +02:00
2 changed files with 164 additions and 57 deletions

View File

@ -4,7 +4,7 @@
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
#Const Version "2024-08-25"
#Const Version "2025-02-15"
#Const ScriptName "Modes/TM2020-Gamemodes/TM_MultiLivesKnockout.Script.txt"
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
@ -38,12 +38,14 @@
#Setting S_EliminatedPlayersNbRanks "4,16,16" as _("Nb of players above which one extra elim. /round")
#Setting S_RoundsWithoutElimination 1 as _("Rounds without elimination")
#Setting S_EliminatePerRounds False as "Eliminate par rounds instead of per players alive"
#Setting S_EliminationMode 0 as "0 per player / 1 per lives / 2 per round"
#Setting S_MaximumLives 3
#Setting S_MatchName "Final"
#Setting S_AlternativeMatchInfosPosition False
/* About S_EliminatedPlayersNbRanks and S_EliminatePerRounds.
* If S_EliminatePerRounds is True, it will decrease the number of lose of life.
/* About S_EliminatedPlayersNbRanks.
* If S_EliminationMode is 2, it will decrease the number of lose of life.
* Example : "8,16"
* Round 1 to 7 -> 3 eliminations per round
* Round 8 to 15 -> 2 eliminations per round
@ -61,7 +63,8 @@
* Example : ""
* 1 elimination per round
*
* If S_EliminatePerRounds is False, it will work like the official
* If S_EliminationMode is 1, it will sum number of remaining lives and apply official system on it
* If S_EliminationMode is 0, it will work like the official Knockout Mode
*/
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
@ -75,6 +78,10 @@
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/Knockout.Script.txt" //< Url of the mania app
#Const C_FakeUsersNb 0
#Const C_ElimMode_Rounds 2
#Const C_ElimMode_Lives 1
#Const C_ElimMode_Official 0
#Const C_Callback_Elimination "Trackmania.Knockout.Elimination"
#Const C_Callback_LostLife "Trackmania.Knockout.LostLife"
@ -463,13 +470,11 @@ if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.1.1")) {
Scores::XmlRpc_SendScores(Scores::C_Section_PreEndRound, "");
}
DisplayLiveRanking(False);
declare Text[] LostLifeAccountIds;
declare Text[] EliminatedAccountIds;
declare Integer[] EliminatedRanks;
if (Round_ForceEndRound || Round_SkipPauseRound) {
if (Round_ForceEndRound || Round_SkipPauseRound || Round_Skipped) {
// Cancel points
foreach (Score in Scores) {
Scores::SetPlayerRoundPoints(Score, 0);
@ -479,6 +484,7 @@ if (Round_ForceEndRound || Round_SkipPauseRound) {
ForcedEndRoundSequence();
}
MB_SetValidRound(False);
Match_CurrentRoundNb -= 1;
} else {
// Eliminate players that did not finish in time
declare Ident[] EliminatedPlayersScoresIds = []; // Score.Id
@ -488,7 +494,6 @@ if (Round_ForceEndRound || Round_SkipPauseRound) {
Match_RegistrationIsOpen = False;
// Eliminate last players
Race::SortScores(Race::C_Sort_PrevRaceTime);
declare Integer ParticipantsNb = GetParticipantsNb();
declare Integer AliveScoresNb = GetAliveScoresNb();
declare Integer LossOfLifeNb = GetLossOfLifeNb(Match_CurrentRoundNb, AliveScoresNb);
@ -505,7 +510,8 @@ if (Round_ForceEndRound || Round_SkipPauseRound) {
LostLifeAccountIds.add(Score.User.WebServicesUserId);
if (!ScoreIsAlive(Score)) {
Scores::SetPlayerMatchPoints(Score, ParticipantsNb - Rank);
// Registered Score must have at least 1 point or a best race time to not be reset at end of the map: https://forum.nadeo.com/viewtopic.php?t=4365
Scores::SetPlayerMatchPoints(Score, ParticipantsNb + 1 - Rank);
EliminatedAccountIds.add(Score.User.WebServicesUserId);
EliminatedRanks.add(Rank);
@ -554,6 +560,7 @@ if (Round_ForceEndRound || Round_SkipPauseRound) {
MB_Sleep(S_ChatTime / 2 * 1000);
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal;
}
DisplayLiveRanking(False);
}
***
@ -690,6 +697,19 @@ Integer GetAliveScoresNb() {
return AliveScoresNb;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
/** Number of lives cumulated
*
* @return Number of lives cumulated
*/
Integer GetRemainingLivesNb() {
declare Integer LivesNb;
foreach (Score in Scores) {
LivesNb += GetScoreRemainingLives(Score);
}
return LivesNb;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
/** Compute Lose of Life Milestones
*
@ -715,14 +735,20 @@ Integer[] GetAllMilestones() {
*/
Integer GetNextMilestone(Integer _RoundNb, Integer _AliveScoresNb) {
if (_AliveScoresNb <= 0) return 0;
declare Integer ThresholdValue = _AliveScoresNb;
if (S_EliminationMode == C_ElimMode_Lives) {
ThresholdValue = GetRemainingLivesNb();
}
declare Integer[] Milestones = GetAllMilestones();
declare Integer NextMilestone = 0;
foreach (Milestone in Milestones) {
if (S_EliminatePerRounds && Milestone > _RoundNb) {
if (S_EliminationMode == C_ElimMode_Rounds && Milestone > _RoundNb) {
NextMilestone = Milestone;
break;
} else if (!S_EliminatePerRounds && Milestone < _AliveScoresNb) { // could be optimized but can change original behavior
} else if ((S_EliminationMode == C_ElimMode_Official || S_EliminationMode == C_ElimMode_Lives) && Milestone < ThresholdValue) { // could be optimized but can change original behavior
NextMilestone = Milestone;
}
}
@ -739,11 +765,17 @@ Integer GetNextMilestone(Integer _RoundNb, Integer _AliveScoresNb) {
Integer GetLossOfLifeNb(Integer _RoundNb, Integer _AliveScoresNb) {
if (_AliveScoresNb <= 1) return 0;
if (_RoundNb <= S_RoundsWithoutElimination) return 0;
declare Integer ThresholdValue = _AliveScoresNb;
if (S_EliminationMode == C_ElimMode_Lives) {
ThresholdValue = GetRemainingLivesNb();
}
declare Integer[] Milestones = GetAllMilestones();
declare Integer NumberOfElimination;
if (S_EliminatePerRounds) {
if (S_EliminationMode == C_ElimMode_Rounds) {
Milestones = Milestones.slice(1);
NumberOfElimination = 1;
foreach (Milestone in Milestones) {
@ -751,10 +783,10 @@ Integer GetLossOfLifeNb(Integer _RoundNb, Integer _AliveScoresNb) {
NumberOfElimination += 1;
}
}
} else {
} else if (S_EliminationMode == C_ElimMode_Official || S_EliminationMode == C_ElimMode_Lives) {
declare Integer RoundMinEliminations = Milestones.count + 1;
foreach (Index => Milestone in Milestones) {
if (Milestone < _AliveScoresNb) {
if (Milestone < ThresholdValue) {
RoundMinEliminations = Index + 1;
}
}
@ -842,7 +874,6 @@ Text GetScoreRemainingLivesText(CSmScore _Score) {
Void UpdateCustomRanking(Boolean _DisplayTimes) {
declare Text[] AccountIdsToDisplay = [];
declare Text[][Text] CustomPoints = [];
declare Integer[Text] CustomTimes = [];
declare Integer ParticipantsNb = GetParticipantsNb();
declare Integer AliveScoresNb = GetAliveScoresNb();
@ -853,11 +884,11 @@ Void UpdateCustomRanking(Boolean _DisplayTimes) {
if (!ScoreIsRegistered(Score)) continue;
AccountIdsToDisplay.add(Score.User.WebServicesUserId);
if (!ScoreIsAlive(Score)) continue;
Scores::SetPlayerMatchPoints(Score, Scores.count + ParticipantsNb - Index);
if (_DisplayTimes && Score.PrevRaceTimes.count == 0) {
CustomTimes[Score.User.WebServicesUserId] = 0; //@ prev race is updated automatically at the moment (22/10/20) so we need to use this
} else if (!_DisplayTimes) {
if (_DisplayTimes) {
Scores::SetPlayerMatchPoints(Score, ParticipantsNb + ParticipantsNb - Index);
} else {
Scores::SetPlayerMatchPoints(Score, ParticipantsNb + GetScoreRemainingLives(Score));
CustomPoints[Score.User.WebServicesUserId] = [GetScoreRemainingLivesText(Score)];
}
if (AliveScoresNb == 1 && ParticipantsNb > 1) {
@ -869,7 +900,9 @@ Void UpdateCustomRanking(Boolean _DisplayTimes) {
declare Text[Text] CustomRanks = [];
Race::SortScores(Race::C_Sort_TotalPoints);
foreach (Score in Scores) {
if (ScoreIsAlive(Score)) {
if (AliveScoresNb == 1 && ScoreIsAlive(Score)) {
CustomRanks[Score.User.WebServicesUserId] = "1";
} else if (ScoreIsAlive(Score)) {
CustomRanks[Score.User.WebServicesUserId] = "-";
} else if (ScoreIsRegistered(Score)) {
CustomRanks[Score.User.WebServicesUserId] = ""^Rank;
@ -881,7 +914,6 @@ Void UpdateCustomRanking(Boolean _DisplayTimes) {
UIModules_ScoresTable::SetCustomRanks(CustomRanks);
UIModules_ScoresTable::DisplayOnly(AccountIdsToDisplay); // Display only registered players
UIModules_ScoresTable::SetCustomPoints(CustomPoints);
UIModules_ScoresTable::SetCustomTimes(CustomTimes);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
@ -926,7 +958,7 @@ Boolean MatchIsOver(Integer _RoundNb) {
*/
Boolean MapIsOver(Integer _RoundNb) {
if (MatchIsOver(_RoundNb)) return True;
if (S_RoundsPerMap > 0 && MB_GetRoundCount() >= S_RoundsPerMap) return True; //< There is a rounds limit and it is reached
if (S_RoundsPerMap > 0 && MB_GetValidRoundCount() >= S_RoundsPerMap) return True; //< There is a rounds limit and it is reached
return False;
}
@ -975,8 +1007,8 @@ Void UpdateMatchInfos(Integer _CurrentRoundNb, Integer _LossOfLife, Integer _Ali
if (MapNumber == 0) MapNumber = 3;
if (_CurrentRoundNb == -1) MatchInfo = "Warm Up";
else if (S_EliminatePerRounds) MatchInfo = "Match Round "^ _CurrentRoundNb ^ " - Round "^ ML::Max(MB_GetRoundCount(), 1) ^"/"^ S_RoundsPerMap ^" of Map "^ MapNumber ^"/"^ MapList.count;
else MatchInfo = "Round "^ ML::Max(MB_GetRoundCount(), 1) ^"/"^ S_RoundsPerMap ^ " - Map "^ MapNumber ^"/"^ MapList.count;
else if (S_EliminationMode == C_ElimMode_Rounds) MatchInfo = "Match Round "^ _CurrentRoundNb ^ " - Round "^ ML::Max(MB_GetValidRoundCount(), 1) ^"/"^ S_RoundsPerMap ^" of Map "^ MapNumber ^"/"^ MapList.count;
else MatchInfo = "Round "^ ML::Max(MB_GetValidRoundCount(), 1) ^"/"^ S_RoundsPerMap ^ " - Map "^ MapNumber ^"/"^ MapList.count;
} else if (_CurrentRoundNb == -1) {
MatchInfo = "Warm Up";
} else {
@ -987,17 +1019,20 @@ Void UpdateMatchInfos(Integer _CurrentRoundNb, Integer _LossOfLife, Integer _Ali
if (_LossOfLife == 0) {
MatchInfo ^= "\nNo loss of life";
ScoreTablesInfo ^= "\nNo loss of life this round";
} else if (S_EliminatePerRounds && _LossOfLife > 1 && _Milestone > 1) {
MatchInfo ^= "\n"^ _LossOfLife ^" lose of life until Match Round "^ _Milestone;
} else if (S_EliminationMode == C_ElimMode_Rounds && _LossOfLife > 1 && _Milestone > 1) {
MatchInfo ^= "\n"^ _LossOfLife ^" lives lost until Match Round "^ _Milestone;
ScoreTablesInfo ^= "\n"^ _LossOfLife ^" players will lose a life until Match Round "^ _Milestone;
} else if (!S_EliminatePerRounds && _LossOfLife > 1 && _Milestone > 1) {
MatchInfo ^= "\n"^ _LossOfLife ^" lose of life until "^ _Milestone ^" players are alive";
} else if (S_EliminationMode == C_ElimMode_Lives && _LossOfLife > 1 && _Milestone > 1) {
MatchInfo ^= "\n"^ _LossOfLife ^" lives lost until "^ _Milestone ^" lives left";
ScoreTablesInfo ^= "\n"^ _LossOfLife ^" players will lose a life until "^ _Milestone ^" lives left";
} else if (S_EliminationMode == C_ElimMode_Official && _LossOfLife > 1 && _Milestone > 1) {
MatchInfo ^= "\n"^ _LossOfLife ^" lives lost until "^ _Milestone ^" players are alive";
ScoreTablesInfo ^= "\n"^ _LossOfLife ^" players will lose a life until "^ _Milestone ^" players are alive";
} else if (_LossOfLife > 1) {
MatchInfo ^= "\n"^ _LossOfLife ^" lose of life";
MatchInfo ^= "\n"^ _LossOfLife ^" players will lose a life";
ScoreTablesInfo ^= "\n"^ _LossOfLife ^" players will lose a life per round";
} else {
MatchInfo ^= "\n1 lose of life";
MatchInfo ^= "\n1 player will lose a life";
ScoreTablesInfo ^= "\n1 player will lose a life";
}
}
@ -1022,6 +1057,13 @@ Void UpdateMatchName() {
Net_MultiLivesKnockout_LiveRanking_MatchName = S_MatchName;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// Update Match Infos
Void UpdateMatchInfosPosition() {
declare netwrite Boolean Net_MultiLivesKnockout_AlternativePosition for Teams[0];
Net_MultiLivesKnockout_AlternativePosition = S_AlternativeMatchInfosPosition;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// Update Number of lives
Void UpdateMaximumLives() {
@ -1048,6 +1090,7 @@ Void UpdateLossOfLife(Integer _LossOfLife) {
*/
Void UpdateInterfacesInfo(Integer _CurrentRoundNb, Integer _LossOfLife, Integer _AliveScoresNb) {
UpdateMatchInfos(_CurrentRoundNb, _LossOfLife, _AliveScoresNb, GetNextMilestone(_CurrentRoundNb, _AliveScoresNb));
UpdateMatchInfosPosition();
UpdateMatchName();
UpdateMaximumLives();
UpdateLossOfLife(_LossOfLife);
@ -1075,7 +1118,7 @@ Void LoadManialinks() {
<label id="label-player-time" pos="54 -2" z-index="2" size="10 5" halign="right" valign="center" textsize="2" textfont="Nadeo/Trackmania/BebasNeueRegular" textcolor="ffffff"/>
</framemodel>
<frame id="frame-global" hidden="1">
<frame id="frame-matchinfo" pos="-160 80">
<frame id="frame-matchinfo" pos="-160 80" hidden="1">
<quad size="55 18" bgcolor="000000" opacity="0.6"/>
<label id="label-matchinfo-name" pos="3 -7.8" size="49 6" valign="bottom" textsize="3.5" textfont="GameFontExtraBold" textcolor="ffffff" text=""/>
<label id="label-matchinfo-info" pos="3 -8.8" size="49 8" textsize="1.3" maxline="2" linespacing="1.1" textfont="GameFontSemiBold" textcolor="ffffff" text="" />
@ -1186,16 +1229,23 @@ Void ToggleUI(Boolean _Display, Boolean _UseAnimation) {
// Update Match info
Void UpdateMatchInfo() {
declare CMlFrame Frame <=> (Page.GetFirstChild("frame-matchinfo") as CMlFrame);
declare netread Text Net_MultiLivesKnockout_LiveRanking_MatchName for Teams[0];
Frame.Visible = (Net_MultiLivesKnockout_LiveRanking_MatchName != "");
declare CMlLabel Label_Name <=> (Frame.GetFirstChild("label-matchinfo-name") as CMlLabel);
declare CMlLabel Label_Info <=> (Frame.GetFirstChild("label-matchinfo-info") as CMlLabel);
declare netread Text Net_MultiLivesKnockout_LiveRanking_MatchName for Teams[0];
declare netread Boolean Net_MultiLivesKnockout_AlternativePosition for Teams[0];
if (Net_MultiLivesKnockout_AlternativePosition) Frame.RelativePosition_V3.Y = 50.;
else Frame.RelativePosition_V3.Y = 80.;
Label_Name.Value = Net_MultiLivesKnockout_LiveRanking_MatchName;
Tools::FitLabelValue(Label_Name, 3.5, 2., .2);
declare netread Text Net_MultiLivesKnockout_LiveRanking_MatchInfo for Teams[0];
Label_Info.Value = Net_MultiLivesKnockout_LiveRanking_MatchInfo;
Tools::FitLabelValue(Label_Info, 1.3, .8, .1);
Tools::FitLabelValue(Label_Info, 1.3, .4, .1);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
@ -1332,15 +1382,17 @@ Void UpdateRanking() {
} else {
declare Ident PlayerId = ScoreIdToPlayerId.get(Score.Id, NullId);
if (PlayerId != NullId) {
declare CSmPlayer Player <=> Players[PlayerId];
PlayerState.CPNb = Player.RaceWaypointTimes.count;
PlayerState.IsNotPlaying = (Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned);
PlayerState.Finished = (Score.PrevRaceTimes.count != 0);
if (PlayerState.CPNb > 0) {
PlayerState.LastCPTime = Player.RaceWaypointTimes[-1];
declare CSmPlayer Player <=> Players.get(PlayerId, Null);
if (Player != Null) {
PlayerState.CPNb = Player.RaceWaypointTimes.count;
PlayerState.IsNotPlaying = (Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned);
PlayerState.Finished = (Score.PrevRaceTimes.count != 0);
if (PlayerState.CPNb > 0) {
PlayerState.LastCPTime = Player.RaceWaypointTimes[-1];
}
if (GlobalLastCPTime < PlayerState.LastCPTime) GlobalLastCPTime = PlayerState.LastCPTime;
}
if (GlobalLastCPTime < PlayerState.LastCPTime) GlobalLastCPTime = PlayerState.LastCPTime;
}
}
@ -1393,7 +1445,10 @@ Void UpdateRanking() {
PlayerState.InDanger = (Rank >= LostOfLifeRank);
if (FirstPlayer == Null) {
FirstPlayer <=> Players[ScoreIdToPlayerId.get(PlayerState.ScoreId, NullId)];
declare Ident FirstPlayerId = ScoreIdToPlayerId.get(PlayerState.ScoreId, NullId);
if (FirstPlayerId != NullId) {
FirstPlayer <=> Players[FirstPlayerId];
}
}
if (FirstPlayer != Null) {
@ -1582,7 +1637,7 @@ main() {
}
if (GUIPlayer == Null && Last_GUIPlayer != NullId) {
Last_GUIPlayer == NullId;
Last_GUIPlayer = NullId;
UpdateLiveRanking = True;
} else if (GUIPlayer != Null && Last_GUIPlayer != GUIPlayer.Id) {
Last_GUIPlayer = GUIPlayer.Id;

View File

@ -5,7 +5,7 @@
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
#Const Version "2024-04-11"
#Const Version "2025-05-08"
#Const ScriptName "Modes/TM2020-Gamemodes/TM_RoundsNearest.Script.txt"
// #RequireContext CSmMode
@ -21,6 +21,7 @@
#Include "Libs/Nadeo/TMGame/Utils/Tracking.Script.txt" as Tracking
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/Checkpoint_Server.Script.txt" as UIModules_Checkpoint
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/PauseMenuOnline_Server.Script.txt" as UIModules_PauseMenu_Online
#Include "Libs/Nadeo/Trackmania/Modes/Rounds/UIModules/SmallScoresTable_Server.Script.txt" as UIModules_SmallScoresTable
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// Settings
@ -33,6 +34,7 @@
#Setting S_PointsRepartition "5,4,3,2,1,0"
#Setting S_RoundsPerMap 8 as _("Number of rounds per track") ///< Number of round to play on one map before going to the next one
#Setting S_TargetTime 20000
#Setting S_SendSameTimeMessage False
#Setting S_WarmUpDuration 30 as _("Duration of one warm up")
#Setting S_WarmUpNb 1 as _("Number of warm up")
@ -67,6 +69,7 @@ Log::RegisterScript(ScriptName, Version);
Log::RegisterScript(Semver::ScriptName, Semver::Version);
Log::RegisterScript(ModeUtils::ScriptName, ModeUtils::Version);
Log::RegisterScript(StateMgr::ScriptName, StateMgr::Version);
Log::RegisterScript(UIModules_SmallScoresTable::ScriptName, UIModules_SmallScoresTable::Version);
***
***Match_LoadLibraries***
@ -111,7 +114,7 @@ UIManager.UIAll.OverlayHideSpectatorInfos = True;
UIManager.UIAll.OverlayHideCountdown = True;
UIModules_ScoresTable::DisplayRoundPoints(True);
UIModules::UnloadModules(["UIModule_Race_TimeGap", "UIModule_Rounds_SmallScoresTable"]);
UIModules::UnloadModules(["UIModule_Race_TimeGap"]);
***
***Match_Yield***
@ -163,6 +166,8 @@ Server_MapsPerMatch = S_MapsPerMatch - 1;
***Match_StartMatch***
***
UIModules_ScoresTable::SetCustomPoints([]);
UIModules_SmallScoresTable::ResetCustomTimes();
UIModules_SmallScoresTable::ResetCustomResults();
***
***Match_InitMap***
@ -201,6 +206,13 @@ foreach (Score in Scores) {
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb);
StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]);
UIModules_ScoresTable::SetCustomPoints(GetWinnersCustomPoints());
foreach (Score in Scores) {
declare Boolean RoundsNearest_AlmostPerfectTime_MessageSent for Score;
RoundsNearest_AlmostPerfectTime_MessageSent = False;
declare Boolean RoundsNearest_DidPerfectTime_MessageSent for Score;
RoundsNearest_DidPerfectTime_MessageSent = False;
}
***
***Rounds_PlayerSpawned***
@ -306,6 +318,8 @@ if (Round_ForceEndRound || Round_SkipPauseRound || Round_Skipped) {
MB_StopMap();
}
}
UIModules_SmallScoresTable::ResetCustomTimes();
UIModules_SmallScoresTable::ResetCustomResults();
***
***Match_EndMap***
@ -448,13 +462,15 @@ Text[][Text] GetWinnersCustomPoints() {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
/// Compute the latest race scores
Void ComputeLatestRaceScores(Boolean _DisplayMessages) {
Void ComputeLatestRaceScores(Boolean _IsEndRound) {
Race::SortScores(Race::C_Sort_PrevRaceTime);
// Points distributed between all players
declare Text[][Text] CustomPoints = GetWinnersCustomPoints();
declare CSmScore[][Integer] ScoresPerAbsoluteDelta;
declare Integer[Text] CustomTimes;
declare Text[Text] CustomResult;
foreach (Score in Scores) {
if (Score.User == Null) continue;
if (Scores::GetPlayerPrevRaceTime(Score) <= 0) continue;
@ -470,6 +486,12 @@ Void ComputeLatestRaceScores(Boolean _DisplayMessages) {
TextDelta ^= DeltaTimeToText(AbsoluteDelta);
if (S_PointsLimit < 0 || Scores::GetPlayerMatchPoints(Score) < S_PointsLimit) {
CustomPoints[Score.User.WebServicesUserId] = [Scores::GetPlayerMatchPoints(Score) ^ " (" ^ TextDelta ^ ")"];
if (AbsoluteDelta == 0) {
CustomResult[Score.User.WebServicesUserId] = "PERFECT";
} else {
CustomResult[Score.User.WebServicesUserId] = TextDelta;
}
CustomTimes[Score.User.WebServicesUserId] = AbsoluteDelta;
}
}
UIModules_ScoresTable::SetCustomPoints(CustomPoints);
@ -478,12 +500,12 @@ Void ComputeLatestRaceScores(Boolean _DisplayMessages) {
declare Integer I = 0;
declare Integer[] PointsRepartition = PointsRepartition::GetPointsRepartition();
declare Text Names;
foreach (Delta => CustomScores in ScoresPerAbsoluteDelta) {
// Attribute less points if they have the same time
if (_DisplayMessages && CustomScores.count > 1) {
if (S_SendSameTimeMessage && _IsEndRound && CustomScores.count > 1) {
I += CustomScores.count - 1;
declare Text Names;
foreach (Key => Score in CustomScores) {
if (Key == 0) {
Names = FormatPlayerName(Score.User.Name);
@ -494,8 +516,12 @@ Void ComputeLatestRaceScores(Boolean _DisplayMessages) {
}
}
UIManager.UIAll.SendChat("$ff3" ^ Names^ " have the same time");
}
foreach (Score in CustomScores) {
}
Names = "";
declare Boolean UpdateBigMessage;
foreach (Key => Score in CustomScores) {
declare Integer Points;
if (PointsRepartition.existskey(I)) {
@ -504,19 +530,45 @@ Void ComputeLatestRaceScores(Boolean _DisplayMessages) {
Points = PointsRepartition[PointsRepartition.count - 1];
}
if (Delta == 0 && CustomScores.count == 1) {
Points += S_BonusForPerfect;
if (_DisplayMessages) {
if (Delta == 0) {
declare Boolean RoundsNearest_DidPerfectTime_MessageSent for Score;
if (!RoundsNearest_DidPerfectTime_MessageSent) {
RoundsNearest_DidPerfectTime_MessageSent = True;
UpdateBigMessage = True;
UIManager.UIAll.SendChat("$ff3" ^ FormatPlayerName(Score.User.Name) ^ " did the perfect time");
UIModules_BigMessage::SetMessage(FormatPlayerName(Score.User.Name) ^ " did the perfect time");
ModeUtils::PlaySound(CUIConfig::EUISound::TieBreakPoint, 0);
}
}
if (Key == 0) {
Names = FormatPlayerName(Score.User.Name);
} else if (Key == CustomScores.count - 1) {
Names ^= " and " ^ FormatPlayerName(Score.User.Name);
} else {
Names ^= ", " ^ FormatPlayerName(Score.User.Name);
}
if (CustomScores.count == 1) {
Points += S_BonusForPerfect;
}
} else if (Delta == 1) {
declare Boolean RoundsNearest_AlmostPerfectTime_MessageSent for Score;
if (!RoundsNearest_AlmostPerfectTime_MessageSent) {
RoundsNearest_AlmostPerfectTime_MessageSent = True;
UIManager.UIAll.SendChat("$ff3" ^ FormatPlayerName(Score.User.Name) ^ " HAH");
}
}
Scores::SetPlayerRoundPoints(Score, Points);
}
if (UpdateBigMessage) {
UIModules_BigMessage::SetMessage(Names ^ " did the perfect time");
}
I += 1;
}
UIModules_SmallScoresTable::SetCustomTimes(CustomTimes);
UIModules_SmallScoresTable::SetCustomResults(CustomResult);
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //