/** * Time Attack mode */ #Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaBase.Script.txt" //#RequireContext CSmMode #Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race" #Const Version "2024-02-25" #Const ScriptName "Modes/TM2020-Gamemodes/TM_ClimbTheMap.Script.txt" // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Libraries // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // #Include "TextLib" as TL #Include "MathLib" as ML #Include "Libs/Nadeo/CMGame/Utils/Task.Script.txt" as Task #Include "Libs/Nadeo/Trackmania/Modes/TimeAttack/StateManager.Script.txt" as StateMgr #Include "Libs/Nadeo/Trackmania/MainMenu/Constants.Script.txt" as MenuConsts // UI from Race #Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/TimeGap_Server.Script.txt" as UIModules_TimeGap #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 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Settings // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // #Setting S_AltitudeUpdateFrequency 500 as "Altitude Update Frequency in ms" #Setting S_XmlRpcUpdateFrequency 10000 as "XmlRpc update frequency in ms" // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Constants // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // #Const C_ModeName "Time Attack" //L16N [Time Attack] Description of the mode rules #Const Description _("$zIn $<$t$6F9Time Attack$> mode, the goal is to set the $<$t$6F9best time$>.\n\nYou have as many tries as you want, and you can $<$t$6F9retry$> when you want by pressing the respawn button.\n\nWhen the time is up, the $<$t$6F9winner$> is the player with the $<$t$6F9best time$>.") #Const C_HudModulePath "" //< Path to the hud module #Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/TimeAttack.Script.txt" //< Url of the mania app #Const C_UploadRecord True #Const C_DisplayRecordGhost False #Const C_DisplayRecordMedal False #Const C_CelebrateRecordGhost False #Const C_CelebrateRecordMedal False #Const C_DisplayWorldTop False #Const C_MlId_LiveAltitude "ClimbTheMap_Altidude" #Const C_Callback_UpdatePBs "Trackmania.ClimbTheMap.UpdatePBs" #Const C_Callback_RequestPB "Trackmania.ClimbTheMap.RequestPB" #Const C_Method_SetPlayersPB "Trackmania.ClimbTheMap.SetPlayersPB" #Const C_Method_SetWR "Trackmania.ClimbTheMap.SetWR" // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // Extends // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ***Match_LogVersions*** *** Log::RegisterScript(ScriptName, Version); Log::RegisterScript(StateMgr::ScriptName, StateMgr::Version); *** ***Match_LoadLibraries*** *** StateMgr::Load(); XmlRpc::RegisterCallback(C_Callback_UpdatePBs, """ * Name: {{{C_Callback_UpdatePBs}}} * Type: CallbackArray * Description: List of the new PB * Data: - Version >=2.0.0: ``` [ "[ "Vvjdn4WBRE6irR6Oie7RpA": 2000, ... ]" ] ``` """); XmlRpc::RegisterCallback(C_Callback_RequestPB, """ * Name: {{{C_Callback_RequestPB}}} * Type: CallbackArray * Description: Request PB to be sure that the Player is initialized by the ManiaScript * Data: - Version >=2.0.0: ``` [ "Vvjdn4WBRE6irR6Oie7RpA" ] ``` """); XmlRpc::RegisterMethod(C_Method_SetPlayersPB, """ * Name: {{{C_Method_SetPlayersPB}}} * Type: TriggerModeScriptEventArray * Description: List of the new PB * Data: - Version >=3.5.0: ``` [ "[ "Vvjdn4WBRE6irR6Oie7RpA": 2000, ... ]" ] ``` """); XmlRpc::RegisterMethod(C_Method_SetWR, """ * Name: {{{C_Method_SetWR}}} * Type: TriggerModeScriptEventArray * Description: WR * Data: - Version >=3.5.0: ``` [ "Beu_", "2000" ] ``` """); *** ***Match_UnloadLibraries*** *** StateMgr::Unload(); XmlRpc::UnregisterCallback(C_Callback_UpdatePBs); XmlRpc::UnregisterCallback(C_Callback_RequestPB); XmlRpc::UnregisterMethod(C_Method_SetPlayersPB); XmlRpc::UnregisterMethod(C_Method_SetWR); *** ***Match_Settings*** *** MB_Settings_UseDefaultTimer = False; MB_Settings_UseDefaultHud = (C_HudModulePath == ""); MB_Settings_UseDefaultPodiumSequence = False; *** ***Match_Rules*** *** ModeInfo::SetName(C_ModeName); ModeInfo::SetType(ModeInfo::C_Type_FreeForAll); ModeInfo::SetRules(Description); ModeInfo::SetStatusMessage(_("TYPE: Free for all\nOBJECTIVE: Set the best time on the track.")); *** ***Match_LoadHud*** *** if (C_HudModulePath != "") Hud_Load(C_HudModulePath); *** ***Match_AfterLoadHud*** *** UIManager.UIAll.ScoreTableOnlyManialink = True; UIModules_Checkpoint::SetRankMode(UIModules_Checkpoint::C_RankMode_BestRace); ClientManiaAppUrl = C_ManiaAppUrl; Race::SortScores(UIModules_ScoresTable::C_Mode_BestTime); UIModules_TimeGap::SetTimeGapMode(UIModules_TimeGap::C_TimeGapMode_BestRace); UIModules_PauseMenu_Online::SetHelp(Description); UIModules_Sign16x9Small::SetScoreMode(UIModules_Sign16x9Small::C_ScoreMode_BestRaceTime); UIManager.UIAll.OverlayHideCountdown = True; UIManager.UIAll.OverlayHideSpectatorInfos = True; UIManager.UIAll.OverlayHideChrono = 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); CarRank::InitializePlayer(Event.Player); XmlRpc::SendCallback(C_Callback_RequestPB, [Event.Player.User.Login]); } } } foreach (Event in XmlRpc.PendingEvents) { if (Event.Type == CXmlRpcEvent::EType::CallbackArray) { if (Event.ParamArray1 == C_Method_SetPlayersPB) { if (Event.ParamArray2.count < 1) continue; declare Integer[Text] AltitudePerLogin; AltitudePerLogin.fromjson(Event.ParamArray2[0]); declare netwrite Int2 Net_ClimbTheMap_AltitudeOfWaypoints for Teams[0]; foreach (Login => RelativeAltitude in AltitudePerLogin) { declare CSmPlayer Player <=> GetPlayer(Login); if (Player == Null) continue; declare Integer Altitude = RelativeAltitude + Net_ClimbTheMap_AltitudeOfWaypoints.X; declare netwrite Integer Net_ClimbTheMap_AltitudeOfPB for Player; if (Net_ClimbTheMap_AltitudeOfPB < Altitude) { Net_ClimbTheMap_AltitudeOfPB = Altitude; } } declare netwrite Integer Net_ClimbTheMap_AltitudePerName_Update for Teams[0]; Net_ClimbTheMap_AltitudePerName_Update += 1; } else if (Event.ParamArray1 == C_Method_SetWR) { if (Event.ParamArray2.count < 3) continue; declare netwrite Int2 Net_ClimbTheMap_AltitudeOfWaypoints for Teams[0]; declare Integer Altitude = ML::Min(TL::ToInteger(Event.ParamArray2[1]) + Net_ClimbTheMap_AltitudeOfWaypoints.X, Net_ClimbTheMap_AltitudeOfWaypoints.Y); declare Integer Time = TL::ToInteger(Event.ParamArray2[2]); declare netwrite Integer Net_ClimbTheMap_TimeOfWR for Teams[0]; declare netwrite Integer Net_ClimbTheMap_AltitudeOfWR for Teams[0]; if ((Time > 0 && Net_ClimbTheMap_TimeOfWR <= 0 || Net_ClimbTheMap_TimeOfWR > Time) || (Time <= 0 && Net_ClimbTheMap_AltitudeOfWR < Altitude)) { Net_ClimbTheMap_TimeOfWR = Time; Net_ClimbTheMap_AltitudeOfWR = Altitude; declare netwrite Text Net_ClimbTheMap_NamefWR for Teams[0]; Net_ClimbTheMap_NamefWR = Event.ParamArray2[0]; declare netwrite Integer Net_ClimbTheMap_AltitudePerName_Update for Teams[0]; Net_ClimbTheMap_AltitudePerName_Update += 1; } } } } StateMgr::Yield(); *** ***Match_StartServer*** *** // Initialize mode Clans::SetClansNb(0); GiveUpBehaviour_RespawnAfter = True; CrudeExtrapolation_AllowDelay = True; Race::SetRespawnBehaviour(Race::C_RespawnBehaviour_GiveUpBeforeFirstCheckpoint); StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]); WarmUp::SetAvailability(True); Race::SetupRecord( MenuConsts::C_ScopeType_Season, MenuConsts::C_ScopeType_PersonalBest, MenuConsts::C_GameMode_TimeAttack, "", C_UploadRecord, C_DisplayRecordGhost, C_DisplayRecordMedal, C_CelebrateRecordGhost, C_CelebrateRecordMedal, C_DisplayWorldTop ); CarRank::Reset(); *** ***Match_InitMap*** *** declare Integer Map_NextUpdate; declare Integer[Text] Map_XmlRpc_AltitudePerLogin_Queue; declare Integer Map_XmlRpc_Queue_NextUpdate; declare netwrite Integer Net_ClimbTheMap_TimeOfWR for Teams[0]; declare netwrite Integer Net_ClimbTheMap_AltitudeOfWR for Teams[0]; declare netwrite Text Net_ClimbTheMap_NamefWR for Teams[0]; declare netwrite Integer[Text] Net_ClimbTheMap_AltitudePerName for Teams[0]; declare netwrite Integer Net_ClimbTheMap_AltitudePerName_Update for Teams[0]; declare netwrite Integer Net_ClimbTheMap_UpdateFrequency for Teams[0] = 500; declare netwrite Int2 Net_ClimbTheMap_AltitudeOfWaypoints for Teams[0]; *** ***Match_StartMap*** *** // Check if the map is valid or not declare CMapLandmark Start = Map::GetStart(); declare CMapLandmark[] Finishes = Map::GetFinishes(); if (Start == Null && Finishes.count == 0) { RaceStateMgr::ForcePlayersStates([RaceStateMgr::C_State_Waiting]); UIModules_BigMessage::SetMessage( _("This map is not valid")); MB_Sleep(3000); UIModules_BigMessage::SetMessage(""); MB_StopMap(); } else { declare netwrite Integer Net_ClimbTheMap_AltitudeOfWaypoints_Update for Teams[0]; declare Real FinishAltitude; foreach (Finish in Finishes) { FinishAltitude = ML::Max(Finish.Position.Y, FinishAltitude); } Net_ClimbTheMap_AltitudeOfWaypoints = ; Net_ClimbTheMap_AltitudeOfWaypoints_Update += 1; Net_ClimbTheMap_UpdateFrequency = S_AltitudeUpdateFrequency; Net_ClimbTheMap_TimeOfWR = 0; Net_ClimbTheMap_AltitudeOfWR = 0; Net_ClimbTheMap_NamefWR = ""; Net_ClimbTheMap_AltitudePerName = []; // Initialize race StartTime = Now + Race::C_SpawnDuration; EndTime = -1; // Spawn players for the race foreach (Player in Players) { Race::Start(Player, StartTime); declare netwrite Integer Net_ClimbTheMap_AltitudeOfPB for Player; Net_ClimbTheMap_AltitudeOfPB = 0; } Net_ClimbTheMap_AltitudePerName_Update += 1; StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]); CarRank::Update(CarRank::C_SortCriteria_BestRace); Race::EnableIntroDuringMatch(True); } *** ***Match_PlayLoop*** *** // Manage race events declare Events::K_RaceEvent[] RacePendingEvents = Race::GetPendingEvents(); foreach (Event in RacePendingEvents) { Race::ValidEvent(Event); // Waypoint if (Event.Type == Events::C_Type_Waypoint) { if (Event.Player != Null) { if (Event.IsEndRace) { // Change Score Scores::UpdatePlayerBestRaceIfBetter(Event.Player); Scores::UpdatePlayerBestLapIfBetter(Event.Player); Scores::UpdatePlayerPrevRace(Event.Player); declare Integer Time = Event.Player.RaceWaypointTimes[Event.Player.RaceWaypointTimes.count - 1]; if (Net_ClimbTheMap_TimeOfWR <= 0 || Net_ClimbTheMap_TimeOfWR > Time) { Net_ClimbTheMap_TimeOfWR = Time; declare netwrite Int2 Net_ClimbTheMap_AltitudeOfWaypoints for Teams[0]; Net_ClimbTheMap_AltitudeOfWR = Net_ClimbTheMap_AltitudeOfWaypoints.Y; Net_ClimbTheMap_NamefWR = Event.Player.User.Name; } } CarRank::ThrottleUpdate(CarRank::C_SortCriteria_BestRace); } } } if (Now > Map_NextUpdate) { Map_NextUpdate = Now + S_AltitudeUpdateFrequency; declare Integer[Text] AltitudePerName; foreach (Player in Players) { if (Player.User == Null) continue; if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned) continue; declare Integer Altitude = ML::FloorInteger(Player.Position.Y); if (Net_ClimbTheMap_TimeOfWR <= 0 && Net_ClimbTheMap_AltitudeOfWR < Altitude) { Net_ClimbTheMap_AltitudeOfWR = ML::Min(Altitude, Net_ClimbTheMap_AltitudeOfWaypoints.Y); Net_ClimbTheMap_NamefWR = Player.User.Name; } declare netwrite Integer Net_ClimbTheMap_AltitudeOfPB for Player; if (Net_ClimbTheMap_AltitudeOfPB < Altitude) { Net_ClimbTheMap_AltitudeOfPB = Altitude; // Map_XmlRpc_AltitudePerLogin_Queue is relative with the Start waypoint Map_XmlRpc_AltitudePerLogin_Queue[Player.User.Login] = Altitude - Net_ClimbTheMap_AltitudeOfWaypoints.X; } AltitudePerName[Player.User.Name] = Altitude; } Net_ClimbTheMap_AltitudePerName = AltitudePerName; Net_ClimbTheMap_AltitudePerName_Update += 1; } if (Now > Map_XmlRpc_Queue_NextUpdate) { Map_XmlRpc_Queue_NextUpdate = Now + S_XmlRpcUpdateFrequency; if (Map_XmlRpc_AltitudePerLogin_Queue.count > 0) { XmlRpc::SendCallback(C_Callback_UpdatePBs, [Map_XmlRpc_AltitudePerLogin_Queue.tojson()]); Map_XmlRpc_AltitudePerLogin_Queue = []; } } // Manage mode events foreach (Event in PendingEvents) { if (Event.HasBeenPassed || Event.HasBeenDiscarded) continue; Events::Invalid(Event); } // Spawn players if (PlayersNbDead > 0) { //< Check for unspawned players only if at least one player is unspawned foreach (Player in Players) { if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned && Race::IsReadyToStart(Player)) { Race::Start(Player); } } } if (Net_ClimbTheMap_UpdateFrequency != S_AltitudeUpdateFrequency) { Net_ClimbTheMap_UpdateFrequency = S_AltitudeUpdateFrequency; } *** ***Match_EndMap*** *** // Ensure that we stop the match (after a vote for the next map, ...) MB_StopMatch(); // Flush queue if (Map_XmlRpc_AltitudePerLogin_Queue.count > 0) { XmlRpc::SendCallback(C_Callback_UpdatePBs, [Map_XmlRpc_AltitudePerLogin_Queue.tojson()]); Map_XmlRpc_AltitudePerLogin_Queue = []; } StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]); Race::EnableIntroDuringMatch(False); CarRank::Update(CarRank::C_SortCriteria_BestRace); Race::StopSkipOutroAll(); *** ***Match_BeforePodiumSequence*** *** ModeUtils::PlaySound(CUIConfig::EUISound::EndRound, 0); UIModules_BigMessage::SetMessage("Changing Map"); MB_Sleep(5000); *** ***Match_AfterPodiumSequence*** *** UIModules_BigMessage::SetMessage(""); *** Void SetMl() { declare Text FrameInstances; for (I, 0, 100) { FrameInstances ^= """