960 lines
37 KiB
Plaintext
960 lines
37 KiB
Plaintext
/**
|
|
* Royal Rounds mode
|
|
*/
|
|
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
|
|
|
#Const CompatibleMapTypes "TrackMania\\TM_Royal,TM_Royal"
|
|
#Const Version "2023-10-16"
|
|
#Const ScriptName "Modes/TrackMania/TM_RoyalRounds_Online.Script.txt"
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
// Libraries
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
#Include "TextLib" as TL
|
|
#Include "MathLib" as ML
|
|
#Include "Libs/Nadeo/CMGame/Utils/Semver.Script.txt" as Semver
|
|
|
|
#Include "Libs/Nadeo/CMGame/Modes/Utils.Script.txt" as ModeUtils
|
|
#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/Modes/Base/UIModules/ScoresTable_Server.Script.txt" as UIModules_ScoresTable
|
|
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/PauseMenuOnline_Server.Script.txt" as UIModules_PauseMenu_Online
|
|
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/Checkpoint_Server.Script.txt" as UIModules_Checkpoint
|
|
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
|
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/Chrono_Server.Script.txt" as UIModules_Chrono
|
|
#Include "Libs/Nadeo/Trackmania/Modes/Rounds/UIModules/SmallScoresTable_Server.Script.txt" as UIModules_SmallScoresTable
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
// Settings
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
#Setting S_PointsLimit 100 as _("Points limit")
|
|
#Setting S_FinishTimeout -1 as _("Finish timeout")
|
|
#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 -1 as _("Number of maps 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_SegmentsPerRound 5 as "Number of segment to end the round"
|
|
#Setting S_StrictPointDistribution False as "Give points only to players who complete all segments"
|
|
#Setting S_AllowCam7DuringWaitingScreen False as ""
|
|
#Setting S_RoundWaitingScreenDuration 20 as _("Round waiting screen duration") //< Maximum time spent waiting for players at the beginning of each round
|
|
#Setting S_FinisherBonusBase 5 as "Base of Bonus for finishers"
|
|
#Setting S_FinisherBonusNumber 10 as "if only one player finish, they get S_FinisherBonusBase x This value. if 2, it's This - 1, etc..."
|
|
|
|
#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")
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
// Constants
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
#Const C_ModeName "RoyalRounds"
|
|
//L16N [Rounds] Description of the mode rules
|
|
#Const Description _("$zIn $<$t$6F9RoyalRounds$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
|
|
|
|
#Const C_DisableSkipOutro True //< Prevent the players from pressing respawn/give up to cut the finish outro and respawn faster
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
// 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);
|
|
Log::RegisterScript(UIModules_Checkpoint::ScriptName, UIModules_Checkpoint::Version);
|
|
Log::RegisterScript(UIModules_SmallScoresTable::ScriptName, UIModules_SmallScoresTable::Version);
|
|
***
|
|
|
|
***Match_LoadLibraries***
|
|
***
|
|
StateMgr::Load();
|
|
***
|
|
|
|
***Match_UnloadLibraries***
|
|
***
|
|
StateMgr::Unload();
|
|
***
|
|
|
|
***Match_Settings***
|
|
***
|
|
MB_Settings_UseDefaultHud = (C_HudModulePath == "");
|
|
MB_Settings_UseDefaultPodiumSequence = False;
|
|
MB_Settings_UseDefaultIntroSequence = 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::SetVisibleFor(UIModules_Checkpoint::C_Target_None);
|
|
UIModules_TimeGap::SetTimeGapMode(UIModules_TimeGap::C_TimeGapMode_Hidden);
|
|
UIModules_PauseMenu_Online::SetHelp(Description);
|
|
// Hide SM Overlay
|
|
UIManager.UIAll.OverlayHideSpectatorInfos = True;
|
|
UIManager.UIAll.OverlayHideCountdown = True;
|
|
|
|
SetML();
|
|
***
|
|
|
|
***Match_Yield***
|
|
***
|
|
foreach (Event in PendingEvents) {
|
|
switch (Event.Type) {
|
|
// Initialize players when they join the server
|
|
case CSmModeEvent::EType::OnPlayerAdded: {
|
|
StateMgr::InitializePlayer(Event.Player);
|
|
}
|
|
}
|
|
}
|
|
|
|
StateMgr::Yield();
|
|
***
|
|
|
|
***Match_InitServer***
|
|
***
|
|
declare Integer Server_PointsLimit;
|
|
declare Integer Server_RoundsPerMap;
|
|
declare Integer Server_MapsPerMatch;
|
|
declare Integer Server_SegmentsPerRound;
|
|
***
|
|
|
|
***Match_StartServer***
|
|
***
|
|
// Initialize mode
|
|
Clans::SetClansNb(0);
|
|
Scores::SaveInScore(Scores::C_Points_Match);
|
|
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
|
UsePvECollisions = False; //< Synchronize obstacles between all players
|
|
Race::UseAutomaticDossardColor(False);
|
|
|
|
Server_PointsLimit = S_PointsLimit - 1;
|
|
Server_RoundsPerMap = S_RoundsPerMap - 1;
|
|
Server_MapsPerMatch = S_MapsPerMatch - 1;
|
|
Server_SegmentsPerRound = S_SegmentsPerRound - 1;
|
|
***
|
|
|
|
***Match_InitMap***
|
|
***
|
|
declare netwrite Text Net_ScriptEnvironment for Teams[0] = S_ScriptEnvironment;
|
|
if (Net_ScriptEnvironment != S_ScriptEnvironment) {
|
|
Net_ScriptEnvironment = S_ScriptEnvironment;
|
|
}
|
|
declare Integer Map_ValidRoundsNb;
|
|
declare Boolean Map_Skipped;
|
|
|
|
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb, S_SegmentsPerRound);
|
|
|
|
// Find start blocks
|
|
declare CMapLandmark[] Starts = Map::GetStarts();
|
|
declare CMapLandmark[Integer] SortedStarts;
|
|
foreach (Start in Starts) {
|
|
SortedStarts[Start.Order] = Start;
|
|
}
|
|
SortedStarts = SortedStarts.sortkey();
|
|
|
|
declare CMapLandmark[] Map_Starts for This;
|
|
Map_Starts = [];
|
|
foreach (Start in SortedStarts) {
|
|
Map_Starts.add(Start);
|
|
}
|
|
|
|
if (Map_Starts.count > 0) {
|
|
Map::SetDefaultStart(Map_Starts[0]);
|
|
} else {
|
|
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
|
UIManager.UIAll.QueueMessage(3000, 1, CUIConfig::EMessageDisplay::Big, _("This map is not valid"));
|
|
MB_Sleep(3000);
|
|
MB_StopMap();
|
|
}
|
|
|
|
// Waiting screen
|
|
declare Integer WaitingScreenDuration = 0;
|
|
if (WaitingScreenDuration >= 0) {
|
|
if (S_AllowCam7DuringWaitingScreen) ModeUtils::PushAndApplyUISequence(UIManager.UIAll, CUIConfig::EUISequence::Playing);
|
|
else ModeUtils::PushAndApplyUISequence(UIManager.UIAll, CUIConfig::EUISequence::RollingBackgroundIntro);
|
|
|
|
// Wait for the connection of the first valid player to start the countdown
|
|
while (MB_MapIsRunning() && AllPlayers.count <= 0) {
|
|
MB_Sleep(1000);
|
|
}
|
|
|
|
UIModules_BigMessage::SetMessage("""The map will start in few seconds""");
|
|
while (MB_MapIsRunning() && S_RoundWaitingScreenDuration - WaitingScreenDuration > 0) {
|
|
if (S_RoundWaitingScreenDuration - WaitingScreenDuration <= 3) {
|
|
ModeUtils::PlaySound(CUIConfig::EUISound::PhaseChange, 0);
|
|
UIModules_BigMessage::SetMessage("""The map starts in {{{S_RoundWaitingScreenDuration - WaitingScreenDuration}}} seconds""");
|
|
}
|
|
WaitingScreenDuration = WaitingScreenDuration + 1;
|
|
MB_Sleep(1000);
|
|
}
|
|
UIModules_BigMessage::SetMessage("");
|
|
|
|
ModeUtils::PopAndApplyUISequence(UIManager.UIAll);
|
|
}
|
|
***
|
|
|
|
***Match_StartMap***
|
|
***
|
|
// Add bot when necessary
|
|
Users_SetNbFakeUsers(C_FakeUsersNb, 0);
|
|
|
|
Map_Skipped = True;
|
|
|
|
StartTime = Now + Race::C_SpawnDuration;
|
|
|
|
UIModules_ScoresTable::SetFooterInfo(_("Warm up"));
|
|
MB_WarmUp(S_WarmUpNb, S_WarmUpDuration * 1000, S_WarmUpTimeout * 1000);
|
|
***
|
|
|
|
***Match_StartWarmUpRound***
|
|
***
|
|
declare netwrite Boolean Net_RoyalRounds_WarmUpUI_IsActive for Teams[0];
|
|
Net_RoyalRounds_WarmUpUI_IsActive = True;
|
|
|
|
foreach (Player in Players) {
|
|
declare netwrite Integer Net_RoyalRounds_WarmUpUI_SelectedSegment for Player = 1;
|
|
Player.LandmarkOrderSelector_Race = Net_RoyalRounds_WarmUpUI_SelectedSegment;
|
|
}
|
|
***
|
|
|
|
***Match_WarmUpLoop***
|
|
***
|
|
// Manage Custom UI Events
|
|
foreach (Event in UIManager.PendingEvents) {
|
|
Log::Log("[UIManager] Event.CustomEventType: " ^ Event.CustomEventType);
|
|
if (TL::StartsWith("Request.WarmUp.Segment.", Event.CustomEventType)) {
|
|
declare Text Target = Event.CustomEventData[0];
|
|
declare CSmPlayer Player = GetPlayer(Target);
|
|
if (Player != Null) {
|
|
declare CMapLandmark[] Map_Starts for This;
|
|
declare netwrite Integer Net_RoyalRounds_WarmUpUI_SelectedSegment for Player = 1;
|
|
declare Integer NewSegment;
|
|
if (Event.CustomEventType == "Request.WarmUp.Segment.Minus") {
|
|
if (Net_RoyalRounds_WarmUpUI_SelectedSegment == 1) NewSegment = 5;
|
|
else NewSegment = Net_RoyalRounds_WarmUpUI_SelectedSegment - 1;
|
|
} else if (Event.CustomEventType == "Request.WarmUp.Segment.Plus") {
|
|
if (Net_RoyalRounds_WarmUpUI_SelectedSegment == 5) NewSegment = 1;
|
|
else NewSegment = Net_RoyalRounds_WarmUpUI_SelectedSegment + 1;
|
|
}
|
|
Player.LandmarkOrderSelector_Race = NewSegment;
|
|
Net_RoyalRounds_WarmUpUI_SelectedSegment = NewSegment;
|
|
Race::SetPlayerDefaultStart(Player, Map_Starts[NewSegment-1]);
|
|
Race::StopSkipOutro(Player);
|
|
}
|
|
}
|
|
}
|
|
***
|
|
|
|
***Match_EndWarmUpRound***
|
|
***
|
|
declare netwrite Boolean Net_RoyalRounds_WarmUpUI_IsActive for Teams[0];
|
|
Net_RoyalRounds_WarmUpUI_IsActive = False;
|
|
***
|
|
|
|
***Rounds_SpawnPlayer***
|
|
***
|
|
declare Integer CurrentSegment for Player.Score = -1;
|
|
|
|
// Init player if newly connected
|
|
if (CurrentSegment == -1) {
|
|
CurrentSegment = 1;
|
|
CurrentRanking[0][Player.User.WebServicesUserId] = 0;
|
|
if (!UpdateRankingSegments.exists(0)) UpdateRankingSegments.add(0);
|
|
if (UpdateRankingTimer == 0) UpdateRankingTimer = Now + 1000;
|
|
}
|
|
|
|
declare Integer Index;
|
|
if (CurrentSegment > Map_Starts.count) {
|
|
Index = Map_Starts.count - 1;
|
|
} else {
|
|
Index = CurrentSegment - 1;
|
|
}
|
|
switch (CurrentSegment) {
|
|
case 1: Player.Dossard_Color = Race::C_DossardColor_Default; // White
|
|
case 2: Player.Dossard_Color = <0.063, 0.816, 0.125>; // Green
|
|
case 3: Player.Dossard_Color = <0.125, 0.251, 0.753>; // Blue
|
|
case 4: Player.Dossard_Color = <0.8, 0.2, 0.2>; // Red
|
|
default: Player.Dossard_Color = <0.2, 0.2, 0.2>; // Black
|
|
}
|
|
Player.LandmarkOrderSelector_Race = Index + 1;
|
|
declare Integer SpecificStartTime = Now + Race::C_SpawnDuration;
|
|
if (IsStartRound) SpecificStartTime = StartTime;
|
|
Race::Start(Player, Map_Starts[Index] , SpecificStartTime);
|
|
***
|
|
|
|
***Rounds_PlayerSpawned***
|
|
***
|
|
declare Integer SpecificOffset = Player.StartTime - StartTime;
|
|
if (IsStartRound) SpecificOffset = 0;
|
|
UIModules_Chrono::SetTimeOffset(Player, SpecificOffset);
|
|
***
|
|
|
|
***Match_InitRound***
|
|
***
|
|
declare netwrite Integer Net_RoyalRounds_CheckpointUI_TotalNbSegments for Teams[0];
|
|
declare Integer[Text] CustomTimes for This = [];
|
|
declare Integer[Text][Integer] CurrentRanking for This = []; // CurrentRanking[Segment][AccountId][Time]
|
|
CurrentRanking[0] = Integer[Text]; // Init white section
|
|
declare Boolean IsStartRound = True;
|
|
|
|
// Reset players for the race
|
|
foreach (Score in Scores) {
|
|
declare Integer CurrentSegment for Score = -1;
|
|
CurrentSegment = 1;
|
|
CurrentRanking[0][Score.User.WebServicesUserId] = 0;
|
|
}
|
|
|
|
declare Integer UpdateRankingTimer = 1;
|
|
declare Integer[] UpdateRankingSegments;
|
|
UpdateRankingSegments.add(0);
|
|
***
|
|
|
|
***Match_StartRound***
|
|
***
|
|
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb, S_SegmentsPerRound);
|
|
StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]);
|
|
|
|
IsStartRound = False;
|
|
***
|
|
|
|
***Match_PlayLoop***
|
|
***
|
|
// Manage race events
|
|
declare RacePendingEvents = Race::GetPendingEvents();
|
|
foreach (Event in RacePendingEvents) {
|
|
if (Event.Type == Events::C_Type_SkipOutro && C_DisableSkipOutro) {
|
|
Race::InvalidEvent(Event);
|
|
} else {
|
|
Race::ValidEvent(Event);
|
|
|
|
// Waypoint
|
|
if (Event.Type == Events::C_Type_Waypoint) {
|
|
if (Event.Player != Null) {
|
|
if (Event.IsEndRace) {
|
|
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}}}""");
|
|
if (CurrentRanking.existskey(CurrentSegment-1)) CurrentRanking[CurrentSegment-1].removekey(Event.Player.User.WebServicesUserId) ;
|
|
if (!CurrentRanking.existskey(CurrentSegment)) CurrentRanking[CurrentSegment] = Integer[Text];
|
|
CurrentRanking[CurrentSegment][Event.Player.User.WebServicesUserId] = Event.Player.StartTime - StartTime + Event.RaceTime;
|
|
|
|
// Update Ranking of the current players
|
|
declare Integer Rank = 1;
|
|
declare Integer I = 1;
|
|
Rank = CurrentRanking[CurrentSegment].count;
|
|
while (CurrentRanking.existskey(CurrentSegment + I)) {
|
|
Rank = Rank + CurrentRanking[CurrentSegment + I].count;
|
|
I = I + 1;
|
|
}
|
|
Event.Player.Dossard_Number = TL::FormatInteger(ML::Clamp(Rank, 0, 99), 2);
|
|
|
|
// Send Rank to Checkpoint UI
|
|
declare netwrite Integer Net_RoyalRounds_CheckpointUI_Rank for Event.Player;
|
|
Net_RoyalRounds_CheckpointUI_Rank = Rank;
|
|
declare netwrite Integer Net_RoyalRounds_CheckpointUI_Update for Event.Player;
|
|
Net_RoyalRounds_CheckpointUI_Update += 1;
|
|
declare netwrite Integer Net_RoyalRounds_CheckpointUI_CurrentNbSegments for Event.Player;
|
|
Net_RoyalRounds_CheckpointUI_CurrentNbSegments = CurrentSegment;
|
|
|
|
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;
|
|
ModeRounds_CanSpawn = True;
|
|
CurrentSegment = CurrentSegment + 1;
|
|
Race::StopSkipScoresTable(Event.Player);
|
|
} else {
|
|
Scores::UpdatePlayerPrevRace(Event.Player);
|
|
UpdateCustomRanking(Event.Player);
|
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
|
if (EndTime <= 0) {
|
|
EndTime = GetFinishTimeout(S_FinishTimeout);
|
|
}
|
|
}
|
|
|
|
// Add to trigger UpdateRanking for all players affected
|
|
if (!UpdateRankingSegments.exists(CurrentSegment-1)) UpdateRankingSegments.add(CurrentSegment-1);
|
|
if (UpdateRankingTimer == 0) UpdateRankingTimer = Now + 1000;
|
|
}
|
|
}
|
|
} else if (Event.Type == Events::C_Type_GiveUp) {
|
|
Log::Log("""[RacePendingEvents] Player {{{Event.Player.User.Name }}} give-up""");
|
|
if (Event.Player != Null) {
|
|
declare Integer CurrentSegment for Event.Player.Score = -1;
|
|
if (CurrentRanking.existskey(CurrentSegment-1)) CurrentRanking[CurrentSegment-1].removekey(Event.Player.User.WebServicesUserId);
|
|
while (CurrentSegment >= 1) {
|
|
if (!UpdateRankingSegments.exists(CurrentSegment-1)) UpdateRankingSegments.add(CurrentSegment-1);
|
|
CurrentSegment = CurrentSegment - 1;
|
|
}
|
|
if (UpdateRankingTimer == 0) UpdateRankingTimer = Now + 1000;
|
|
declare Boolean ModeRounds_CanSpawn for Event.Player.Score = Rounds_Settings_CanSpawnDefault;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Manage mode events
|
|
foreach (Event in PendingEvents) {
|
|
if (Event.HasBeenPassed || Event.HasBeenDiscarded) continue;
|
|
Events::Invalid(Event);
|
|
}
|
|
|
|
if (UpdateRankingTimer > 0 && Now > UpdateRankingTimer) {
|
|
UpdateRankingSegments = UpdateRankingSegments.sortreverse();
|
|
|
|
foreach (Segment in UpdateRankingSegments) {
|
|
Log::Log("[UpdateRanking] Updating ranking of players of the segment " ^ Segment);
|
|
declare Integer I = 1;
|
|
declare Integer Rank = 1;
|
|
while (CurrentRanking.existskey(Segment + I)) {
|
|
Rank = Rank + CurrentRanking[Segment + I].count;
|
|
I = I + 1;
|
|
}
|
|
|
|
if (CurrentRanking.existskey(Segment)) {
|
|
if (Segment == 0) Rank = Rank + CurrentRanking[0].count - 1;
|
|
foreach (ID => Time in CurrentRanking[Segment]) {
|
|
declare CSmPlayer Player = ModeUtils::GetPlayerFromAccountId(ID);
|
|
if (Player != Null) Player.Dossard_Number = TL::FormatInteger(ML::Clamp(Rank, 0, 99), 2);
|
|
if (Segment != 0) Rank = Rank + 1;
|
|
}
|
|
}
|
|
}
|
|
UpdateRankingTimer = 0;
|
|
UpdateRankingSegments.clear();
|
|
}
|
|
|
|
// Server info change
|
|
if (
|
|
Server_PointsLimit != S_PointsLimit ||
|
|
Server_RoundsPerMap != S_RoundsPerMap ||
|
|
Server_MapsPerMatch != S_MapsPerMatch ||
|
|
Server_SegmentsPerRound != S_SegmentsPerRound
|
|
) {
|
|
Server_PointsLimit = S_PointsLimit;
|
|
Server_RoundsPerMap = S_RoundsPerMap;
|
|
Server_MapsPerMatch = S_MapsPerMatch;
|
|
Server_SegmentsPerRound = S_SegmentsPerRound;
|
|
|
|
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb, S_SegmentsPerRound);
|
|
}
|
|
|
|
if (Net_ScriptEnvironment != S_ScriptEnvironment) {
|
|
Net_ScriptEnvironment = S_ScriptEnvironment;
|
|
}
|
|
|
|
if (Net_RoyalRounds_CheckpointUI_TotalNbSegments != S_SegmentsPerRound) {
|
|
Net_RoyalRounds_CheckpointUI_TotalNbSegments = S_SegmentsPerRound;
|
|
}
|
|
***
|
|
|
|
***Rounds_CheckStopRound***
|
|
***
|
|
// End the round
|
|
// If All players finished
|
|
if (Players.count > 0 && PlayersNbAlive <= 0) {
|
|
declare Boolean NoOneCanPlay = True;
|
|
foreach (Player in Players) {
|
|
declare Boolean ModeRounds_CanSpawn for Player.Score = Rounds_Settings_CanSpawnDefault;
|
|
if (ModeRounds_CanSpawn) {
|
|
NoOneCanPlay = False;
|
|
break;
|
|
}
|
|
}
|
|
if (NoOneCanPlay) {
|
|
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]);
|
|
|
|
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;
|
|
// Get the last round points
|
|
UpdateCustomRanking(Null);
|
|
Race::SortScores(Race::C_Sort_TotalPoints);
|
|
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
|
UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound;
|
|
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();
|
|
}
|
|
}
|
|
CustomTimes.clear();
|
|
CurrentRanking.clear();
|
|
UIModules_SmallScoresTable::ResetCustomTimes();
|
|
UIModules_SmallScoresTable::ResetCustomResults();
|
|
***
|
|
|
|
***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);
|
|
Scores::SetPlayerWinner(Scores::GetBestPlayer(Scores::C_Sort_MatchPoints));
|
|
***
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
// Functions
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
/** Update the scores table footer text
|
|
*
|
|
* @param _PointsLimit The points limit
|
|
* @param _RoundsPerMap The number of round per map
|
|
* @param _MapsPerMatch The number of maps per match
|
|
* @param _ValidRoundsNb Number of valid rounds played
|
|
* @param _SegmentsPerRound The number of segment per round
|
|
*/
|
|
Void UpdateScoresTableFooter(Integer _PointsLimit, Integer _RoundsPerMap, Integer _MapsPerMatch, Integer _ValidRoundsNb, Integer _SegmentsPerRound) {
|
|
declare Text[] Parts;
|
|
declare Text Message = "";
|
|
if (_PointsLimit > 0) {
|
|
if (Parts.count > 0) Message ^= " | ";
|
|
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 ^= " | ";
|
|
Message ^= """%{{{Parts.count + 1}}}{{{ML::Min(_ValidRoundsNb+1, _RoundsPerMap)}}}/{{{_RoundsPerMap}}}""";
|
|
//L16N [Rounds] Number of rounds played during the map.
|
|
Parts.add(_("Rounds : "));
|
|
}
|
|
if (_MapsPerMatch > 0) {
|
|
if (Parts.count > 0) Message ^= " | ";
|
|
Message ^= """%{{{Parts.count + 1}}}{{{MB_GetMapCount()}}}/{{{_MapsPerMatch}}}""";
|
|
//L16N [Rounds] Number of maps played during the match.
|
|
Parts.add(_("Maps : "));
|
|
}
|
|
if (_SegmentsPerRound != 5) {
|
|
if (Parts.count > 0) Message ^= " | ";
|
|
Message ^= """%{{{Parts.count + 1}}}{{{_SegmentsPerRound}}}""";
|
|
Parts.add("Segments : ");
|
|
}
|
|
|
|
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]));
|
|
case 4: UIModules_ScoresTable::SetFooterInfo(TL::Compose(Message, Parts[0], Parts[1], Parts[2], Parts[3]));
|
|
}
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
/** 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 + Map.TMObjective_AuthorTime / 6;
|
|
}
|
|
|
|
return Now + FinishTimeout;
|
|
}
|
|
|
|
/** Update the Scores Table with Real Time
|
|
*
|
|
* @param _Player The Player who end the round
|
|
*/
|
|
Void UpdateCustomRanking(CSmPlayer _Player) {
|
|
declare Integer[Text] CustomTimes for This;
|
|
declare Text[Text] CustomResult;
|
|
declare Integer[Text][Integer] CurrentRanking for This = [];
|
|
declare Integer[] PointsRepartition = PointsRepartition::GetPointsRepartition();
|
|
|
|
if (_Player != Null) {
|
|
declare Integer CurrentSegment for _Player.Score;
|
|
CustomTimes[_Player.User.WebServicesUserId] = CurrentRanking[CurrentSegment][_Player.User.WebServicesUserId];
|
|
} else {
|
|
if (!S_StrictPointDistribution) {
|
|
CustomTimes.clear();
|
|
CurrentRanking = CurrentRanking.sortkeyreverse();
|
|
declare Integer LastTimeFromThePreviousSegment = 0;
|
|
declare Integer LastTime = 0;
|
|
foreach (Segment => DummyVar in CurrentRanking) {
|
|
if (Segment == 0) break;
|
|
if (CustomTimes.count > 0) LastTimeFromThePreviousSegment = LastTime;
|
|
foreach (ID => Time in CurrentRanking[Segment]) {
|
|
CustomTimes[ID] = Time + LastTimeFromThePreviousSegment;
|
|
LastTime = Time + LastTimeFromThePreviousSegment;
|
|
if (LastTimeFromThePreviousSegment > 0) CustomResult[ID] = """({{{Segment - S_SegmentsPerRound}}}) {{{TL::TimeToText(Time, True, True)}}}""";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
declare Integer BonusForFinishers = 0;
|
|
if (S_FinisherBonusBase > 0 && S_FinisherBonusNumber > 0 && CurrentRanking.existskey(S_SegmentsPerRound)) {
|
|
BonusForFinishers = ML::Max(0, S_FinisherBonusBase * (S_FinisherBonusNumber + 1 - CurrentRanking[S_SegmentsPerRound].count));
|
|
if (_Player == Null && BonusForFinishers > 0) {// If End round
|
|
UIManager.UIAll.SendChat("$<$ff3$> " ^ CurrentRanking[S_SegmentsPerRound].count ^ " players received " ^ BonusForFinishers ^ " bonus points for being the only finishers" );
|
|
}
|
|
}
|
|
|
|
if (PointsRepartition.count > 0) {
|
|
CustomTimes = CustomTimes.sort();
|
|
declare Integer I = 0;
|
|
foreach (AccountId => DummyVar in CustomTimes) {
|
|
declare CSmPlayer Player = ModeUtils::GetPlayerFromAccountId(AccountId);
|
|
if (Player == Null) continue;
|
|
|
|
declare Integer Points = 0;
|
|
if (PointsRepartition.existskey(I)) {
|
|
Points = PointsRepartition[I];
|
|
} else {
|
|
Points = PointsRepartition[PointsRepartition.count - 1];
|
|
}
|
|
declare Integer CurrentSegment for Player.Score;
|
|
if (CurrentSegment == S_SegmentsPerRound) {
|
|
Points += BonusForFinishers;
|
|
}
|
|
Scores::SetPlayerRoundPoints(Player.Score, Points);
|
|
I += 1;
|
|
}
|
|
}
|
|
UIModules_SmallScoresTable::SetCustomTimes(CustomTimes);
|
|
UIModules_SmallScoresTable::SetCustomResults(CustomResult);
|
|
}
|
|
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
|
/// 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) {
|
|
if (PointsLimitReached(_UseTieBreak, _PointsLimit) == 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;
|
|
}
|
|
|
|
/**
|
|
* Set the UI
|
|
*/
|
|
Void SetML() {
|
|
declare Text MLText = """
|
|
<manialink name="ML_RoyalRounds_CheckpointUI" version="3">
|
|
<script><!--
|
|
#Include "TextLib" as TL
|
|
|
|
main() {
|
|
declare Ident Last_GUIPlayerId;
|
|
declare Integer LastUpdate = 0;
|
|
|
|
declare Frame_Global <=> (Page.GetFirstChild("frame-global") as CMlFrame);
|
|
declare Label_Rank <=> (Page.GetFirstChild("label-rank") as CMlLabel);
|
|
declare Label_Time <=> (Page.GetFirstChild("label-time") as CMlLabel);
|
|
declare Label_Segments <=> (Page.GetFirstChild("label-segments") as CMlLabel);
|
|
|
|
declare netread Integer Net_RoyalRounds_CheckpointUI_TotalNbSegments for Teams[0];
|
|
|
|
while(True) {
|
|
yield;
|
|
|
|
if (GUIPlayer != Null) {
|
|
declare netread Integer Net_RoyalRounds_CheckpointUI_Rank for GUIPlayer;
|
|
declare netread Integer Net_RoyalRounds_CheckpointUI_CurrentNbSegments for GUIPlayer;
|
|
declare netread Integer Net_RoyalRounds_CheckpointUI_Update for GUIPlayer;
|
|
|
|
if (LastUpdate != Net_RoyalRounds_CheckpointUI_Update) {
|
|
if (GUIPlayer.RaceWaypointTimes.count <= 0) continue; // skip if there is any latency (useful for spectators)
|
|
LastUpdate = Net_RoyalRounds_CheckpointUI_Update;
|
|
if (Net_RoyalRounds_CheckpointUI_Rank > 0) {
|
|
Label_Time.Value = TL::TimeToText(GUIPlayer.RaceWaypointTimes[0], True, True);
|
|
Label_Rank.Value = TL::FormatRank(Net_RoyalRounds_CheckpointUI_Rank, False);
|
|
Label_Segments.Value = TL::Compose("Segment %1/%2", TL::ToText(Net_RoyalRounds_CheckpointUI_CurrentNbSegments), TL::ToText(Net_RoyalRounds_CheckpointUI_TotalNbSegments));
|
|
|
|
Frame_Global.Visible = True;
|
|
AnimMgr.Flush(Frame_Global);
|
|
AnimMgr.Add(Frame_Global, "<frame hidden=\"1\" />", Now, 3000, CAnimManager::EAnimManagerEasing::Linear);
|
|
}
|
|
}
|
|
if (Last_GUIPlayerId != GUIPlayer.Id) {
|
|
Last_GUIPlayerId = GUIPlayer.Id;
|
|
if (Frame_Global.Visible) {
|
|
AnimMgr.Flush(Frame_Global);
|
|
Frame_Global.Visible = False;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
--></script>
|
|
<stylesheet>
|
|
<style class="text-default" textfont="Oswald" textcolor="ffffff" textsize="3" />
|
|
<style class="text-number" textfont="OswaldMono" textcolor="ffffff" textsize="4" />
|
|
</stylesheet>
|
|
<frame z-index="-2" hidden="1" id="frame-global" pos="-10 45">
|
|
<frame id="frame-checkpoint">
|
|
<frame pos="0 -12" id="frame-race">
|
|
<frame pos="0 5" id="frame-race-time" scale="0.9">
|
|
<quad z-index="0" size="20 8" valign="center" bgcolor="000000" opacity="0.6" />
|
|
<label pos="19 0" z-index="1" size="18 10" halign="right" valign="center2" class="text-number" id="label-time" />
|
|
</frame>
|
|
<frame pos="0 5" id="frame-race-rank" scale="0.9">
|
|
<quad z-index="0" size="10 8" halign="right" valign="center" bgcolor="000000" opacity="0.6" id="quad-race-rank" />
|
|
<label pos="-0.5 0" z-index="1" size="8.5 10" halign="right" valign="center2" textsize="3" textemboss="1" class="text-default" id="label-rank" />
|
|
</frame>
|
|
<frame pos="0 -2.25" id="frame-race-segment" scale="0.9">
|
|
<quad z-index="0" size="25 8" valign="center" bgcolor="000000" opacity="0.6" />
|
|
<label pos="24 0" z-index="1" size="23 10" halign="right" valign="center2" class="text-default" id="label-segments" />
|
|
</frame>
|
|
</frame>
|
|
</frame>
|
|
</frame>
|
|
</manialink>
|
|
""";
|
|
Layers::Create("ML_RoyalRounds_CheckpointUI", MLText);
|
|
Layers::SetType("ML_RoyalRounds_CheckpointUI", CUILayer::EUILayerType::Normal);
|
|
Layers::Attach("ML_RoyalRounds_CheckpointUI");
|
|
|
|
MLText = """
|
|
<manialink name="ML_RoyalRounds_WarmUpUI" version="3">
|
|
<script><!--
|
|
#Include "TextLib" as TL
|
|
#Include "ColorLib" as CL
|
|
|
|
#Const C_SegmentColor ["fff", "1b2", "24c", "c33", "000" ]
|
|
#Const C_SegmentName ["white", "green", "blue", "red", "black" ]
|
|
|
|
|
|
Boolean InputPlayerIsSpectator() {
|
|
if (GUIPlayer != Null && InputPlayer != Null && GUIPlayer.User.Login == InputPlayer.User.Login) return False;
|
|
return True;
|
|
}
|
|
|
|
Void DevLog(Text _LogText) {
|
|
declare netread Text Net_ScriptEnvironment for Teams[0] = "production";
|
|
if (Net_ScriptEnvironment == "development") log(_LogText);
|
|
}
|
|
|
|
main() {
|
|
declare Integer TimeToDisplay = 0;
|
|
declare Integer LastUpdate = 0;
|
|
|
|
declare Frame_Global <=> (Page.GetFirstChild("frame-global") as CMlFrame);
|
|
declare Label_Text <=> (Page.GetFirstChild("label-text") as CMlLabel);
|
|
declare Quad_Left_Button <=> (Page.GetFirstChild("quad-left-button") as CMlQuad);
|
|
declare Quad_Left_Arrow <=> (Page.GetFirstChild("quad-left-arrow") as CMlQuad);
|
|
declare Quad_Right_Button <=> (Page.GetFirstChild("quad-right-button") as CMlQuad);
|
|
declare Quad_Right_Arrow <=> (Page.GetFirstChild("quad-right-arrow") as CMlQuad);
|
|
|
|
declare netread Boolean Net_RoyalRounds_WarmUpUI_IsActive for Teams[0];
|
|
declare Integer Last_SelectedSegment = 1;
|
|
|
|
while(True) {
|
|
yield;
|
|
|
|
if (Net_RoyalRounds_WarmUpUI_IsActive && GUIPlayer != Null) {
|
|
|
|
declare netread Integer Net_RoyalRounds_WarmUpUI_SelectedSegment for GUIPlayer = 1;
|
|
|
|
foreach(Event in PendingEvents) {
|
|
DevLog("[PendingEvents] Event.Type: " ^ Event.Type);
|
|
|
|
if (!InputPlayerIsSpectator() && Event.Type == CMlScriptEvent::Type::MouseClick && TL::Find("quad-", Event.ControlId, True, True) && TL::Find("-button", Event.ControlId, True, True)) {
|
|
if (Event.ControlId == "quad-left-button") {
|
|
DevLog("[PendingEvents] Player " ^ GUIPlayer.User.Name ^ " ask the previous segment");
|
|
SendCustomEvent("Request.WarmUp.Segment.Minus", [GUIPlayer.User.Login]);
|
|
} else {
|
|
DevLog("[PendingEvents] Player " ^ GUIPlayer.User.Name ^ " ask the next segment");
|
|
SendCustomEvent("Request.WarmUp.Segment.Plus", [GUIPlayer.User.Login]);
|
|
}
|
|
} else if (!InputPlayerIsSpectator() && Event.Type == CMlScriptEvent::Type::MouseOver && TL::Find("quad-", Event.ControlId, True, True) && TL::Find("-button", Event.ControlId, True, True)) {
|
|
declare Quad <=> (Page.GetFirstChild(Event.ControlId) as CMlQuad);
|
|
Quad.Opacity = 0.5;
|
|
} else if (Event.Type == CMlScriptEvent::Type::MouseOut && TL::Find("quad-", Event.ControlId, True, True) && TL::Find("-button", Event.ControlId, True, True)) {
|
|
declare Quad <=> (Page.GetFirstChild(Event.ControlId) as CMlQuad);
|
|
Quad.Opacity = 0.7;
|
|
}
|
|
}
|
|
|
|
if (Last_SelectedSegment != Net_RoyalRounds_WarmUpUI_SelectedSegment) {
|
|
Last_SelectedSegment = Net_RoyalRounds_WarmUpUI_SelectedSegment;
|
|
if (Last_SelectedSegment == 1) Quad_Left_Arrow.Colorize = CL::HexToRgb(C_SegmentColor[4]);
|
|
else Quad_Left_Arrow.Colorize = CL::Hex3ToRgb(C_SegmentColor[Last_SelectedSegment-2]);
|
|
Label_Text.Value = "You are on the $<$" ^ C_SegmentColor[Last_SelectedSegment-1] ^ C_SegmentName[Last_SelectedSegment-1] ^ "$> segment";
|
|
if (Last_SelectedSegment == 5) Quad_Right_Arrow.Colorize = CL::HexToRgb(C_SegmentColor[0]);
|
|
else Quad_Right_Arrow.Colorize = CL::Hex3ToRgb(C_SegmentColor[Last_SelectedSegment]);
|
|
}
|
|
|
|
|
|
if (!Frame_Global.Visible) {
|
|
Frame_Global.Visible = True;
|
|
}
|
|
} else if (Frame_Global.Visible) {
|
|
Frame_Global.Visible = False;
|
|
}
|
|
}
|
|
}
|
|
--></script>
|
|
<frame id="frame-global" hidden=1 pos="0 85">
|
|
<quad pos="0.75 0" z-index="0" size="50 10" opacity="0.7" image="https://files.virtit.fr/TrackMania/UI/tilted_bg_half.dds" colorize="ff6e00" halign="center"/>
|
|
<label pos="1 -5.5" z-index="1" size="38 10" text="WARMUP" textfont="GameFontBlack" halign="center" valign="center2" textprefix="$i" textsize="8"/>
|
|
<quad pos="-0.75 -10" z-index="0" size="50 10" opacity="0.7" image="https://files.virtit.fr/TrackMania/UI/tilted_bg_half.dds" colorize="000" halign="center"/>
|
|
<label id="label-text" pos="-1 -15" z-index="1" size="45 10" text="You are on the $<$fffwhite$> segment" textfont="GameFontSemiBold" halign="center" valign="center2" textprefix="$i" textsize="1.5" textcolor="D8D8D8"/>
|
|
<quad id="quad-left-button" pos="-30 1.9" z-index="0" size="13 23" opacity="0.7" image="https://files.virtit.fr/TrackMania/UI/tilted_bg_left_button.dds" colorize="000" halign="center" scriptevents="1"/>
|
|
<quad id="quad-left-arrow" pos="-30 -5" z-index="1" size="10 10" image="file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds" halign="center" colorize="000"/>
|
|
<quad id="quad-right-button" pos="30.2 1.6" z-index="0" size="13 23.5" opacity="0.7" image="https://files.virtit.fr/TrackMania/UI/tilted_bg_right_button.dds" colorize="000" halign="center" scriptevents="1"/>
|
|
<quad id="quad-right-arrow" pos="30 -5" z-index="1" size="10 10" image="file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_RIGHT_OBLIQUE.dds" halign="center" colorize="1b2"/>
|
|
</frame>
|
|
</manialink>
|
|
""";
|
|
Layers::Create("ML_RoyalRounds_WarmUpUI", MLText);
|
|
Layers::SetType("ML_RoyalRounds_WarmUpUI", CUILayer::EUILayerType::Normal);
|
|
Layers::Attach("ML_RoyalRounds_WarmUpUI");
|
|
}
|