Compare commits
40 Commits
5076df7f37
...
master
Author | SHA1 | Date | |
---|---|---|---|
dcd89d584d | |||
da04fba158 | |||
a0496073da | |||
ab7dd77649 | |||
b57aaa5179 | |||
26cc4f63d9 | |||
80e4641eb9 | |||
e2dc0d5d73 | |||
b646019307 | |||
80003e1a39 | |||
deea33e0bd | |||
4dea966448 | |||
7891c33934 | |||
e6aa4049a1 | |||
5a9d8abc82 | |||
a3f042e7f7 | |||
fba93075b0 | |||
a11e708c4d | |||
cf33391e7f | |||
0945702fdd | |||
fead1aa840 | |||
2f3b20c322 | |||
62fc7bbf83 | |||
8b267a6978 | |||
786bbea656 | |||
8abd261ef9 | |||
4425fc257e | |||
8b3a30e441 | |||
2ca013890a | |||
654c2b4dde | |||
685d6ad3fb | |||
86c36f306d | |||
4bafbc6de3 | |||
78519b8044 | |||
04b4137078 | |||
1e9c428fb0 | |||
1962e99ddf | |||
4b2f911c2c | |||
bade6e62c0 | |||
51273a46ef |
@ -4,7 +4,7 @@
|
|||||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
||||||
|
|
||||||
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||||
#Const Version "2023-10-16"
|
#Const Version "2024-05-08"
|
||||||
#Const ScriptName "Modes/TM2020-Gamemodes/LastManStanding.Script.txt"
|
#Const ScriptName "Modes/TM2020-Gamemodes/LastManStanding.Script.txt"
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
@ -26,7 +26,7 @@
|
|||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
#Setting S_ForceLapsNb 1
|
#Setting S_ForceLapsNb 1
|
||||||
#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_RoundsPerMap 1 as _("Number of rounds per map") ///< Number of round to play on one map before going to the next one
|
||||||
|
#Setting S_MapsPerMatch 4 as "Only used if S_KeepScoresBetweenRounds = True"
|
||||||
|
|
||||||
#Setting S_AFKIdleTime 120000 as "Time before being an AFK player will be kicked"
|
#Setting S_AFKIdleTime 120000 as "Time before being an AFK player will be kicked"
|
||||||
|
|
||||||
@ -36,6 +36,7 @@
|
|||||||
#Setting S_MalusEveryNSecs 10 as "Roll a new Malus every N Sec"
|
#Setting S_MalusEveryNSecs 10 as "Roll a new Malus every N Sec"
|
||||||
#Setting S_NextMalusPreparationTime 10 as "Time given to players to prepare them before a Malus"
|
#Setting S_NextMalusPreparationTime 10 as "Time given to players to prepare them before a Malus"
|
||||||
#Setting S_MalusDuration 5 as "Malus Duration"
|
#Setting S_MalusDuration 5 as "Malus Duration"
|
||||||
|
#Setting S_KeepScoresBetweenRounds False
|
||||||
|
|
||||||
#Setting S_TrustClientSimu False
|
#Setting S_TrustClientSimu False
|
||||||
#Setting S_UseCrudeExtrapolation False
|
#Setting S_UseCrudeExtrapolation False
|
||||||
@ -50,6 +51,8 @@
|
|||||||
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/Rounds.Script.txt" //< Url of the mania app
|
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/Rounds.Script.txt" //< Url of the mania app
|
||||||
#Const C_FakeUsersNb 0
|
#Const C_FakeUsersNb 0
|
||||||
|
|
||||||
|
#Const C_Callback_CustomChat_ChatMessage "CustomChat.GamemodeChatMessage"
|
||||||
|
|
||||||
#Const C_Malus_Reset 0
|
#Const C_Malus_Reset 0
|
||||||
#Const C_Malus_ForceEngine 1
|
#Const C_Malus_ForceEngine 1
|
||||||
#Const C_Malus_BackwardOnly 2
|
#Const C_Malus_BackwardOnly 2
|
||||||
@ -92,18 +95,31 @@ Log::RegisterScript(StateMgr::ScriptName, StateMgr::Version);
|
|||||||
***Match_LoadLibraries***
|
***Match_LoadLibraries***
|
||||||
***
|
***
|
||||||
StateMgr::Load();
|
StateMgr::Load();
|
||||||
|
|
||||||
|
XmlRpc::RegisterCallback(C_Callback_CustomChat_ChatMessage, """
|
||||||
|
* Name: {{{C_Callback_CustomChat_ChatMessage}}}
|
||||||
|
* Type: CallbackArray
|
||||||
|
* Description: Gamemode Chat Message for the Custom Chat plugin
|
||||||
|
* Data:
|
||||||
|
- Version >=2.0.0:
|
||||||
|
```
|
||||||
|
[
|
||||||
|
"This is a gamemode chat message"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
""");
|
||||||
***
|
***
|
||||||
|
|
||||||
***Match_UnloadLibraries***
|
***Match_UnloadLibraries***
|
||||||
***
|
***
|
||||||
StateMgr::Unload();
|
StateMgr::Unload();
|
||||||
|
XmlRpc::UnregisterCallback(C_Callback_CustomChat_ChatMessage);
|
||||||
***
|
***
|
||||||
|
|
||||||
***Match_Settings***
|
***Match_Settings***
|
||||||
***
|
***
|
||||||
MB_Settings_UseDefaultTimer = False;
|
MB_Settings_UseDefaultTimer = False;
|
||||||
MB_Settings_UseDefaultHud = (C_HudModulePath == "");
|
MB_Settings_UseDefaultHud = (C_HudModulePath == "");
|
||||||
MB_Settings_UseDefaultPodiumSequence = False;
|
|
||||||
Rounds_Settings_UseDefaultSpawnManagement = False;
|
Rounds_Settings_UseDefaultSpawnManagement = False;
|
||||||
MB_Settings_UseDefaultIntroSequence = False;
|
MB_Settings_UseDefaultIntroSequence = False;
|
||||||
***
|
***
|
||||||
@ -154,6 +170,7 @@ foreach (Event in UIManager.PendingEvents) {
|
|||||||
if (Event.CustomEventType == "LMS_NotifyAFK") {
|
if (Event.CustomEventType == "LMS_NotifyAFK") {
|
||||||
if (Event.CustomEventData.count > 0) {
|
if (Event.CustomEventData.count > 0) {
|
||||||
UIManager.UIAll.SendChat("$ff9" ^ Event.CustomEventData[0] ^ " has been kicked for being AFK");
|
UIManager.UIAll.SendChat("$ff9" ^ Event.CustomEventData[0] ^ " has been kicked for being AFK");
|
||||||
|
XmlRpc::SendCallback(C_Callback_CustomChat_ChatMessage, ["$ff9" ^ Event.CustomEventData[0] ^ " has been kicked for being AFK"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,6 +184,7 @@ StateMgr::Yield();
|
|||||||
Clans::SetClansNb(0);
|
Clans::SetClansNb(0);
|
||||||
UsePvPCollisions = True;
|
UsePvPCollisions = True;
|
||||||
UsePvECollisions = True;
|
UsePvECollisions = True;
|
||||||
|
Scores::SaveInScore(Scores::C_Points_Match);
|
||||||
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
||||||
WarmUp::SetAvailability(True);
|
WarmUp::SetAvailability(True);
|
||||||
CarRank::Reset();
|
CarRank::Reset();
|
||||||
@ -175,35 +193,8 @@ ResetNetworkVariables();
|
|||||||
|
|
||||||
***Match_InitMap***
|
***Match_InitMap***
|
||||||
***
|
***
|
||||||
declare Integer Map_ValidRoundsNb;
|
|
||||||
|
|
||||||
declare Integer Map_TimeBeforeMalus;
|
|
||||||
declare Integer Map_TimeBeforeNightmare;
|
|
||||||
declare Integer Map_MalusEveryNSecs;
|
|
||||||
declare Integer Map_NextMalusPreparationTime;
|
|
||||||
declare Integer Map_MalusDuration;
|
|
||||||
declare Integer Map_RoundsPerMap;
|
|
||||||
|
|
||||||
declare Text[] AccountIdsOfPlayers for This = [];
|
|
||||||
declare Integer LandmarkIndex for This = 0;
|
|
||||||
declare K_Malus[Text] MalusQueue;
|
|
||||||
|
|
||||||
declare Boolean ActiveMalus = False;
|
|
||||||
declare Boolean PendingMalus = False;
|
|
||||||
declare Integer NextStepMalusTime = 0;
|
|
||||||
declare Integer MalusIndex;
|
|
||||||
declare Integer MalusTime;
|
|
||||||
|
|
||||||
declare netwrite Boolean Net_DisplayUI for Teams[0] = False;
|
|
||||||
declare netwrite Integer Net_NBPlayers for Teams[0] = 0;
|
|
||||||
declare netwrite Integer Net_PlayersNbAlive for Teams[0] = 0;
|
|
||||||
declare netwrite Integer Net_NextMalus for Teams[0] = -1;
|
|
||||||
declare netwrite Integer Net_TimeBeforeMalus for Teams[0] = -1;
|
|
||||||
declare netwrite Integer Net_RoundsPerMap for Teams[0] = 0;
|
|
||||||
declare netwrite Integer Net_CurrentRoundNb for Teams[0] = 0;
|
|
||||||
|
|
||||||
ResetNetworkVariables();
|
ResetNetworkVariables();
|
||||||
UIModules_ScoresTable::SetCustomTimes([]);
|
ResetCustomPoints();
|
||||||
|
|
||||||
// Map Intro
|
// Map Intro
|
||||||
declare Boolean MapIsCompatible;
|
declare Boolean MapIsCompatible;
|
||||||
@ -218,6 +209,7 @@ if (!MapIsCompatible) {
|
|||||||
UIManager.UIAll.QueueMessage(3000, 1, CUIConfig::EMessageDisplay::Big, _("This map is not valid"));
|
UIManager.UIAll.QueueMessage(3000, 1, CUIConfig::EMessageDisplay::Big, _("This map is not valid"));
|
||||||
MB_Sleep(3000);
|
MB_Sleep(3000);
|
||||||
MB_StopMap();
|
MB_StopMap();
|
||||||
|
MB_SetValidMap(False);
|
||||||
} else if (S_IntroTime > 0) {
|
} else if (S_IntroTime > 0) {
|
||||||
declare netwrite Boolean Net_LMS_IsIntro for Teams[0] = False;
|
declare netwrite Boolean Net_LMS_IsIntro for Teams[0] = False;
|
||||||
Net_LMS_IsIntro = True;
|
Net_LMS_IsIntro = True;
|
||||||
@ -250,13 +242,39 @@ CarRank::Reset();
|
|||||||
|
|
||||||
***Match_InitRound***
|
***Match_InitRound***
|
||||||
***
|
***
|
||||||
declare Boolean ThrottleUpdate;
|
declare Integer Round_TimeBeforeMalus;
|
||||||
|
declare Integer Round_TimeBeforeNightmare;
|
||||||
|
declare Integer Round_MalusEveryNSecs;
|
||||||
|
declare Integer Round_NextMalusPreparationTime;
|
||||||
|
declare Integer Round_MalusDuration;
|
||||||
|
declare Integer Round_RoundsPerMap;
|
||||||
|
|
||||||
|
declare Text[] Round_AccountIdsOfEliminated;
|
||||||
|
|
||||||
|
declare Text[] LMS_AccountIdsOfPlayers for This = [];
|
||||||
|
declare Integer LMS_LandmarkIndex for This = 0;
|
||||||
|
|
||||||
|
declare netwrite Boolean Net_DisplayUI for Teams[0] = False;
|
||||||
|
declare netwrite Integer Net_NBPlayers for Teams[0] = 0;
|
||||||
|
declare netwrite Integer Net_PlayersNbAlive for Teams[0] = 0;
|
||||||
|
declare netwrite Integer Net_NextMalus for Teams[0] = -1;
|
||||||
|
declare netwrite Integer Net_TimeBeforeMalus for Teams[0] = -1;
|
||||||
|
declare netwrite Integer Net_RoundsPerMap for Teams[0] = 0;
|
||||||
|
declare netwrite Integer Net_CurrentRoundNb for Teams[0] = 0;
|
||||||
|
|
||||||
|
declare Boolean Round_ThrottleUpdate = False;
|
||||||
|
declare Boolean Round_ActiveMalus = False;
|
||||||
|
declare Boolean Round_PendingMalus = False;
|
||||||
|
declare Integer Round_NextStepMalusTime = 0;
|
||||||
|
declare Integer Round_MalusIndex = 0;
|
||||||
|
declare Integer Round_MalusTime = 0;
|
||||||
|
declare K_Malus[Text] Round_MalusQueue;
|
||||||
***
|
***
|
||||||
|
|
||||||
***Match_StartRound***
|
***Match_StartRound***
|
||||||
***
|
***
|
||||||
Scores::Clear();
|
UIModules_ScoresTable::SetScoreMode(UIModules_ScoresTable::C_Mode_Points);
|
||||||
UIModules_ScoresTable::SetCustomTimes([]);
|
Race::SortScores(Race::C_Sort_RoundPoints);
|
||||||
|
|
||||||
declare netwrite Integer Net_LMS_AFKIdleTime for Teams[0] = 120000;
|
declare netwrite Integer Net_LMS_AFKIdleTime for Teams[0] = 120000;
|
||||||
Net_LMS_AFKIdleTime = S_AFKIdleTime;
|
Net_LMS_AFKIdleTime = S_AFKIdleTime;
|
||||||
@ -269,29 +287,29 @@ while (Players.count < 2 && Now < (StartMapTime + 3000)) {
|
|||||||
|
|
||||||
// Initialize race
|
// Initialize race
|
||||||
StartTime = Now + Race::C_SpawnDuration;
|
StartTime = Now + Race::C_SpawnDuration;
|
||||||
Map_TimeBeforeMalus = S_TimeBeforeMalus;
|
Round_TimeBeforeMalus = S_TimeBeforeMalus;
|
||||||
Map_TimeBeforeNightmare = S_TimeBeforeNightmare;
|
Round_TimeBeforeNightmare = S_TimeBeforeNightmare;
|
||||||
Map_MalusEveryNSecs = S_MalusEveryNSecs;
|
Round_MalusEveryNSecs = S_MalusEveryNSecs;
|
||||||
Map_NextMalusPreparationTime = S_NextMalusPreparationTime;
|
Round_NextMalusPreparationTime = S_NextMalusPreparationTime;
|
||||||
Map_MalusDuration = S_MalusDuration;
|
Round_MalusDuration = S_MalusDuration;
|
||||||
Map_RoundsPerMap = S_RoundsPerMap;
|
Round_RoundsPerMap = S_RoundsPerMap;
|
||||||
UpdateScoresTableFooter();
|
UpdateScoresTableFooter();
|
||||||
MalusTime = GetTimeBeforeMalus(StartTime, S_TimeBeforeMalus, S_TimeBeforeNightmare);
|
Round_MalusTime = GetTimeBeforeMalus(StartTime, S_TimeBeforeMalus, S_TimeBeforeNightmare);
|
||||||
Net_DisplayUI = True;
|
Net_DisplayUI = True;
|
||||||
Net_TimeBeforeMalus = MalusTime;
|
Net_TimeBeforeMalus = Round_MalusTime;
|
||||||
Net_NextMalus = -1;
|
Net_NextMalus = -1;
|
||||||
Net_RoundsPerMap = Map_RoundsPerMap;
|
Net_RoundsPerMap = Round_RoundsPerMap;
|
||||||
Net_CurrentRoundNb = Map_ValidRoundsNb + 1;
|
Net_CurrentRoundNb = MB_GetValidRoundCount();
|
||||||
MalusQueue = [];
|
ResetCustomPoints();
|
||||||
|
|
||||||
// Spawn players for the race
|
// Spawn players for the race
|
||||||
---Rounds_CanSpawn---
|
---Rounds_CanSpawn---
|
||||||
|
|
||||||
declare Text[] AccountIdsOfPlayers for This = [];
|
declare Text[] LMS_AccountIdsOfPlayers for This = [];
|
||||||
|
|
||||||
declare CMapLandmark PlayerLM;
|
declare CMapLandmark PlayerLM;
|
||||||
declare Integer LandmarkIndex for This = 0;
|
declare Integer LMS_LandmarkIndex for This = 0;
|
||||||
AccountIdsOfPlayers = [];
|
LMS_AccountIdsOfPlayers = [];
|
||||||
|
|
||||||
// Suffle Players list to randomise spawn
|
// Suffle Players list to randomise spawn
|
||||||
declare CSmPlayer[Integer] ShuffledPlayers;
|
declare CSmPlayer[Integer] ShuffledPlayers;
|
||||||
@ -313,28 +331,29 @@ foreach (Player in ShuffledPlayers) {
|
|||||||
if (Player == Null) continue;
|
if (Player == Null) continue;
|
||||||
PlayerLM = Null;
|
PlayerLM = Null;
|
||||||
while (PlayerLM == Null) {
|
while (PlayerLM == Null) {
|
||||||
if (LandmarkIndex > Landmarks.count - 1 ) {
|
if (LMS_LandmarkIndex > Landmarks.count - 1 ) {
|
||||||
LandmarkIndex = 0;
|
LMS_LandmarkIndex = 0;
|
||||||
}
|
}
|
||||||
if (Map::IsMultilap(Landmarks[LandmarkIndex])) {
|
if (Map::IsMultilap(Landmarks[LMS_LandmarkIndex])) {
|
||||||
PlayerLM = Landmarks[LandmarkIndex];
|
PlayerLM = Landmarks[LMS_LandmarkIndex];
|
||||||
}
|
}
|
||||||
LandmarkIndex = LandmarkIndex + 1 ;
|
LMS_LandmarkIndex = LMS_LandmarkIndex + 1 ;
|
||||||
}
|
}
|
||||||
Race::Start(Player, PlayerLM , StartTime);
|
Race::Start(Player, PlayerLM , StartTime);
|
||||||
CarRank::SetRank(Player, Net_NBPlayers);
|
CarRank::SetRank(Player, Net_NBPlayers);
|
||||||
AccountIdsOfPlayers.add(Player.User.WebServicesUserId);
|
LMS_AccountIdsOfPlayers.add(Player.User.WebServicesUserId);
|
||||||
MalusQueue[Player.User.Login] = GetNewMalus(C_Malus_Reset, 1500);
|
Round_MalusQueue[Player.User.Login] = GetNewMalus(C_Malus_Reset, 1500);
|
||||||
}
|
}
|
||||||
|
|
||||||
UIModules_ScoresTable::DisplayOnly(AccountIdsOfPlayers);
|
UIModules_ScoresTable::DisplayOnly(LMS_AccountIdsOfPlayers);
|
||||||
|
|
||||||
StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]);
|
StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]);
|
||||||
Race::EnableIntroDuringMatch(False);
|
Race::EnableIntroDuringMatch(False);
|
||||||
UIManager.UIAll.SendChat("$<$ff3$> Stay the most time on the structure. $<$ff9GL HF!$>");
|
UIManager.UIAll.SendChat("$<$ff3$> Stay the most time on the structure. $<$ff9GL HF!$>");
|
||||||
|
XmlRpc::SendCallback(C_Callback_CustomChat_ChatMessage, ["$<$ff3$> Stay the most time on the structure. $<$ff9GL HF!$>"]);
|
||||||
***
|
***
|
||||||
|
|
||||||
|
// @mslint-disable-next-line max-statements
|
||||||
***Match_PlayLoop***
|
***Match_PlayLoop***
|
||||||
***
|
***
|
||||||
// Manage race events
|
// Manage race events
|
||||||
@ -346,20 +365,23 @@ foreach (Event in RacePendingEvents) {
|
|||||||
|
|
||||||
switch (Event.Type) {
|
switch (Event.Type) {
|
||||||
case Events::C_Type_Waypoint: {
|
case Events::C_Type_Waypoint: {
|
||||||
ThrottleUpdate = True;
|
Round_ThrottleUpdate = True;
|
||||||
|
|
||||||
Scores::UpdatePlayerBestRaceIfBetter(Event.Player);
|
Scores::UpdatePlayerBestRaceIfBetter(Event.Player);
|
||||||
Race::StopSkipOutro(Event.Player);
|
Race::StopSkipOutro(Event.Player);
|
||||||
UpdateCustomRanking(Event.Player.User, False);
|
UpdateCustomRanking(Event.Player.User, Event.Player, False);
|
||||||
|
if (Event.Player.User != Null) Round_AccountIdsOfEliminated.add(Event.Player.User.WebServicesUserId);
|
||||||
}
|
}
|
||||||
case Events::C_Type_GiveUp: {
|
case Events::C_Type_GiveUp: {
|
||||||
ThrottleUpdate = True;
|
Round_ThrottleUpdate = True;
|
||||||
UpdateCustomRanking(Event.Player.User, True);
|
UpdateCustomRanking(Event.Player.User, Event.Player, True);
|
||||||
|
if (Event.Player.User != Null) Round_AccountIdsOfEliminated.add(Event.Player.User.WebServicesUserId);
|
||||||
}
|
}
|
||||||
case Events::C_Type_Eliminated: {
|
case Events::C_Type_Eliminated: {
|
||||||
ThrottleUpdate = True;
|
Round_ThrottleUpdate = True;
|
||||||
Race::StopSkipOutro(Event.Player);
|
Race::StopSkipOutro(Event.Player);
|
||||||
UpdateCustomRanking(Event.Player.User, True);
|
UpdateCustomRanking(Event.Player.User, Event.Player, True);
|
||||||
|
if (Event.Player.User != Null) Round_AccountIdsOfEliminated.add(Event.Player.User.WebServicesUserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -371,28 +393,30 @@ foreach (Event in PendingEvents) {
|
|||||||
|
|
||||||
if (Event.Type == CSmModeEvent::EType::OnPlayerRemoved) {
|
if (Event.Type == CSmModeEvent::EType::OnPlayerRemoved) {
|
||||||
if (Event.User == Null ) continue;
|
if (Event.User == Null ) continue;
|
||||||
if (!AccountIdsOfPlayers.exists(Event.User.WebServicesUserId)) continue;
|
if (!LMS_AccountIdsOfPlayers.exists(Event.User.WebServicesUserId)) continue;
|
||||||
if (IsEliminated(Event.User, Null)) continue;
|
if (Round_AccountIdsOfEliminated.exists(Event.User.WebServicesUserId)) continue;
|
||||||
ThrottleUpdate = True;
|
Round_ThrottleUpdate = True;
|
||||||
UpdateCustomRanking(Event.User, True);
|
UpdateCustomRanking(Event.User, Null, True);
|
||||||
|
Round_AccountIdsOfEliminated.add(Event.User.WebServicesUserId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect when a players count changed without having triggered any of Event (when becoming spectator for example)
|
// Detect when a players count changed without having triggered any of Event (when becoming spectator for example)
|
||||||
if (!ThrottleUpdate && Net_PlayersNbAlive != PlayersNbAlive) {
|
if (!Round_ThrottleUpdate && Net_PlayersNbAlive != PlayersNbAlive) {
|
||||||
log("Trying to detect why the player count changed");
|
log("Trying to detect why the player count changed");
|
||||||
foreach (Player in AllPlayers) {
|
foreach (Player in AllPlayers) {
|
||||||
if (Player.User == Null || Player.Score == Null) continue;
|
if (Player.User == Null || Player.Score == Null) continue;
|
||||||
if (!AccountIdsOfPlayers.exists(Player.User.WebServicesUserId)) continue;
|
if (!LMS_AccountIdsOfPlayers.exists(Player.User.WebServicesUserId)) continue;
|
||||||
if (Player.SpawnStatus != CSmPlayer::ESpawnStatus::NotSpawned) continue;
|
if (Player.SpawnStatus != CSmPlayer::ESpawnStatus::NotSpawned) continue;
|
||||||
if (IsEliminated(Player.User, Player.Score)) continue;
|
if (Round_AccountIdsOfEliminated.exists(Player.User.WebServicesUserId)) continue;
|
||||||
UpdateCustomRanking(Player.User, True);
|
UpdateCustomRanking(Player.User, Player, True);
|
||||||
ThrottleUpdate = True;
|
Round_AccountIdsOfEliminated.add(Player.User.WebServicesUserId);
|
||||||
|
Round_ThrottleUpdate = True;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ThrottleUpdate) {
|
if (Round_ThrottleUpdate) {
|
||||||
ThrottleUpdate = False;
|
Round_ThrottleUpdate = False;
|
||||||
Net_PlayersNbAlive = PlayersNbAlive;
|
Net_PlayersNbAlive = PlayersNbAlive;
|
||||||
declare Integer Points = Net_NBPlayers - Net_PlayersNbAlive;
|
declare Integer Points = Net_NBPlayers - Net_PlayersNbAlive;
|
||||||
foreach (Player in Players) {
|
foreach (Player in Players) {
|
||||||
@ -403,97 +427,97 @@ if (ThrottleUpdate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PlayersNbAlive <= 1 && AccountIdsOfPlayers.count >= 2) { //TODO just respawn in case of 1 player
|
if (PlayersNbAlive <= 1 && LMS_AccountIdsOfPlayers.count >= 2) { //TODO just respawn in case of 1 player
|
||||||
Net_TimeBeforeMalus = -1;
|
Net_TimeBeforeMalus = -1;
|
||||||
MB_StopRound();
|
MB_StopRound();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Update the map duration setting
|
// Update the map duration setting
|
||||||
if (Map_TimeBeforeMalus != S_TimeBeforeMalus || Map_TimeBeforeMalus != S_TimeBeforeNightmare || Map_MalusEveryNSecs != S_MalusEveryNSecs || Map_NextMalusPreparationTime != S_NextMalusPreparationTime || Map_MalusDuration != S_MalusDuration || Map_RoundsPerMap != S_RoundsPerMap) {
|
if (Round_TimeBeforeMalus != S_TimeBeforeMalus || Round_TimeBeforeMalus != S_TimeBeforeNightmare || Round_MalusEveryNSecs != S_MalusEveryNSecs || Round_NextMalusPreparationTime != S_NextMalusPreparationTime || Round_MalusDuration != S_MalusDuration || Round_RoundsPerMap != S_RoundsPerMap) {
|
||||||
Map_TimeBeforeMalus = S_TimeBeforeMalus;
|
Round_TimeBeforeMalus = S_TimeBeforeMalus;
|
||||||
Map_TimeBeforeNightmare = S_TimeBeforeNightmare;
|
Round_TimeBeforeNightmare = S_TimeBeforeNightmare;
|
||||||
Map_MalusEveryNSecs = S_MalusEveryNSecs;
|
Round_MalusEveryNSecs = S_MalusEveryNSecs;
|
||||||
Map_NextMalusPreparationTime = S_NextMalusPreparationTime;
|
Round_NextMalusPreparationTime = S_NextMalusPreparationTime;
|
||||||
Map_MalusDuration = S_MalusDuration;
|
Round_MalusDuration = S_MalusDuration;
|
||||||
Map_RoundsPerMap = S_RoundsPerMap;
|
Round_RoundsPerMap = S_RoundsPerMap;
|
||||||
Net_RoundsPerMap = Map_RoundsPerMap;
|
Net_RoundsPerMap = Round_RoundsPerMap;
|
||||||
UpdateScoresTableFooter();
|
UpdateScoresTableFooter();
|
||||||
MalusTime = GetTimeBeforeMalus(StartTime, S_TimeBeforeMalus, S_TimeBeforeNightmare);
|
Round_MalusTime = GetTimeBeforeMalus(StartTime, S_TimeBeforeMalus, S_TimeBeforeNightmare);
|
||||||
if (NextStepMalusTime == 0) {
|
if (Round_NextStepMalusTime == 0) {
|
||||||
Net_TimeBeforeMalus = MalusTime;
|
Net_TimeBeforeMalus = Round_MalusTime;
|
||||||
}
|
}
|
||||||
if (Map_MalusDuration <= 0 || (Map_TimeBeforeMalus < 0 && Map_TimeBeforeNightmare < 0)) {
|
if (Round_MalusDuration <= 0 || (Round_TimeBeforeMalus < 0 && Round_TimeBeforeNightmare < 0)) {
|
||||||
Net_TimeBeforeMalus = -1;
|
Net_TimeBeforeMalus = -1;
|
||||||
Net_NextMalus = -1;
|
Net_NextMalus = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run Malus
|
// Run Malus
|
||||||
if (Players.count > 0 && S_MalusDuration > 0 && MalusTime != -1 && Now > MalusTime) {
|
if (Players.count > 0 && S_MalusDuration > 0 && Round_MalusTime != -1 && Now > Round_MalusTime) {
|
||||||
if (Now > NextStepMalusTime) {
|
if (Now > Round_NextStepMalusTime) {
|
||||||
if (!ActiveMalus && !PendingMalus) {
|
if (!Round_ActiveMalus && !Round_PendingMalus) {
|
||||||
if (S_TimeBeforeNightmare >= 0 && Now > (StartTime + (S_TimeBeforeNightmare * 1000))) {
|
if (S_TimeBeforeNightmare >= 0 && Now > (StartTime + (S_TimeBeforeNightmare * 1000))) {
|
||||||
MalusIndex = C_Malus_Nightmare;
|
Round_MalusIndex = C_Malus_Nightmare;
|
||||||
} else if (AllPlayersAreInTurtle()) {
|
} else if (AllPlayersAreInTurtle()) {
|
||||||
log("All players are in turtle");
|
log("All players are in turtle");
|
||||||
MalusIndex = ML::Rand(7, 10); // Boost if all players in Turtle
|
Round_MalusIndex = ML::Rand(7, 10); // Boost if all players in Turtle
|
||||||
} else {
|
} else {
|
||||||
MalusIndex = ML::Rand(1, 15);
|
Round_MalusIndex = ML::Rand(1, 15);
|
||||||
}
|
}
|
||||||
PendingMalus = True;
|
Round_PendingMalus = True;
|
||||||
ActiveMalus = False;
|
Round_ActiveMalus = False;
|
||||||
NextStepMalusTime = Now + (S_NextMalusPreparationTime*1000);
|
Round_NextStepMalusTime = Now + (S_NextMalusPreparationTime*1000);
|
||||||
|
|
||||||
// Players UI update
|
// Players UI update
|
||||||
Net_NextMalus = MalusIndex;
|
Net_NextMalus = Round_MalusIndex;
|
||||||
Net_TimeBeforeMalus = NextStepMalusTime;
|
Net_TimeBeforeMalus = Round_NextStepMalusTime;
|
||||||
} else if (PendingMalus && !ActiveMalus) {
|
} else if (Round_PendingMalus && !Round_ActiveMalus) {
|
||||||
foreach (Player in Players) {
|
foreach (Player in Players) {
|
||||||
MalusQueue[Player.User.Login] = GetNewMalus(MalusIndex);
|
Round_MalusQueue[Player.User.Login] = GetNewMalus(Round_MalusIndex);
|
||||||
}
|
}
|
||||||
PendingMalus = False;
|
Round_PendingMalus = False;
|
||||||
ActiveMalus = True;
|
Round_ActiveMalus = True;
|
||||||
NextStepMalusTime = Now + (S_MalusDuration*1000);
|
Round_NextStepMalusTime = Now + (S_MalusDuration*1000);
|
||||||
|
|
||||||
UIModules_BigMessage::SetMessage("Current Effect: "^C_Malus_Name[MalusIndex]);
|
UIModules_BigMessage::SetMessage("Current Effect: "^C_Malus_Name[Round_MalusIndex]);
|
||||||
|
|
||||||
// Players UI update
|
// Players UI update
|
||||||
Net_NextMalus = 0;
|
Net_NextMalus = 0;
|
||||||
Net_TimeBeforeMalus = NextStepMalusTime;
|
Net_TimeBeforeMalus = Round_NextStepMalusTime;
|
||||||
} else if (!PendingMalus && ActiveMalus) {
|
} else if (!Round_PendingMalus && Round_ActiveMalus) {
|
||||||
if (MalusIndex == 99) {
|
if (Round_MalusIndex == 99) {
|
||||||
foreach (Player in Players) {
|
foreach (Player in Players) {
|
||||||
MalusQueue[Player.User.Login] = GetNewMalus(C_Malus_Nightmare);
|
Round_MalusQueue[Player.User.Login] = GetNewMalus(C_Malus_Nightmare);
|
||||||
}
|
}
|
||||||
NextStepMalusTime = Now + (S_MalusDuration*1000);
|
Round_NextStepMalusTime = Now + (S_MalusDuration*1000);
|
||||||
} else {
|
} else {
|
||||||
foreach (Player in Players) {
|
foreach (Player in Players) {
|
||||||
MalusQueue[Player.User.Login] = GetNewMalus(C_Malus_Reset);
|
Round_MalusQueue[Player.User.Login] = GetNewMalus(C_Malus_Reset);
|
||||||
}
|
}
|
||||||
PendingMalus = False;
|
Round_PendingMalus = False;
|
||||||
ActiveMalus = False;
|
Round_ActiveMalus = False;
|
||||||
|
|
||||||
NextStepMalusTime = Now + (S_MalusEveryNSecs*1000);
|
Round_NextStepMalusTime = Now + (S_MalusEveryNSecs*1000);
|
||||||
|
|
||||||
UIModules_BigMessage::SetMessage("");
|
UIModules_BigMessage::SetMessage("");
|
||||||
|
|
||||||
// Players UI update
|
// Players UI update
|
||||||
Net_NextMalus = -1;
|
Net_NextMalus = -1;
|
||||||
Net_TimeBeforeMalus = NextStepMalusTime;
|
Net_TimeBeforeMalus = Round_NextStepMalusTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (Login => Malus in MalusQueue) {
|
foreach (Login => Malus in Round_MalusQueue) {
|
||||||
declare CSmPlayer Player = GetPlayer(Login);
|
declare CSmPlayer Player = GetPlayer(Login);
|
||||||
if (Malus.Time + 1000 < Now) { // Clear old entry
|
if (Malus.Time + 1000 < Now) { // Clear old entry
|
||||||
MalusQueue.removekey(Login);
|
Round_MalusQueue.removekey(Login);
|
||||||
} else if (Player != Null && Malus.Time <= Now && (Player.SpawnStatus == CSmPlayer::ESpawnStatus::Spawned || Player.SpawnStatus == CSmPlayer::ESpawnStatus::Spawning)) {
|
} else if (Player != Null && Malus.Time <= Now && (Player.SpawnStatus == CSmPlayer::ESpawnStatus::Spawned || Player.SpawnStatus == CSmPlayer::ESpawnStatus::Spawning)) {
|
||||||
Log::Log("[ApplyPhysics] Trying to set Event " ^ C_Malus_Name[Malus.MalusIndex] ^ " for " ^ Player.User.Name);
|
Log::Log("[ApplyPhysics] Trying to set Event " ^ C_Malus_Name[Malus.MalusIndex] ^ " for " ^ Player.User.Name);
|
||||||
if (SetMalus(Player, Malus.MalusIndex)) {
|
if (SetMalus(Player, Malus.MalusIndex)) {
|
||||||
MalusQueue.removekey(Login);
|
Round_MalusQueue.removekey(Login);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -501,8 +525,7 @@ foreach (Login => Malus in MalusQueue) {
|
|||||||
|
|
||||||
***Match_EndRound***
|
***Match_EndRound***
|
||||||
***
|
***
|
||||||
PendingMalus = False;
|
UIModules_BigMessage::SetMessage("");
|
||||||
ActiveMalus = False;
|
|
||||||
Net_DisplayUI = False;
|
Net_DisplayUI = False;
|
||||||
Net_TimeBeforeMalus = -1;
|
Net_TimeBeforeMalus = -1;
|
||||||
Net_NextMalus = -1;
|
Net_NextMalus = -1;
|
||||||
@ -521,12 +544,23 @@ if (Round_ForceEndRound || Round_SkipPauseRound) {
|
|||||||
}
|
}
|
||||||
MB_SetValidRound(False);
|
MB_SetValidRound(False);
|
||||||
} else {
|
} else {
|
||||||
Map_ValidRoundsNb += 1;
|
if (S_KeepScoresBetweenRounds) {
|
||||||
|
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
||||||
|
MB_Sleep((S_ChatTime*1000)/2);
|
||||||
|
|
||||||
|
ComputeRoundPoints();
|
||||||
|
UIModules_ScoresTable::SetCustomPoints([]);
|
||||||
|
UIModules_ScoresTable::DisplayRoundPoints(True);
|
||||||
|
MB_Sleep((S_ChatTime*1000)/2);
|
||||||
|
|
||||||
|
Scores::EndRound();
|
||||||
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
|
MB_Sleep((S_ChatTime*1000)/2);
|
||||||
|
} else {
|
||||||
declare CSmScore WinnerScore <=> Scores::GetBestPlayer(Scores::C_Sort_RoundPoints);
|
declare CSmScore WinnerScore <=> Scores::GetBestPlayer(Scores::C_Sort_RoundPoints);
|
||||||
if (WinnerScore == Null) {
|
if (WinnerScore == Null) {
|
||||||
foreach (Score in Scores) {
|
foreach (Score in Scores) {
|
||||||
if (Score.BestRaceTimes.count <= 0 && Score.User != Null && AccountIdsOfPlayers.exists(Score.User.WebServicesUserId)) {
|
if (Score.BestRaceTimes.count <= 0 && Score.User != Null && LMS_AccountIdsOfPlayers.exists(Score.User.WebServicesUserId)) {
|
||||||
declare CSmPlayer Player = GetPlayer(Score.User.Login);
|
declare CSmPlayer Player = GetPlayer(Score.User.Login);
|
||||||
if (Player != Null && !Player.RequestsSpectate) {
|
if (Player != Null && !Player.RequestsSpectate) {
|
||||||
WinnerScore <=> Score;
|
WinnerScore <=> Score;
|
||||||
@ -547,17 +581,36 @@ if (Round_ForceEndRound || Round_SkipPauseRound) {
|
|||||||
|
|
||||||
Scores::EndRound();
|
Scores::EndRound();
|
||||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
|
MB_Sleep((S_ChatTime*1000)/2);
|
||||||
|
|
||||||
MB_Sleep(5000);
|
|
||||||
UIModules_BigMessage::SetMessage("");
|
UIModules_BigMessage::SetMessage("");
|
||||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
||||||
MB_Sleep((S_ChatTime*1000)/2);
|
MB_Sleep((S_ChatTime*1000)/2);
|
||||||
|
|
||||||
|
Scores::Clear();
|
||||||
|
}
|
||||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal;
|
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal;
|
||||||
UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing;
|
UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing;
|
||||||
|
|
||||||
if (MapIsOver(Map_ValidRoundsNb)) MB_StopMatch();
|
UIModules_ScoresTable::DisplayRoundPoints(False);
|
||||||
}
|
UIModules_ScoresTable::SetCustomPoints([]);
|
||||||
|
|
||||||
|
if (MapIsOver()) MB_StopMap();
|
||||||
|
}
|
||||||
|
***
|
||||||
|
|
||||||
|
|
||||||
|
***Match_EndMap***
|
||||||
|
***
|
||||||
|
if (MatchIsOver()) {
|
||||||
|
MB_StopMatch();
|
||||||
|
if (!S_KeepScoresBetweenRounds) MB_SkipPodiumSequence();
|
||||||
|
|
||||||
|
declare CSmScore Winner <=> Scores::GetBestPlayer(Scores::C_Sort_MatchPoints);
|
||||||
|
Scores::SetPlayerWinner(Winner);
|
||||||
|
} else {
|
||||||
|
MB_SkipPodiumSequence();
|
||||||
|
}
|
||||||
***
|
***
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
@ -582,30 +635,13 @@ Void ResetNetworkVariables() {
|
|||||||
Net_CurrentRoundNb = 0;
|
Net_CurrentRoundNb = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Void ResetCustomPoints() {
|
||||||
/** Check if a Player is already considered eliminated
|
declare Text[][Text] CustomPoints = [];
|
||||||
*
|
|
||||||
* @param _User The User
|
|
||||||
* @param _Score The Score, can be Null but it will search the Score in Scores
|
|
||||||
*
|
|
||||||
* @return Return True if the player have a time
|
|
||||||
*/
|
|
||||||
Boolean IsEliminated(CUser _User, CSmScore _Score) {
|
|
||||||
if (UIModules_ScoresTable::GetCustomTimes().existskey(_User.WebServicesUserId)) return True;
|
|
||||||
if (_Score == Null) {
|
|
||||||
foreach (Score in Scores) {
|
foreach (Score in Scores) {
|
||||||
if (Score.User == Null) continue;
|
if (Score.User == Null) continue;
|
||||||
if (Score.User == _User) {
|
CustomPoints[Score.User.WebServicesUserId] = ["--:--.---"];
|
||||||
if (Score.PrevRaceTimes.count > 0) return True;
|
|
||||||
else return False;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
UIModules_ScoresTable::SetCustomPoints(CustomPoints);
|
||||||
} else if (_Score.PrevRaceTimes.count > 0) {
|
|
||||||
return True;
|
|
||||||
}
|
|
||||||
|
|
||||||
return False;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Detect if all players are in Turtle
|
/** Detect if all players are in Turtle
|
||||||
@ -645,17 +681,40 @@ Boolean AllPlayersAreInTurtle() {
|
|||||||
/** Update the Scores Table with hidden custom points
|
/** Update the Scores Table with hidden custom points
|
||||||
*
|
*
|
||||||
* @param _User The User who is eliminated
|
* @param _User The User who is eliminated
|
||||||
|
* @param _Player The Player who is eliminated. Can be Null
|
||||||
* @param _OverrideTime Compute time because it not ended the Map
|
* @param _OverrideTime Compute time because it not ended the Map
|
||||||
*/
|
*/
|
||||||
Void UpdateCustomRanking(CUser _User, Boolean _OverrideTime) {
|
Void UpdateCustomRanking(CUser _User, CSmPlayer _Player, Boolean _OverrideTime) {
|
||||||
if (_User == Null) return;
|
if (_User == Null) return;
|
||||||
|
|
||||||
if (_OverrideTime) {
|
declare Text[][Text] CustomPoints = UIModules_ScoresTable::GetCustomPoints();
|
||||||
declare Integer[Text] CustomTimes = UIModules_ScoresTable::GetCustomTimes();
|
|
||||||
CustomTimes[_User.WebServicesUserId] = Now - StartTime;
|
if (_OverrideTime || _Player == Null || _Player.RaceWaypointTimes.count == 0) {
|
||||||
UIModules_ScoresTable::SetCustomTimes(CustomTimes);
|
CustomPoints[_User.WebServicesUserId] = [TL::TimeToText(Now - StartTime, True, True)];
|
||||||
|
} else {
|
||||||
|
CustomPoints[_User.WebServicesUserId] = [TL::TimeToText(_Player.RaceWaypointTimes[-1], True, True)];
|
||||||
}
|
}
|
||||||
|
UIModules_ScoresTable::SetCustomPoints(CustomPoints);
|
||||||
UIManager.UIAll.SendChat("""$<$ff3$> Player $<$ff9{{{_User.Name}}}$> is eliminated""");
|
UIManager.UIAll.SendChat("""$<$ff3$> Player $<$ff9{{{_User.Name}}}$> is eliminated""");
|
||||||
|
XmlRpc::SendCallback(C_Callback_CustomChat_ChatMessage, ["""$<$ff3$> Player $<$ff9{{{_User.Name}}}$> is eliminated"""]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Distribute real RoundPoints using Points Repartition
|
||||||
|
* Only used when S_KeepScoresBetweenRounds = True
|
||||||
|
*/
|
||||||
|
Void ComputeRoundPoints() {
|
||||||
|
declare Integer[] PointsRepartition = PointsRepartition::GetPointsRepartition();
|
||||||
|
foreach (Key => Score in Scores) {
|
||||||
|
declare Integer Points = 0;
|
||||||
|
if (PointsRepartition.count > 0) {
|
||||||
|
if (PointsRepartition.existskey(Key)) {
|
||||||
|
Points = PointsRepartition[Key];
|
||||||
|
} else {
|
||||||
|
Points = PointsRepartition[PointsRepartition.count - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scores::SetPlayerRoundPoints(Score, Points);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Update the scores table footer text
|
/** Update the scores table footer text
|
||||||
@ -678,11 +737,18 @@ Boolean AllPlayersAreInTurtle() {
|
|||||||
Message ^= """%{{{Parts.count + 1}}}{{{TL::TimeToText(S_TimeBeforeNightmare*1000)}}}""";
|
Message ^= """%{{{Parts.count + 1}}}{{{TL::TimeToText(S_TimeBeforeNightmare*1000)}}}""";
|
||||||
Parts.add("Time Before NM: ");
|
Parts.add("Time Before NM: ");
|
||||||
}
|
}
|
||||||
|
if (S_KeepScoresBetweenRounds) {
|
||||||
|
if (Parts.count > 0) Message ^= "\n";
|
||||||
|
Message ^= """%{{{Parts.count + 1}}}{{{MB_GetMapCount()}}}/{{{S_MapsPerMatch}}}""";
|
||||||
|
//L16N [Rounds] Number of maps played during the match.
|
||||||
|
Parts.add(_("Maps : "));
|
||||||
|
}
|
||||||
|
|
||||||
switch (Parts.count) {
|
switch (Parts.count) {
|
||||||
case 0: Footer = Message;
|
case 0: Footer = Message;
|
||||||
case 1: Footer = TL::Compose(Message, Parts[0]);
|
case 1: Footer = TL::Compose(Message, Parts[0]);
|
||||||
case 2: Footer = TL::Compose(Message, Parts[0], Parts[1]);
|
case 2: Footer = TL::Compose(Message, Parts[0], Parts[1]);
|
||||||
|
case 3: Footer = TL::Compose(Message, Parts[0], Parts[1], Parts[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UIModules_ScoresTable::SetFooterInfo(Footer);
|
UIModules_ScoresTable::SetFooterInfo(Footer);
|
||||||
@ -1033,12 +1099,20 @@ Void SetML() {
|
|||||||
|
|
||||||
|
|
||||||
/** Check if we should go to the next map
|
/** Check if we should go to the next map
|
||||||
*
|
|
||||||
* @param _ValidRoundsNb Number of valid rounds played
|
|
||||||
*
|
|
||||||
* @return True if it is the case, false otherwise
|
* @return True if it is the case, false otherwise
|
||||||
*/
|
*/
|
||||||
Boolean MapIsOver(Integer _ValidRoundsNb) {
|
Boolean MapIsOver() {
|
||||||
if (S_RoundsPerMap > 0 && _ValidRoundsNb >= S_RoundsPerMap) return True; //< There is a rounds limit and it is reached
|
log("""MapIsOver> S_RoundsPerMap: {{{S_RoundsPerMap}}} / MB_GetValidRoundCount() {{{MB_GetValidRoundCount()}}}""");
|
||||||
|
if (S_RoundsPerMap > 0 && MB_GetValidRoundCount() >= S_RoundsPerMap) return True; //< There is a rounds limit and it is reached
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Check if the match is over
|
||||||
|
* @return True if it is the case, false otherwise
|
||||||
|
*/
|
||||||
|
Boolean MatchIsOver() {
|
||||||
|
log("""MatchIsOver> S_KeepScoresBetweenRounds: {{{S_KeepScoresBetweenRounds}}} / MB_GetValidMapCount() {{{MB_GetValidMapCount()}}} / S_MapsPerMatch {{{S_MapsPerMatch}}}""");
|
||||||
|
if (!S_KeepScoresBetweenRounds) return True;
|
||||||
|
if (MB_GetValidMapCount() >= S_MapsPerMatch) return True;
|
||||||
return False;
|
return False;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
// Constants
|
// Constants
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
#Const C_DebugMode_Version "2024-03-12"
|
#Const C_DebugMode_Version "2024-05-08"
|
||||||
#Const C_DebugMode_MainUI "DebugMode_MainUI"
|
#Const C_DebugMode_MainUI "DebugMode_MainUI"
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
@ -548,6 +548,10 @@ Text GetManialink() {
|
|||||||
#Include "MathLib" as ML
|
#Include "MathLib" as ML
|
||||||
// #RequireContext CSmMlScriptIngame
|
// #RequireContext CSmMlScriptIngame
|
||||||
|
|
||||||
|
#Const ScriptName {{{dump(C_DebugMode_MainUI)}}}
|
||||||
|
#Const Version {{{dump(C_DebugMode_Version)}}}
|
||||||
|
|
||||||
|
|
||||||
Real ComputeBackgroundSize(CMlLabel _Label, Text _Value, Real _MaxLineWidth) {
|
Real ComputeBackgroundSize(CMlLabel _Label, Text _Value, Real _MaxLineWidth) {
|
||||||
declare Real SpaceSize = _Label.ComputeWidth(" ");
|
declare Real SpaceSize = _Label.ComputeWidth(" ");
|
||||||
|
|
||||||
|
179
TM_Lobby.Script.txt
Normal file
179
TM_Lobby.Script.txt
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/**
|
||||||
|
* Lobby Gamemode
|
||||||
|
*/
|
||||||
|
|
||||||
|
// #RequireContext CSmMode
|
||||||
|
#Extends "Modes/TrackMania/TM_TimeAttack_Online.Script.txt"
|
||||||
|
|
||||||
|
#Include "Libs/Nadeo/Trackmania/Modes/Stunt/Constants.Script.txt" as StuntConstants
|
||||||
|
|
||||||
|
#Const StuntConstants::C_FigureNames as C_FigureNames
|
||||||
|
#Const C_Manialink_Id "Lobby.Records"
|
||||||
|
|
||||||
|
#Setting S_TimeLimit 0
|
||||||
|
|
||||||
|
#Struct K_BestTime {
|
||||||
|
Text PlayerName;
|
||||||
|
Integer Time;
|
||||||
|
}
|
||||||
|
#Struct K_BestStunt {
|
||||||
|
Text PlayerName;
|
||||||
|
Text FigureName;
|
||||||
|
Integer FigurePoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
***Match_StartServer***
|
||||||
|
***
|
||||||
|
UseStunts = True;
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_AfterLoadHud***
|
||||||
|
***
|
||||||
|
SetManialink();
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_InitMatch***
|
||||||
|
***
|
||||||
|
declare netwrite K_BestTime Net_Lobby_BestTime for Teams[0];
|
||||||
|
declare netwrite Integer Net_Lobby_BestTime_Serial for Teams[0];
|
||||||
|
declare netwrite K_BestStunt Net_Lobby_BestStunt for Teams[0];
|
||||||
|
declare netwrite Integer Net_Lobby_BestStunt_Serial for Teams[0];
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_StartMatch***
|
||||||
|
***
|
||||||
|
Net_Lobby_BestTime = K_BestTime{};
|
||||||
|
Net_Lobby_BestTime_Serial += 1;
|
||||||
|
Net_Lobby_BestStunt = K_BestStunt{};
|
||||||
|
Net_Lobby_BestStunt_Serial += 1;
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_PlayLoop***
|
||||||
|
***
|
||||||
|
|
||||||
|
|
||||||
|
foreach (Event in PendingEvents) {
|
||||||
|
if (Event.Player == Null || Event.Player.User == Null) continue;
|
||||||
|
|
||||||
|
switch (Event.Type) {
|
||||||
|
case CSmModeEvent::EType::OnPlayerTriggersWaypoint: {
|
||||||
|
if (Event.IsFinish && (Net_Lobby_BestTime.Time == 0 || Net_Lobby_BestTime.Time > Event.WaypointTime)) {
|
||||||
|
|
||||||
|
Net_Lobby_BestTime = K_BestTime {
|
||||||
|
PlayerName = Event.Player.User.Name,
|
||||||
|
Time = Event.WaypointTime
|
||||||
|
};
|
||||||
|
Net_Lobby_BestTime_Serial += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case CSmModeEvent::EType::OnStuntFigure: {
|
||||||
|
if (Event.StuntFigure.Points < 20) continue;
|
||||||
|
declare Integer FigurePoints = Event.StuntFigure.Points * 10;
|
||||||
|
declare Text FigureName = C_FigureNames[Event.StuntFigure.Name]^" "^ Event.StuntFigure.Angle ^"°";
|
||||||
|
|
||||||
|
if (Net_Lobby_BestStunt.FigurePoints == 0 || Net_Lobby_BestStunt.FigurePoints < Event.StuntFigure.Points * 10) {
|
||||||
|
Net_Lobby_BestStunt = K_BestStunt {
|
||||||
|
PlayerName = Event.Player.User.Name,
|
||||||
|
FigureName = FigureName,
|
||||||
|
FigurePoints = FigurePoints
|
||||||
|
};
|
||||||
|
Net_Lobby_BestStunt_Serial += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare CUIConfig UI <=> UIManager.GetUI(Event.Player);
|
||||||
|
if (UI != Null) {
|
||||||
|
UI.SendChat("$CFFYou made $<$fff"^ FigurePoints ^" points$> with a $<$fff"^ FigureName^ "$>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
***
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
/// Load Manialink
|
||||||
|
Void SetManialink() {
|
||||||
|
declare Text MLText = """
|
||||||
|
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||||
|
<manialink version="3" name="{{{C_Manialink_Id}}}">
|
||||||
|
<stylesheet>
|
||||||
|
<style class="text" textcolor="ffffff" textfont="GameFontSemiBold" textprefix="$s" textsize="1"/>
|
||||||
|
</stylesheet>
|
||||||
|
<frame id="frame-global" hidden="1" pos="0 90">
|
||||||
|
<frame pos="-71 0">
|
||||||
|
<quad size="45 15" bgcolor="000" opacity="0.5"/>
|
||||||
|
<label pos="22.5 -2" size="43 5" halign="center" textfont="GameFontBlack" textcolor="ffffff" textsize="2" textprefix="$i" text="BEST RACE TIME"/>
|
||||||
|
<label id="label-time-time" pos="22.5 -6.5" size="43 5" halign="center" textfont="GameFontExtraBold" textcolor="ffffff" textsize="1.2" text=""/>
|
||||||
|
<label id="label-time-playername" pos="22.5 -10" size="43 5" halign="center" textfont="GameFontSemiBold" textcolor="ffffff" textsize="1.2" text=""/>
|
||||||
|
</frame>
|
||||||
|
<frame pos="29 0">
|
||||||
|
<quad size="45 15" bgcolor="000" opacity="0.5"/>
|
||||||
|
<label pos="22.5 -2" size="43 5" halign="center" textfont="GameFontBlack" textcolor="ffffff" textsize="2" textprefix="$i" text="BEST STUNT FIGURE"/>
|
||||||
|
<label id="label-stunt-figurename" pos="22.5 -6.5" size="43 5" halign="center" textfont="GameFontExtraBold" textcolor="ffffff" textsize="1.2" text=""/>
|
||||||
|
<label id="label-stunt-playername" pos="22.5 -10" size="43 5" halign="center" textfont="GameFontSemiBold" textcolor="ffffff" textsize="1.2" text=""/>
|
||||||
|
</frame>
|
||||||
|
</frame>
|
||||||
|
<script><!--
|
||||||
|
#Include "TextLib" as TL
|
||||||
|
|
||||||
|
#Const ScriptName {{{dump(C_Manialink_Id)}}}
|
||||||
|
#Const Version {{{dump(Version)}}}
|
||||||
|
|
||||||
|
{{{dumptype(K_BestTime)}}}
|
||||||
|
{{{dumptype(K_BestStunt)}}}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
declare CMlFrame Frame_Global <=> (Page.GetFirstChild("frame-global") as CMlFrame);
|
||||||
|
|
||||||
|
declare CMlLabel Label_Time_Time <=> (Frame_Global.GetFirstChild("label-time-time") as CMlLabel);
|
||||||
|
declare CMlLabel Label_Time_PlayerName <=> (Frame_Global.GetFirstChild("label-time-playername") as CMlLabel);
|
||||||
|
|
||||||
|
declare CMlLabel Label_Stunt_FigureName <=> (Frame_Global.GetFirstChild("label-stunt-figurename") as CMlLabel);
|
||||||
|
declare CMlLabel Label_Stunt_PlayerName <=> (Frame_Global.GetFirstChild("label-stunt-playername") as CMlLabel);
|
||||||
|
|
||||||
|
wait (InputPlayer != Null);
|
||||||
|
|
||||||
|
Frame_Global.Visible = True;
|
||||||
|
|
||||||
|
declare netread K_BestTime Net_Lobby_BestTime for Teams[0];
|
||||||
|
declare netread Integer Net_Lobby_BestTime_Serial for Teams[0];
|
||||||
|
declare Integer Last_BestTime_Serial = -1;
|
||||||
|
|
||||||
|
declare netread K_BestStunt Net_Lobby_BestStunt for Teams[0];
|
||||||
|
declare netread Integer Net_Lobby_BestStunt_Serial for Teams[0];
|
||||||
|
declare Integer Last_BestStunt_Serial = -1;
|
||||||
|
|
||||||
|
while (True) {
|
||||||
|
yield;
|
||||||
|
|
||||||
|
if (Last_BestTime_Serial != Net_Lobby_BestTime_Serial) {
|
||||||
|
Last_BestTime_Serial = Net_Lobby_BestTime_Serial;
|
||||||
|
|
||||||
|
if (Net_Lobby_BestTime.PlayerName == "") {
|
||||||
|
Label_Time_Time.Value = "";
|
||||||
|
Label_Time_PlayerName.Value = "";
|
||||||
|
} else {
|
||||||
|
Label_Time_Time.Value = TL::TimeToText(Net_Lobby_BestTime.Time, True, True);
|
||||||
|
Label_Time_PlayerName.Value = "by "^ Net_Lobby_BestTime.PlayerName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Last_BestStunt_Serial != Net_Lobby_BestStunt_Serial) {
|
||||||
|
Last_BestStunt_Serial = Net_Lobby_BestStunt_Serial;
|
||||||
|
|
||||||
|
if (Net_Lobby_BestStunt.PlayerName == "") {
|
||||||
|
Label_Stunt_FigureName.Value = "";
|
||||||
|
Label_Stunt_PlayerName.Value = "";
|
||||||
|
} else {
|
||||||
|
Label_Stunt_FigureName.Value = Net_Lobby_BestStunt.FigurePoints ^" points - "^ Net_Lobby_BestStunt.FigureName;
|
||||||
|
Label_Stunt_PlayerName.Value = "by "^ Net_Lobby_BestStunt.PlayerName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--></script>
|
||||||
|
</manialink>
|
||||||
|
""";
|
||||||
|
Layers::Create(C_Manialink_Id, MLText);
|
||||||
|
Layers::SetType(C_Manialink_Id, CUILayer::EUILayerType::Normal);
|
||||||
|
Layers::Attach(C_Manialink_Id);
|
||||||
|
}
|
1660
TM_MultiLivesKnockout.Script.txt
Normal file
1660
TM_MultiLivesKnockout.Script.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,10 +1,11 @@
|
|||||||
/**
|
/**
|
||||||
* Rounds mode
|
* Rounds Nearest.
|
||||||
|
* Nearest you are of the S_TargetTime, more points you win
|
||||||
*/
|
*/
|
||||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
||||||
|
|
||||||
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||||
#Const Version "2023-10-16"
|
#Const Version "2025-05-08"
|
||||||
#Const ScriptName "Modes/TM2020-Gamemodes/TM_RoundsNearest.Script.txt"
|
#Const ScriptName "Modes/TM2020-Gamemodes/TM_RoundsNearest.Script.txt"
|
||||||
|
|
||||||
// #RequireContext CSmMode
|
// #RequireContext CSmMode
|
||||||
@ -20,21 +21,24 @@
|
|||||||
#Include "Libs/Nadeo/TMGame/Utils/Tracking.Script.txt" as Tracking
|
#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/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/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
|
// Settings
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
#Setting S_PointsLimit 20 as _("Points limit")
|
#Setting S_BonusForPerfect 50
|
||||||
#Setting S_FinishTimeout -1 as _("Finish timeout")
|
#Setting S_FinishTimeout 10 as _("Finish timeout")
|
||||||
#Setting S_RoundsPerMap -1 as _("Number of rounds per track") ///< Number of round to play on one map before going to the next one
|
#Setting S_MapsPerMatch 0 as _("Number of tracks per match") ///< Number of maps to play before finishing the match
|
||||||
#Setting S_MapsPerMatch -1 as _("Number of tracks per match") ///< Number of maps to play before finishing the match
|
#Setting S_NbOfWinners 4 as _("Number of winners")
|
||||||
#Setting S_UseTieBreak True as _("Use tie-break") ///< Continue to play the map until the tie is broken
|
#Setting S_PointsLimit 50 as _("Points limit")
|
||||||
#Setting S_WarmUpNb 0 as _("Number of warm up")
|
#Setting S_PointsRepartition "5,4,3,2,1,0"
|
||||||
#Setting S_WarmUpDuration 0 as _("Duration of one warm up")
|
#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_WarmUpTimeout -1 as _("Warm up timeout")
|
|
||||||
#Setting S_PointsRepartition "1,0"
|
|
||||||
#Setting S_BonusForPerfect 4
|
|
||||||
#Setting S_TargetTime 20000
|
#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")
|
||||||
|
#Setting S_WarmUpTimeout -1 as _("Warm up timeout")
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
// Constants
|
// Constants
|
||||||
@ -65,6 +69,7 @@ Log::RegisterScript(ScriptName, Version);
|
|||||||
Log::RegisterScript(Semver::ScriptName, Semver::Version);
|
Log::RegisterScript(Semver::ScriptName, Semver::Version);
|
||||||
Log::RegisterScript(ModeUtils::ScriptName, ModeUtils::Version);
|
Log::RegisterScript(ModeUtils::ScriptName, ModeUtils::Version);
|
||||||
Log::RegisterScript(StateMgr::ScriptName, StateMgr::Version);
|
Log::RegisterScript(StateMgr::ScriptName, StateMgr::Version);
|
||||||
|
Log::RegisterScript(UIModules_SmallScoresTable::ScriptName, UIModules_SmallScoresTable::Version);
|
||||||
***
|
***
|
||||||
|
|
||||||
***Match_LoadLibraries***
|
***Match_LoadLibraries***
|
||||||
@ -109,7 +114,7 @@ UIManager.UIAll.OverlayHideSpectatorInfos = True;
|
|||||||
UIManager.UIAll.OverlayHideCountdown = True;
|
UIManager.UIAll.OverlayHideCountdown = True;
|
||||||
|
|
||||||
UIModules_ScoresTable::DisplayRoundPoints(True);
|
UIModules_ScoresTable::DisplayRoundPoints(True);
|
||||||
UIModules::UnloadModules(["UIModule_Race_TimeGap", "UIModule_Rounds_SmallScoresTable"]);
|
UIModules::UnloadModules(["UIModule_Race_TimeGap"]);
|
||||||
***
|
***
|
||||||
|
|
||||||
***Match_Yield***
|
***Match_Yield***
|
||||||
@ -158,6 +163,13 @@ Server_RoundsPerMap = S_RoundsPerMap - 1;
|
|||||||
Server_MapsPerMatch = S_MapsPerMatch - 1;
|
Server_MapsPerMatch = S_MapsPerMatch - 1;
|
||||||
***
|
***
|
||||||
|
|
||||||
|
***Match_StartMatch***
|
||||||
|
***
|
||||||
|
UIModules_ScoresTable::SetCustomPoints([]);
|
||||||
|
UIModules_SmallScoresTable::ResetCustomTimes();
|
||||||
|
UIModules_SmallScoresTable::ResetCustomResults();
|
||||||
|
***
|
||||||
|
|
||||||
***Match_InitMap***
|
***Match_InitMap***
|
||||||
***
|
***
|
||||||
declare Integer Map_ValidRoundsNb;
|
declare Integer Map_ValidRoundsNb;
|
||||||
@ -170,18 +182,37 @@ UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_Valid
|
|||||||
***
|
***
|
||||||
Map_Skipped = True;
|
Map_Skipped = True;
|
||||||
CarRank::Reset();
|
CarRank::Reset();
|
||||||
UIModules_ScoresTable::SetCustomPoints([]);
|
UIModules_ScoresTable::SetCustomPoints(GetWinnersCustomPoints());
|
||||||
|
|
||||||
// Warm up
|
// Warm up
|
||||||
|
foreach (Score in Scores) {
|
||||||
|
WarmUp::CanPlay(Score, CanSpawn(Score));
|
||||||
|
}
|
||||||
|
|
||||||
UIModules_ScoresTable::SetFooterInfo(_("Warm up"));
|
UIModules_ScoresTable::SetFooterInfo(_("Warm up"));
|
||||||
MB_WarmUp(S_WarmUpNb, S_WarmUpDuration * 1000, S_WarmUpTimeout * 1000);
|
MB_WarmUp(S_WarmUpNb, S_WarmUpDuration * 1000, S_WarmUpTimeout * 1000);
|
||||||
***
|
***
|
||||||
|
|
||||||
|
***Rounds_CanSpawn***
|
||||||
|
***
|
||||||
|
foreach (Score in Scores) {
|
||||||
|
declare Boolean ModeRounds_CanSpawn for Score = True;
|
||||||
|
ModeRounds_CanSpawn = CanSpawn(Score);
|
||||||
|
}
|
||||||
|
***
|
||||||
|
|
||||||
***Match_StartRound***
|
***Match_StartRound***
|
||||||
***
|
***
|
||||||
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb);
|
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb);
|
||||||
StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]);
|
StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]);
|
||||||
UIModules_ScoresTable::SetCustomPoints([]);
|
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***
|
***Rounds_PlayerSpawned***
|
||||||
@ -204,7 +235,7 @@ foreach (Event in RacePendingEvents) {
|
|||||||
Scores::UpdatePlayerBestRaceIfBetter(Event.Player);
|
Scores::UpdatePlayerBestRaceIfBetter(Event.Player);
|
||||||
Scores::UpdatePlayerBestLapIfBetter(Event.Player);
|
Scores::UpdatePlayerBestLapIfBetter(Event.Player);
|
||||||
Scores::UpdatePlayerPrevRace(Event.Player);
|
Scores::UpdatePlayerPrevRace(Event.Player);
|
||||||
ComputeLatestRaceScores();
|
ComputeLatestRaceScores(False);
|
||||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
|
|
||||||
// Start the countdown if it's the first player to finish
|
// Start the countdown if it's the first player to finish
|
||||||
@ -263,30 +294,36 @@ if (Round_ForceEndRound || Round_SkipPauseRound || Round_Skipped) {
|
|||||||
} else {
|
} else {
|
||||||
Map_ValidRoundsNb += 1;
|
Map_ValidRoundsNb += 1;
|
||||||
// Get the last round points
|
// Get the last round points
|
||||||
ComputeLatestRaceScores();
|
ComputeLatestRaceScores(True);
|
||||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
|
||||||
UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound;
|
UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound;
|
||||||
MB_Sleep(3000);
|
MB_Sleep(S_ChatTime * 1000 / 3);
|
||||||
|
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
||||||
|
MB_Sleep(S_ChatTime * 1000 / 3);
|
||||||
// Add them to the total scores
|
// Add them to the total scores
|
||||||
ComputeScores();
|
ComputeScores();
|
||||||
UIModules_ScoresTable::SetCustomPoints([]);
|
UIModules_ScoresTable::SetCustomPoints(GetWinnersCustomPoints());
|
||||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
MB_Sleep(3000);
|
MB_Sleep(S_ChatTime * 1000 / 3);
|
||||||
|
UIModules_BigMessage::SetMessage("");
|
||||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal;
|
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal;
|
||||||
UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing;
|
UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing;
|
||||||
|
|
||||||
if (MapIsOver(S_UseTieBreak, S_PointsLimit, Map_ValidRoundsNb, S_RoundsPerMap)) {
|
// Match is over, we have all the winners
|
||||||
Map_Skipped = False;
|
if (MatchIsOver(MB_GetMapCount(), Map_Skipped)) {
|
||||||
|
MB_StopMatch();
|
||||||
|
}
|
||||||
|
// Map is over, we played all the rounds
|
||||||
|
else if (MapIsOver(Map_ValidRoundsNb)) {
|
||||||
MB_StopMap();
|
MB_StopMap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
UIModules_SmallScoresTable::ResetCustomTimes();
|
||||||
|
UIModules_SmallScoresTable::ResetCustomResults();
|
||||||
***
|
***
|
||||||
|
|
||||||
***Match_EndMap***
|
***Match_EndMap***
|
||||||
***
|
***
|
||||||
if (MatchIsOver(S_UseTieBreak, S_PointsLimit, MB_GetMapCount(), S_MapsPerMatch, S_RoundsPerMap, Map_Skipped)) MB_StopMatch();
|
|
||||||
|
|
||||||
if (!MB_MapIsRunning() && MB_MatchIsRunning()) MB_SkipPodiumSequence();
|
if (!MB_MapIsRunning() && MB_MatchIsRunning()) MB_SkipPodiumSequence();
|
||||||
|
|
||||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
@ -348,6 +385,24 @@ Void UpdateScoresTableFooter(Integer _PointsLimit, Integer _RoundsPerMap, Intege
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
/** Check if a player can spawn
|
||||||
|
*
|
||||||
|
* @param _Score The player's score
|
||||||
|
*
|
||||||
|
* @return True if the player can spawn,
|
||||||
|
* False otherwise
|
||||||
|
*/
|
||||||
|
Boolean CanSpawn(CSmScore _Score) {
|
||||||
|
if (_Score == Null) return False;
|
||||||
|
|
||||||
|
if (Scores::GetPlayerMatchPoints(_Score) >= S_PointsLimit) {
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
return True;
|
||||||
|
}
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
/** Get the time left to the players to finish the round after the first player
|
/** Get the time left to the players to finish the round after the first player
|
||||||
*
|
*
|
||||||
@ -390,15 +445,32 @@ Text FormatPlayerName(Text _Name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Text[][Text] GetWinnersCustomPoints() {
|
||||||
|
if (S_PointsLimit <= 0) return [];
|
||||||
|
|
||||||
|
declare Text[][Text] CustomPoints = [];
|
||||||
|
|
||||||
|
foreach (Score in Scores) {
|
||||||
|
if (Score.User == Null) continue;
|
||||||
|
if (Scores::GetPlayerMatchPoints(Score) < S_PointsLimit) continue;
|
||||||
|
|
||||||
|
declare Integer Rank = S_PointsLimit + 1 + S_NbOfWinners + 1 - Scores::GetPlayerMatchPoints(Score);
|
||||||
|
CustomPoints[Score.User.WebServicesUserId] = [TL::FormatRank(Rank, True)];
|
||||||
|
}
|
||||||
|
return CustomPoints;
|
||||||
|
}
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
/// Compute the latest race scores
|
/// Compute the latest race scores
|
||||||
Void ComputeLatestRaceScores() {
|
Void ComputeLatestRaceScores(Boolean _IsEndRound) {
|
||||||
Race::SortScores(Race::C_Sort_PrevRaceTime);
|
Race::SortScores(Race::C_Sort_PrevRaceTime);
|
||||||
|
|
||||||
// Points distributed between all players
|
// Points distributed between all players
|
||||||
|
|
||||||
declare Text[][Text] CustomPoints = [];
|
declare Text[][Text] CustomPoints = GetWinnersCustomPoints();
|
||||||
declare CSmScore[][Integer] ScoresPerAbsoluteDelta;
|
declare CSmScore[][Integer] ScoresPerAbsoluteDelta;
|
||||||
|
declare Integer[Text] CustomTimes;
|
||||||
|
declare Text[Text] CustomResult;
|
||||||
foreach (Score in Scores) {
|
foreach (Score in Scores) {
|
||||||
if (Score.User == Null) continue;
|
if (Score.User == Null) continue;
|
||||||
if (Scores::GetPlayerPrevRaceTime(Score) <= 0) continue;
|
if (Scores::GetPlayerPrevRaceTime(Score) <= 0) continue;
|
||||||
@ -412,7 +484,15 @@ Void ComputeLatestRaceScores() {
|
|||||||
else TextDelta = "-";
|
else TextDelta = "-";
|
||||||
|
|
||||||
TextDelta ^= DeltaTimeToText(AbsoluteDelta);
|
TextDelta ^= DeltaTimeToText(AbsoluteDelta);
|
||||||
|
if (S_PointsLimit < 0 || Scores::GetPlayerMatchPoints(Score) < S_PointsLimit) {
|
||||||
CustomPoints[Score.User.WebServicesUserId] = [Scores::GetPlayerMatchPoints(Score) ^ " (" ^ TextDelta ^ ")"];
|
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);
|
UIModules_ScoresTable::SetCustomPoints(CustomPoints);
|
||||||
|
|
||||||
@ -420,12 +500,12 @@ Void ComputeLatestRaceScores() {
|
|||||||
|
|
||||||
declare Integer I = 0;
|
declare Integer I = 0;
|
||||||
declare Integer[] PointsRepartition = PointsRepartition::GetPointsRepartition();
|
declare Integer[] PointsRepartition = PointsRepartition::GetPointsRepartition();
|
||||||
|
declare Text Names;
|
||||||
|
|
||||||
foreach (Delta => CustomScores in ScoresPerAbsoluteDelta) {
|
foreach (Delta => CustomScores in ScoresPerAbsoluteDelta) {
|
||||||
// Attribute less points if they have the same time
|
// Attribute less points if they have the same time
|
||||||
if (CustomScores.count > 1) {
|
if (S_SendSameTimeMessage && _IsEndRound && CustomScores.count > 1) {
|
||||||
I += CustomScores.count - 1;
|
I += CustomScores.count - 1;
|
||||||
declare Text Names;
|
|
||||||
foreach (Key => Score in CustomScores) {
|
foreach (Key => Score in CustomScores) {
|
||||||
if (Key == 0) {
|
if (Key == 0) {
|
||||||
Names = FormatPlayerName(Score.User.Name);
|
Names = FormatPlayerName(Score.User.Name);
|
||||||
@ -437,7 +517,11 @@ Void ComputeLatestRaceScores() {
|
|||||||
}
|
}
|
||||||
UIManager.UIAll.SendChat("$ff3" ^ Names^ " have the same time");
|
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;
|
declare Integer Points;
|
||||||
|
|
||||||
if (PointsRepartition.existskey(I)) {
|
if (PointsRepartition.existskey(I)) {
|
||||||
@ -446,110 +530,128 @@ Void ComputeLatestRaceScores() {
|
|||||||
Points = PointsRepartition[PointsRepartition.count - 1];
|
Points = PointsRepartition[PointsRepartition.count - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Delta == 0 && CustomScores.count == 1) {
|
if (Delta == 0) {
|
||||||
Points += S_BonusForPerfect;
|
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");
|
UIManager.UIAll.SendChat("$ff3" ^ 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);
|
Scores::SetPlayerRoundPoints(Score, Points);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (UpdateBigMessage) {
|
||||||
|
UIModules_BigMessage::SetMessage(Names ^ " did the perfect time");
|
||||||
|
}
|
||||||
|
|
||||||
I += 1;
|
I += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UIModules_SmallScoresTable::SetCustomTimes(CustomTimes);
|
||||||
|
UIModules_SmallScoresTable::SetCustomResults(CustomResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
/// Compute the map scores
|
/// Compute the map scores
|
||||||
Void ComputeScores() {
|
Void ComputeScores() {
|
||||||
|
if (S_PointsLimit <= 0) {
|
||||||
Scores::EndRound();
|
Scores::EndRound();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
declare Integer NbOfWinners = 0;
|
||||||
/** Check if the points limit was reached
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
*
|
|
||||||
* @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) {
|
foreach (Score in Scores) {
|
||||||
declare Integer Points = Scores::GetPlayerMatchPoints(Score);
|
// Already won
|
||||||
if (Points > MaxScore) {
|
if (Scores::GetPlayerMatchPoints(Score) >= S_PointsLimit) {
|
||||||
MaxScore = Points;
|
Scores::SetPlayerMatchPoints(Score, S_PointsLimit + 1 + S_NbOfWinners - NbOfWinners);
|
||||||
Tie = False;
|
NbOfWinners += 1;
|
||||||
} else if (Points == MaxScore) {
|
|
||||||
Tie = True;
|
|
||||||
}
|
}
|
||||||
|
// New winner
|
||||||
|
else if (Scores::GetPlayerMatchPoints(Score) + Scores::GetPlayerRoundPoints(Score) >= S_PointsLimit) {
|
||||||
|
Scores::SetPlayerMatchPoints(Score, S_PointsLimit + 1 + S_NbOfWinners - NbOfWinners);
|
||||||
|
NbOfWinners += 1;
|
||||||
|
}
|
||||||
|
// Standard round finish
|
||||||
|
else {
|
||||||
|
Scores::AddPlayerMatchPoints(Score, Scores::GetPlayerRoundPoints(Score));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_UseTieBreak && Tie) return C_PointsLimit_Tie; //< There is a tie and it is not allowed
|
Scores::AddPlayerMapPoints(Score, Scores::GetPlayerRoundPoints(Score));
|
||||||
if (_PointsLimit > 0 && MaxScore >= _PointsLimit) return C_PointsLimit_Reached; //< There is a points limit and it is reached
|
Scores::SetPlayerRoundPoints(Score, 0);
|
||||||
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
|
/** 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
|
* @return True if it is the case, false otherwise
|
||||||
*/
|
*/
|
||||||
Boolean MapIsOver(Boolean _UseTieBreak, Integer _PointsLimit, Integer _ValidRoundsNb, Integer _RoundsPerMap) {
|
Boolean MapIsOver(Integer _ValidRoundsNb) {
|
||||||
declare Integer PointsLimitReached = PointsLimitReached(_UseTieBreak, _PointsLimit);
|
if (_ValidRoundsNb >= S_RoundsPerMap) return True;
|
||||||
|
|
||||||
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;
|
return False;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
/** Check if we should go to the next match
|
/** 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 _MapsPerMatch Number of maps to play to complete a match
|
||||||
* @param _RoundsPerMap Number of rounds to play to complete the map
|
* @param _MapSkipped if map was skipped
|
||||||
*
|
*
|
||||||
* @return True if it is the case, false otherwise
|
* @return True if it is the case, false otherwise
|
||||||
*/
|
*/
|
||||||
Boolean MatchIsOver(Boolean _UseTieBreak, Integer _PointsLimit, Integer _MapCount, Integer _MapsPerMatch, Integer _RoundsPerMap, Boolean _MapSkipped) {
|
Boolean MatchIsOver(Integer _MapCount, Boolean _MapSkipped) {
|
||||||
declare Integer PointsLimitReached = PointsLimitReached(_UseTieBreak, _PointsLimit);
|
Log::Log("[Cup] MatchIsOver() check | S_PointsLimit : "^S_PointsLimit);
|
||||||
|
if (S_PointsLimit > 0) {
|
||||||
Log::Log("""[Rounds] MatchIsOver() > _UseTieBreak: {{{_UseTieBreak}}} | _PointsLimit: {{{_PointsLimit}}} | _MapCount: {{{_MapCount}}} | _MapsPerMatch: {{{_MapsPerMatch}}} | _RoundsPerMap: {{{_RoundsPerMap}}} | PointsLimitReached: {{{PointsLimitReached}}} | _MapSkipped : {{{_MapSkipped}}}""");
|
declare Integer NbOfScoreWinners = 0;
|
||||||
|
foreach (Score in Scores) {
|
||||||
// If there is a point limit and it is reached, stop the match
|
if (Scores::GetPlayerMatchPoints(Score) > S_PointsLimit) NbOfScoreWinners += 1;
|
||||||
if (PointsLimitReached == C_PointsLimit_Reached) {
|
|
||||||
return True;
|
|
||||||
}
|
}
|
||||||
// If there is an explicit maps limit ...
|
declare Integer NbOfPlayerWinners = 0;
|
||||||
else if (_MapsPerMatch >= 1) {
|
foreach (Player in Players) {
|
||||||
|
if (Scores::GetPlayerMatchPoints(Player.Score) > S_PointsLimit) NbOfPlayerWinners += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's only one player they need to reach the points limit to win
|
||||||
|
// If there's more than one player then all players except one must reach the points limit
|
||||||
|
declare Integer PlayerWinnersLimit = ML::Max(Players.count - 1, 1);
|
||||||
|
Log::Log("""[Cup] Match is over ? {{{(NbOfScoreWinners >= S_NbOfWinners || NbOfPlayerWinners >= PlayerWinnersLimit)}}} | ({{{NbOfScoreWinners}}} >= {{{S_NbOfWinners}}} || {{{NbOfPlayerWinners}}} >= {{{PlayerWinnersLimit}}})""");
|
||||||
|
if (NbOfScoreWinners >= S_NbOfWinners || NbOfPlayerWinners >= PlayerWinnersLimit) return True;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_MapsPerMatch >= 1) {
|
||||||
if (
|
if (
|
||||||
(_MapCount >= _MapsPerMatch && PointsLimitReached != C_PointsLimit_Tie) || //< ... stop the match if the maps limit is reached and the match is not a tie
|
_MapCount >= S_MapsPerMatch || //< ... 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
|
(_MapSkipped && S_MapsPerMatch == 1 && _MapCount >= S_MapsPerMatch) //< ... stop the match if the map was skipped and the match is played on only one map
|
||||||
) {
|
) {
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If there is a rounds limit but no maps limit, continue to play until another limit is reached
|
// If there is a rounds limit but no maps limit, continue to play until another limit is reached
|
||||||
else if (_RoundsPerMap >= 1) {
|
else if (S_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;
|
return False;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return False;
|
||||||
|
}
|
633
TM_RoundsThenTimeattack.Script.txt
Normal file
633
TM_RoundsThenTimeattack.Script.txt
Normal file
@ -0,0 +1,633 @@
|
|||||||
|
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
||||||
|
// #RequireContext CSmMode
|
||||||
|
|
||||||
|
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||||
|
#Const Version "2024-08-12"
|
||||||
|
#Const ScriptName "Modes/TM2020-Gamemodes/TM_RoundsThenTimeattack.Script.txt"
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
// MARK: Libraries
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
#Include "TextLib" as TL
|
||||||
|
#Include "MathLib" as ML
|
||||||
|
#Include "Libs/Nadeo/CMGame/Utils/Semver.Script.txt" as Semver
|
||||||
|
#Include "Libs/Nadeo/Trackmania/MainMenu/Constants.Script.txt" as MenuConsts
|
||||||
|
#Include "Libs/Nadeo/Trackmania/Modes/Rounds/StateManager.Script.txt" as StateMgr
|
||||||
|
#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/CMGame/Modes/UIModules_Server.Script.txt" as UIModules_Server
|
||||||
|
#Include "Libs/Nadeo/CMGame/Modes/UIModules_Common.Script.txt" as UIModules_Common
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
// MARK: Settings
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
#Setting S_DisableFreeCam False
|
||||||
|
#Setting S_TimeLimit 300 as _("Time limit") ///< Time limit before going to the next map
|
||||||
|
#Setting S_PointsLimit 50 as _("Points limit")
|
||||||
|
#Setting S_FinishTimeout -1 as _("Finish timeout")
|
||||||
|
#Setting S_RoundsPerMap -1 as _("Number of rounds per track") ///< Number of round to play on one map before going to the next one
|
||||||
|
#Setting S_MapsPerMatch -1 as _("Number of tracks per match") ///< Number of maps to play before finishing the match
|
||||||
|
#Setting S_UseTieBreak True as _("Use tie-break") ///< Continue to play the map until the tie is broken
|
||||||
|
#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")
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
// MARK: Constants
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
#Const C_ModeName "Rounds"
|
||||||
|
//L16N [Rounds] Description of the mode rules
|
||||||
|
#Const Description _("$zIn $<$t$6F9Rounds$z$z$> mode, the goal is to win a maximum number of $<$t$6F9points.\n\n$z$>The rounds mode consists of $<$t$6F9a series of races$z$>.\nWhen you finish a race in a good $<$t$6F9position$z$>, you get $<$t$6F9points$z$>, added to your total.\n\nThe $<$t$6F9winner$z$> is the first player whose total reaches the $<$t$6F9point limit$z$> (30 for example).")
|
||||||
|
|
||||||
|
#Const C_HudModulePath "" //< Path to the hud module
|
||||||
|
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/Rounds.Script.txt" //< Url of the mania app
|
||||||
|
#Const C_FakeUsersNb 0
|
||||||
|
|
||||||
|
#Const C_PointsLimit_NotReached 0
|
||||||
|
#Const C_PointsLimit_Reached 1
|
||||||
|
#Const C_PointsLimit_Tie 2
|
||||||
|
|
||||||
|
#Const C_UploadRecord True
|
||||||
|
#Const C_DisplayRecordGhost False
|
||||||
|
#Const C_DisplayRecordMedal False
|
||||||
|
#Const C_CelebrateRecordGhost True
|
||||||
|
#Const C_CelebrateRecordMedal True
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
// MARK: Extends
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
***Match_LogVersions***
|
||||||
|
***
|
||||||
|
Log::RegisterScript(ScriptName, Version);
|
||||||
|
Log::RegisterScript(Semver::ScriptName, Semver::Version);
|
||||||
|
Log::RegisterScript(ModeUtils::ScriptName, ModeUtils::Version);
|
||||||
|
Log::RegisterScript(StateMgr::ScriptName, StateMgr::Version);
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_LoadLibraries***
|
||||||
|
***
|
||||||
|
StateMgr::Load();
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_UnloadLibraries***
|
||||||
|
***
|
||||||
|
StateMgr::Unload();
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_Settings***
|
||||||
|
***
|
||||||
|
MB_Settings_UseDefaultHud = (C_HudModulePath == "");
|
||||||
|
MB_Settings_UseDefaultRespawnBehaviour = False;
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_Rules***
|
||||||
|
***
|
||||||
|
ModeInfo::SetName(C_ModeName);
|
||||||
|
ModeInfo::SetType(ModeInfo::C_Type_FreeForAll);
|
||||||
|
ModeInfo::SetRules(Description);
|
||||||
|
ModeInfo::SetStatusMessage("");
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_LoadHud***
|
||||||
|
***
|
||||||
|
if (C_HudModulePath != "") Hud_Load(C_HudModulePath);
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_AfterLoadHud***
|
||||||
|
***
|
||||||
|
ClientManiaAppUrl = C_ManiaAppUrl;
|
||||||
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
|
UIModules_ScoresTable::SetScoreMode(UIModules_ScoresTable::C_Mode_Points);
|
||||||
|
UIModules_Checkpoint::SetVisibilityTimeDiff(False);
|
||||||
|
UIModules_Checkpoint::SetRankMode(UIModules_Checkpoint::C_RankMode_CurrentRace);
|
||||||
|
UIModules_PauseMenu_Online::SetHelp(Description);
|
||||||
|
UIModules_Sign16x9Small::SetScoreMode(UIModules_Sign16x9Small::C_ScoreMode_Points);
|
||||||
|
// Hide SM Overlay
|
||||||
|
UIManager.UIAll.OverlayHideSpectatorInfos = True;
|
||||||
|
UIManager.UIAll.OverlayHideCountdown = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StateMgr::Yield();
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_InitServer***
|
||||||
|
***
|
||||||
|
declare Integer Server_PointsLimit;
|
||||||
|
declare Integer Server_RoundsPerMap;
|
||||||
|
declare Integer Server_MapsPerMatch;
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_StartServer***
|
||||||
|
***
|
||||||
|
// Initialize mode
|
||||||
|
Clans::SetClansNb(0);
|
||||||
|
Scores::SaveInScore(Scores::C_Points_Match);
|
||||||
|
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
||||||
|
WarmUp::SetAvailability(True);
|
||||||
|
Race::SetupRecord(
|
||||||
|
MenuConsts::C_ScopeType_Season,
|
||||||
|
MenuConsts::C_ScopeType_PersonalBest,
|
||||||
|
MenuConsts::C_GameMode_Rounds,
|
||||||
|
"",
|
||||||
|
C_UploadRecord,
|
||||||
|
C_DisplayRecordGhost,
|
||||||
|
C_DisplayRecordMedal,
|
||||||
|
C_CelebrateRecordGhost,
|
||||||
|
C_CelebrateRecordMedal
|
||||||
|
);
|
||||||
|
|
||||||
|
Server_PointsLimit = S_PointsLimit - 1;
|
||||||
|
Server_RoundsPerMap = S_RoundsPerMap - 1;
|
||||||
|
Server_MapsPerMatch = S_MapsPerMatch - 1;
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_InitMap***
|
||||||
|
***
|
||||||
|
declare Integer Map_ValidRoundsNb;
|
||||||
|
declare Boolean Map_Skipped;
|
||||||
|
|
||||||
|
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb);
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_StartMap***
|
||||||
|
***
|
||||||
|
// Add bot when necessary
|
||||||
|
Users_SetNbFakeUsers(C_FakeUsersNb, 0);
|
||||||
|
|
||||||
|
UIManager.UIAll.PlayerDisableFreeCam = S_DisableFreeCam;
|
||||||
|
|
||||||
|
Map_Skipped = True;
|
||||||
|
CarRank::Reset();
|
||||||
|
|
||||||
|
// Warm up
|
||||||
|
UIModules_ScoresTable::SetFooterInfo(_("Warm up"));
|
||||||
|
MB_WarmUp(S_WarmUpNb, S_WarmUpDuration * 1000, S_WarmUpTimeout * 1000);
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_InitRound***
|
||||||
|
***
|
||||||
|
declare Boolean Round_IsTimeAttack;
|
||||||
|
declare Integer Round_TimeLimit;
|
||||||
|
declare Integer[Ident] Round_Scores_Times;
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_StartRound***
|
||||||
|
***
|
||||||
|
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb);
|
||||||
|
StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]);
|
||||||
|
|
||||||
|
Round_IsTimeAttack = (Map_ValidRoundsNb + 1 >= S_RoundsPerMap);
|
||||||
|
|
||||||
|
if (Round_IsTimeAttack) {
|
||||||
|
Race::SetRespawnBehaviour(Race::C_RespawnBehaviour_GiveUpBeforeFirstCheckpoint);
|
||||||
|
|
||||||
|
Round_TimeLimit = S_TimeLimit;
|
||||||
|
StartTime = Now + Race::C_SpawnDuration;
|
||||||
|
SetTimeLimit(StartTime, S_TimeLimit);
|
||||||
|
|
||||||
|
UIModules_Server::Private_CustomizableModule_SetProperties("Rounds_SmallScoresTable", UIModules_Common::K_ModuleProperties {
|
||||||
|
Position = <-158.5, 40.>,
|
||||||
|
Scale = 1.,
|
||||||
|
Visible = False
|
||||||
|
});
|
||||||
|
|
||||||
|
UIModules_ScoresTable::SetScoreMode(UIModules_ScoresTable::C_Mode_PrevTime);
|
||||||
|
} else {
|
||||||
|
Race::SetRespawnBehaviour(Race::C_RespawnBehaviour_NeverGiveUp);
|
||||||
|
|
||||||
|
UIModules_Server::Private_CustomizableModule_SetProperties("Rounds_SmallScoresTable", UIModules_Common::K_ModuleProperties {
|
||||||
|
Position = <-158.5, 40.>,
|
||||||
|
Scale = 1.,
|
||||||
|
Visible = True
|
||||||
|
});
|
||||||
|
|
||||||
|
UIModules_ScoresTable::SetScoreMode(UIModules_ScoresTable::C_Mode_Points);
|
||||||
|
}
|
||||||
|
UIModules_ScoresTable::SetCustomTimes([]);
|
||||||
|
***
|
||||||
|
|
||||||
|
***Rounds_PlayerSpawned***
|
||||||
|
***
|
||||||
|
CarRank::ThrottleUpdate(CarRank::C_SortCriteria_CurrentRace);
|
||||||
|
***
|
||||||
|
|
||||||
|
***Match_PlayLoop***
|
||||||
|
***
|
||||||
|
// Manage race events
|
||||||
|
|
||||||
|
foreach (Event in Race::GetPendingEvents()) {
|
||||||
|
Race::ValidEvent(Event);
|
||||||
|
|
||||||
|
// Waypoint
|
||||||
|
if (Event.Type == Events::C_Type_Waypoint) {
|
||||||
|
CarRank::ThrottleUpdate(CarRank::C_SortCriteria_CurrentRace);
|
||||||
|
if (Event.Player != Null && Event.Player.Score != Null) {
|
||||||
|
if (Event.IsEndRace) {
|
||||||
|
Scores::UpdatePlayerBestRaceIfBetter(Event.Player);
|
||||||
|
Scores::UpdatePlayerBestLapIfBetter(Event.Player);
|
||||||
|
Scores::UpdatePlayerPrevRace(Event.Player);
|
||||||
|
|
||||||
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
|
|
||||||
|
if (Round_IsTimeAttack) {
|
||||||
|
if (!Round_Scores_Times.existskey(Event.Player.Score.Id)) {
|
||||||
|
Round_Scores_Times[Event.Player.Score.Id] = Event.RaceTime;
|
||||||
|
|
||||||
|
declare Integer[Text] CustomTimes = UIModules_ScoresTable::GetCustomTimes();
|
||||||
|
CustomTimes[Event.Player.User.WebServicesUserId] = Event.RaceTime;
|
||||||
|
UIModules_ScoresTable::SetCustomTimes(CustomTimes);
|
||||||
|
} else if (Round_Scores_Times[Event.Player.Score.Id] > Event.RaceTime) {
|
||||||
|
Round_Scores_Times[Event.Player.Score.Id] = Event.RaceTime;
|
||||||
|
|
||||||
|
declare Integer[Text] CustomTimes = UIModules_ScoresTable::GetCustomTimes();
|
||||||
|
CustomTimes[Event.Player.User.WebServicesUserId] = Event.RaceTime;
|
||||||
|
UIModules_ScoresTable::SetCustomTimes(CustomTimes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ComputeLatestRaceScores();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the countdown if it's the first player to finish
|
||||||
|
if (EndTime <= 0) {
|
||||||
|
EndTime = GetFinishTimeout(S_FinishTimeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Event.IsEndLap) {
|
||||||
|
Scores::UpdatePlayerBestLapIfBetter(Event.Player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Round_IsTimeAttack) {
|
||||||
|
if (PlayersNbDead > 0) {
|
||||||
|
foreach (Player in Players) {
|
||||||
|
if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned && Race::IsReadyToStart(Player)) {
|
||||||
|
Race::Start(Player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the map duration setting
|
||||||
|
if (Round_TimeLimit != S_TimeLimit) {
|
||||||
|
Round_TimeLimit = S_TimeLimit;
|
||||||
|
SetTimeLimit(StartTime, S_TimeLimit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Manage mode events
|
||||||
|
foreach (Event in PendingEvents) {
|
||||||
|
if (Event.HasBeenPassed || Event.HasBeenDiscarded) continue;
|
||||||
|
Events::Invalid(Event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server info change
|
||||||
|
if (
|
||||||
|
Server_PointsLimit != S_PointsLimit ||
|
||||||
|
Server_RoundsPerMap != S_RoundsPerMap ||
|
||||||
|
Server_MapsPerMatch != S_MapsPerMatch
|
||||||
|
) {
|
||||||
|
Server_PointsLimit = S_PointsLimit;
|
||||||
|
Server_RoundsPerMap = S_RoundsPerMap;
|
||||||
|
Server_MapsPerMatch = S_MapsPerMatch;
|
||||||
|
|
||||||
|
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb);
|
||||||
|
}
|
||||||
|
***
|
||||||
|
|
||||||
|
***Rounds_CheckStopRound***
|
||||||
|
***
|
||||||
|
// End the round
|
||||||
|
// If All players finished
|
||||||
|
if (!Round_IsTimeAttack && Players.count > 0 && PlayersNbAlive <= 0) {
|
||||||
|
MB_StopRound();
|
||||||
|
Round_Skipped = False;
|
||||||
|
}
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
MB_SetValidRound(False);
|
||||||
|
} else {
|
||||||
|
Map_ValidRoundsNb += 1;
|
||||||
|
|
||||||
|
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
||||||
|
UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound;
|
||||||
|
|
||||||
|
// Get the last round points
|
||||||
|
if (Round_IsTimeAttack) {
|
||||||
|
MB_Sleep(3000);
|
||||||
|
ComputeBestRaceScores(Round_Scores_Times);
|
||||||
|
UIModules_ScoresTable::SetCustomTimes([]);
|
||||||
|
UIModules_ScoresTable::SetScoreMode(UIModules_ScoresTable::C_Mode_Points);
|
||||||
|
} else {
|
||||||
|
ComputeLatestRaceScores();
|
||||||
|
}
|
||||||
|
|
||||||
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
|
MB_Sleep(3000);
|
||||||
|
// Add them to the total scores
|
||||||
|
ComputeScores();
|
||||||
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
|
MB_Sleep(3000);
|
||||||
|
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***
|
||||||
|
***
|
||||||
|
if (MatchIsOver(S_UseTieBreak, S_PointsLimit, MB_GetMapCount(), S_MapsPerMatch, S_RoundsPerMap, Map_Skipped)) MB_StopMatch();
|
||||||
|
|
||||||
|
if (!MB_MapIsRunning() && MB_MatchIsRunning()) MB_SkipPodiumSequence();
|
||||||
|
|
||||||
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
|
declare CSmScore Winner <=> Scores::GetBestPlayer(Scores::C_Sort_MatchPoints);
|
||||||
|
Scores::SetPlayerWinner(Winner);
|
||||||
|
|
||||||
|
if (!MB_MatchIsRunning()) {
|
||||||
|
// Compute ranking for tracking
|
||||||
|
declare Integer PreviousPoints = 0;
|
||||||
|
declare Integer Rank = 0;
|
||||||
|
foreach (Key => Score in Scores) {
|
||||||
|
if (Key == 0 || Scores::GetPlayerMatchPoints(Score) < PreviousPoints) {
|
||||||
|
PreviousPoints = Scores::GetPlayerMatchPoints(Score);
|
||||||
|
Rank = Key + 1;
|
||||||
|
}
|
||||||
|
Tracking::SendPlayerMatchResult(UIManager, Score.User, Rank, Winner == Score && Scores.count > 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
***
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
// MARK: Functions
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
/** Update the time limit
|
||||||
|
*
|
||||||
|
* @param _StartTime The starting time of the map
|
||||||
|
* @param _NewTimeLimit The time limit before going to the next map
|
||||||
|
*/
|
||||||
|
Void SetTimeLimit(Integer _StartTime, Integer _NewTimeLimit) {
|
||||||
|
if (_NewTimeLimit <= 0) {
|
||||||
|
EndTime = -1;
|
||||||
|
UIModules_ScoresTable::SetFooterInfo(TL::Compose("%1 -", _("Time Limit")));
|
||||||
|
} else {
|
||||||
|
EndTime = _StartTime + (_NewTimeLimit * 1000);
|
||||||
|
UIModules_ScoresTable::SetFooterInfo(TL::Compose("%1 "^TL::TimeToText(_NewTimeLimit*1000), _("Time Limit")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
/** Update the scores table footer text
|
||||||
|
*
|
||||||
|
* @param _PointsLimit The points limit
|
||||||
|
* @param _RoundsPerMap The number of rounds per map
|
||||||
|
* @param _MapsPerMatch The number of maps per match
|
||||||
|
* @param _ValidRoundsNb Number of valid rounds played
|
||||||
|
*/
|
||||||
|
Void UpdateScoresTableFooter(Integer _PointsLimit, Integer _RoundsPerMap, Integer _MapsPerMatch, Integer _ValidRoundsNb) {
|
||||||
|
declare Text[] Parts;
|
||||||
|
declare Text Message = "";
|
||||||
|
if (_PointsLimit > 0) {
|
||||||
|
if (Parts.count > 0) Message ^= "\n";
|
||||||
|
Message ^= """%{{{Parts.count + 1}}}{{{_PointsLimit}}}""";
|
||||||
|
//L16N [Rounds] Number of points to reach to win the match.
|
||||||
|
Parts.add(_("Points limit : "));
|
||||||
|
}
|
||||||
|
if (_RoundsPerMap > 0) {
|
||||||
|
if (Parts.count > 0) Message ^= "\n";
|
||||||
|
Message ^= """%{{{Parts.count + 1}}}{{{ML::Min(_ValidRoundsNb+1, _RoundsPerMap)}}}/{{{_RoundsPerMap}}}""";
|
||||||
|
//L16N [Rounds] Number of rounds played during the track.
|
||||||
|
Parts.add(_("Rounds : "));
|
||||||
|
}
|
||||||
|
if (_MapsPerMatch > 0) {
|
||||||
|
if (Parts.count > 0) Message ^= "\n";
|
||||||
|
Message ^= """%{{{Parts.count + 1}}}{{{MB_GetMapCount()}}}/{{{_MapsPerMatch}}}""";
|
||||||
|
//L16N [Rounds] Number of tracks played during the match.
|
||||||
|
Parts.add(_("Tracks : "));
|
||||||
|
}
|
||||||
|
|
||||||
|
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]));
|
||||||
|
case 3: UIModules_ScoresTable::SetFooterInfo(TL::Compose(Message, Parts[0], Parts[1], Parts[2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
/** Get the time left to the players to finish the round after the first player
|
||||||
|
*
|
||||||
|
* @return The time left in ms
|
||||||
|
*/
|
||||||
|
Integer GetFinishTimeout(Integer _FinishTimeout) {
|
||||||
|
declare Integer FinishTimeout = 0;
|
||||||
|
|
||||||
|
if (_FinishTimeout >= 0) {
|
||||||
|
FinishTimeout = _FinishTimeout * 1000;
|
||||||
|
} else {
|
||||||
|
FinishTimeout = 5000;
|
||||||
|
if (Map.TMObjective_IsLapRace && Race::GetLapsNb() > 0 && Map.TMObjective_NbLaps > 0) {
|
||||||
|
FinishTimeout += ((Map.TMObjective_AuthorTime / Map.TMObjective_NbLaps) * Race::GetLapsNb()) / 6;
|
||||||
|
} else {
|
||||||
|
FinishTimeout += Map.TMObjective_AuthorTime / 6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Now + FinishTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
/// Compute the latest race scores
|
||||||
|
Void ComputeLatestRaceScores() {
|
||||||
|
Race::SortScores(Race::C_Sort_PrevRaceTime);
|
||||||
|
|
||||||
|
// Points distributed between all players
|
||||||
|
declare Integer I = 0;
|
||||||
|
declare Integer[] PointsRepartition = PointsRepartition::GetPointsRepartition();
|
||||||
|
foreach (Score in Scores) {
|
||||||
|
if (Scores::GetPlayerPrevRaceTime(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
/// Compute the latest race scores
|
||||||
|
Void ComputeBestRaceScores(Integer[Ident] _Times) {
|
||||||
|
declare Integer[Ident] Times = _Times.sort();
|
||||||
|
|
||||||
|
// Points distributed between all players
|
||||||
|
declare Integer I = 0;
|
||||||
|
declare Integer[] PointsRepartition = PointsRepartition::GetPointsRepartition();
|
||||||
|
foreach (ScoreId => Time in Times) {
|
||||||
|
if (!Scores.existskey(ScoreId)) continue;
|
||||||
|
declare CSmScore Score <=> Scores[ScoreId];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
/// Compute the map scores
|
||||||
|
Void ComputeScores() {
|
||||||
|
Scores::EndRound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
|
/** 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;
|
||||||
|
}
|
@ -4,7 +4,7 @@
|
|||||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
||||||
|
|
||||||
#Const CompatibleMapTypes "TrackMania\\TM_Royal,TM_Royal"
|
#Const CompatibleMapTypes "TrackMania\\TM_Royal,TM_Royal"
|
||||||
#Const Version "2023-10-16"
|
#Const Version "2024-04-05"
|
||||||
#Const ScriptName "Modes/TrackMania/TM_RoyalRounds_Online.Script.txt"
|
#Const ScriptName "Modes/TrackMania/TM_RoyalRounds_Online.Script.txt"
|
||||||
|
|
||||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||||
@ -323,7 +323,7 @@ UIModules_Chrono::SetTimeOffset(Player, SpecificOffset);
|
|||||||
declare netwrite Integer Net_RoyalRounds_CheckpointUI_TotalNbSegments for Teams[0];
|
declare netwrite Integer Net_RoyalRounds_CheckpointUI_TotalNbSegments for Teams[0];
|
||||||
declare Integer[Text] CustomTimes for This = [];
|
declare Integer[Text] CustomTimes for This = [];
|
||||||
declare Integer[Text][Integer] CurrentRanking for This = []; // CurrentRanking[Segment][AccountId][Time]
|
declare Integer[Text][Integer] CurrentRanking for This = []; // CurrentRanking[Segment][AccountId][Time]
|
||||||
CurrentRanking[0] = Integer[Text]; // Init white section
|
CurrentRanking[0] = []; // Init white section
|
||||||
declare Boolean IsStartRound = True;
|
declare Boolean IsStartRound = True;
|
||||||
|
|
||||||
// Reset players for the race
|
// Reset players for the race
|
||||||
@ -349,8 +349,7 @@ IsStartRound = False;
|
|||||||
***Match_PlayLoop***
|
***Match_PlayLoop***
|
||||||
***
|
***
|
||||||
// Manage race events
|
// Manage race events
|
||||||
declare RacePendingEvents = Race::GetPendingEvents();
|
foreach (Event in Race::GetPendingEvents()) {
|
||||||
foreach (Event in RacePendingEvents) {
|
|
||||||
if (Event.Type == Events::C_Type_SkipOutro && C_DisableSkipOutro) {
|
if (Event.Type == Events::C_Type_SkipOutro && C_DisableSkipOutro) {
|
||||||
Race::InvalidEvent(Event);
|
Race::InvalidEvent(Event);
|
||||||
} else {
|
} else {
|
||||||
@ -363,7 +362,7 @@ foreach (Event in RacePendingEvents) {
|
|||||||
declare Integer CurrentSegment for Event.Player.Score = -1;
|
declare Integer CurrentSegment for Event.Player.Score = -1;
|
||||||
Log::Log("""[RacePendingEvents] Player {{{Event.Player.User.Name }}} end the segment {{{CurrentSegment}}} at {{{Event.Player.StartTime - StartTime + Event.RaceTime}}}""");
|
Log::Log("""[RacePendingEvents] Player {{{Event.Player.User.Name }}} end the segment {{{CurrentSegment}}} at {{{Event.Player.StartTime - StartTime + Event.RaceTime}}}""");
|
||||||
if (CurrentRanking.existskey(CurrentSegment-1)) CurrentRanking[CurrentSegment-1].removekey(Event.Player.User.WebServicesUserId) ;
|
if (CurrentRanking.existskey(CurrentSegment-1)) CurrentRanking[CurrentSegment-1].removekey(Event.Player.User.WebServicesUserId) ;
|
||||||
if (!CurrentRanking.existskey(CurrentSegment)) CurrentRanking[CurrentSegment] = Integer[Text];
|
if (!CurrentRanking.existskey(CurrentSegment)) CurrentRanking[CurrentSegment] = [];
|
||||||
CurrentRanking[CurrentSegment][Event.Player.User.WebServicesUserId] = Event.Player.StartTime - StartTime + Event.RaceTime;
|
CurrentRanking[CurrentSegment][Event.Player.User.WebServicesUserId] = Event.Player.StartTime - StartTime + Event.RaceTime;
|
||||||
|
|
||||||
// Update Ranking of the current players
|
// Update Ranking of the current players
|
||||||
@ -385,7 +384,7 @@ foreach (Event in RacePendingEvents) {
|
|||||||
Net_RoyalRounds_CheckpointUI_CurrentNbSegments = CurrentSegment;
|
Net_RoyalRounds_CheckpointUI_CurrentNbSegments = CurrentSegment;
|
||||||
|
|
||||||
if (CurrentSegment < S_SegmentsPerRound) { // TODO Try to keep CP diff a the bottom of the screen
|
if (CurrentSegment < S_SegmentsPerRound) { // TODO Try to keep CP diff a the bottom of the screen
|
||||||
declare ModeRounds_CanSpawn for Event.Player.Score = Rounds_Settings_CanSpawnDefault;
|
declare Boolean ModeRounds_CanSpawn for Event.Player.Score = Rounds_Settings_CanSpawnDefault;
|
||||||
ModeRounds_CanSpawn = True;
|
ModeRounds_CanSpawn = True;
|
||||||
CurrentSegment = CurrentSegment + 1;
|
CurrentSegment = CurrentSegment + 1;
|
||||||
Race::StopSkipScoresTable(Event.Player);
|
Race::StopSkipScoresTable(Event.Player);
|
||||||
@ -413,7 +412,6 @@ foreach (Event in RacePendingEvents) {
|
|||||||
CurrentSegment = CurrentSegment - 1;
|
CurrentSegment = CurrentSegment - 1;
|
||||||
}
|
}
|
||||||
if (UpdateRankingTimer == 0) UpdateRankingTimer = Now + 1000;
|
if (UpdateRankingTimer == 0) UpdateRankingTimer = Now + 1000;
|
||||||
declare Boolean ModeRounds_CanSpawn for Event.Player.Score = Rounds_Settings_CanSpawnDefault;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -508,7 +506,8 @@ if (Round_ForceEndRound || Round_SkipPauseRound) {
|
|||||||
***
|
***
|
||||||
Race::StopSkipOutroAll();
|
Race::StopSkipOutroAll();
|
||||||
EndTime = -1;
|
EndTime = -1;
|
||||||
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
// Keep Playing State to keep SmallScoresTable visible
|
||||||
|
StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]);
|
||||||
|
|
||||||
if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.1.1")) {
|
if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.1.1")) {
|
||||||
Scores::XmlRpc_SendScores(Scores::C_Section_PreEndRound, "");
|
Scores::XmlRpc_SendScores(Scores::C_Section_PreEndRound, "");
|
||||||
@ -529,9 +528,11 @@ if (Round_ForceEndRound || Round_SkipPauseRound || Round_Skipped) {
|
|||||||
// Get the last round points
|
// Get the last round points
|
||||||
UpdateCustomRanking(Null);
|
UpdateCustomRanking(Null);
|
||||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
|
||||||
UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound;
|
UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound;
|
||||||
MB_Sleep(3000);
|
MB_Sleep(3000);
|
||||||
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
|
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
||||||
|
MB_Sleep(3000);
|
||||||
// Add them to the total scores
|
// Add them to the total scores
|
||||||
ComputeScores();
|
ComputeScores();
|
||||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||||
@ -544,6 +545,7 @@ if (Round_ForceEndRound || Round_SkipPauseRound || Round_Skipped) {
|
|||||||
MB_StopMap();
|
MB_StopMap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
||||||
CustomTimes.clear();
|
CustomTimes.clear();
|
||||||
CurrentRanking.clear();
|
CurrentRanking.clear();
|
||||||
UIModules_SmallScoresTable::ResetCustomTimes();
|
UIModules_SmallScoresTable::ResetCustomTimes();
|
||||||
@ -638,8 +640,7 @@ Integer GetFinishTimeout(Integer _FinishTimeout) {
|
|||||||
if (_Player != Null) {
|
if (_Player != Null) {
|
||||||
declare Integer CurrentSegment for _Player.Score;
|
declare Integer CurrentSegment for _Player.Score;
|
||||||
CustomTimes[_Player.User.WebServicesUserId] = CurrentRanking[CurrentSegment][_Player.User.WebServicesUserId];
|
CustomTimes[_Player.User.WebServicesUserId] = CurrentRanking[CurrentSegment][_Player.User.WebServicesUserId];
|
||||||
} else {
|
} else if (!S_StrictPointDistribution) {
|
||||||
if (!S_StrictPointDistribution) {
|
|
||||||
CustomTimes.clear();
|
CustomTimes.clear();
|
||||||
CurrentRanking = CurrentRanking.sortkeyreverse();
|
CurrentRanking = CurrentRanking.sortkeyreverse();
|
||||||
declare Integer LastTimeFromThePreviousSegment = 0;
|
declare Integer LastTimeFromThePreviousSegment = 0;
|
||||||
@ -654,7 +655,6 @@ Integer GetFinishTimeout(Integer _FinishTimeout) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
declare Integer BonusForFinishers = 0;
|
declare Integer BonusForFinishers = 0;
|
||||||
if (S_FinisherBonusBase > 0 && S_FinisherBonusNumber > 0 && CurrentRanking.existskey(S_SegmentsPerRound)) {
|
if (S_FinisherBonusBase > 0 && S_FinisherBonusNumber > 0 && CurrentRanking.existskey(S_SegmentsPerRound)) {
|
||||||
@ -678,7 +678,7 @@ Integer GetFinishTimeout(Integer _FinishTimeout) {
|
|||||||
Points = PointsRepartition[PointsRepartition.count - 1];
|
Points = PointsRepartition[PointsRepartition.count - 1];
|
||||||
}
|
}
|
||||||
declare Integer CurrentSegment for Player.Score;
|
declare Integer CurrentSegment for Player.Score;
|
||||||
if (CurrentSegment == S_SegmentsPerRound) {
|
if (CurrentSegment == S_SegmentsPerRound && Scores::GetPlayerPrevRaceTime(Player.Score) > 0) {
|
||||||
Points += BonusForFinishers;
|
Points += BonusForFinishers;
|
||||||
}
|
}
|
||||||
Scores::SetPlayerRoundPoints(Player.Score, Points);
|
Scores::SetPlayerRoundPoints(Player.Score, Points);
|
||||||
|
Reference in New Issue
Block a user