diff --git a/TM_ClimbTheMap.Script.txt b/TM_ClimbTheMap.Script.txt new file mode 100644 index 0000000..f5b8e9b --- /dev/null +++ b/TM_ClimbTheMap.Script.txt @@ -0,0 +1,452 @@ +/** +* Time Attack mode +*/ +#Extends "Libs/Nadeo/TMNext/TrackMania/Modes/TMNextBase.Script.txt" + +//#RequireContext CSmMode + +#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race" +#Const Version "2023-09-04" +#Const ScriptName "Modes/TrackMania/TM_TimeAttack_Online.Script.txt" + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// Libraries +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +#Include "TextLib" as TL +#Include "MathLib" as ML +#Include "Libs/Nadeo/CommonLibs/Common/Task.Script.txt" as Task +#Include "Libs/Nadeo/TMNext/TrackMania/Modes/TimeAttack/StateManager.Script.txt" as StateMgr +#Include "Libs/Nadeo/TMNext/TrackMania/Modes/TrophyRanking.Script.txt" as TrophyRanking +#Include "Libs/Nadeo/TMNext/TrackMania/Menu/Constants.Script.txt" as MenuConsts +#Include "Libs/Nadeo/CommonLibs/Common/Tracking.Script.txt" as Tracking +// UI from Race +#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/TimeGap_Server.Script.txt" as UIModules_TimeGap +#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/Checkpoint_Server.Script.txt" as UIModules_Checkpoint +#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/PauseMenuOnline_Server.Script.txt" as UIModules_PauseMenu_Online +#Include "ManiaApps/Nadeo/TMNext/TrackMania/TimeAttack/UIModules/EndMatchTrophy_Server.Script.txt" as UIModules_EndMatchTrophy + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// Settings +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +#Setting S_AltitudeUpdateFrequency 500 as "Altitude Update Frequency" + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// 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/TMNext/TrackMania/TimeAttack/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" + + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// Extends +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +***Match_LogVersions*** +*** +Log::RegisterScript(ScriptName, Version); +Log::RegisterScript(StateMgr::ScriptName, StateMgr::Version); +*** + +***Match_LoadLibraries*** +*** +StateMgr::Load(); +*** + +***Match_UnloadLibraries*** +*** +StateMgr::Unload(); +*** + +***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); + } + } +} + +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 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; +*** + +***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 Int2 Net_ClimbTheMap_AltitudeOfWaypoints for Teams[0]; + 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; + + // Initialize race + StartTime = Now + Race::C_SpawnDuration; + EndTime = -1; + + // Spawn players for the race + foreach (Player in Players) { + Race::Start(Player, StartTime); + } + + StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]); + CarRank::Update(CarRank::C_SortCriteria_BestRace); + Race::EnableIntroDuringMatch(True); +} +*** + +***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); + } + 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; + + AltitudePerName[Player.User.Name] = ML::FloorInteger(Player.Position.Y); + } + Net_ClimbTheMap_AltitudePerName = AltitudePerName; + Net_ClimbTheMap_AltitudePerName_Update += 1; +} + +// 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(); + +StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]); +Race::EnableIntroDuringMatch(False); + +TrophyRanking::UpdateUsersRank(); +CarRank::Update(CarRank::C_SortCriteria_BestRace); +Race::StopSkipOutroAll(); +*** + +***Match_BeforePodiumSequence*** +*** +ModeUtils::PlaySound(CUIConfig::EUISound::EndRound, 0); + +UIModules_BigMessage::SetMessage("Changing Map"); +*** + +***Match_AfterPodiumSequence*** +*** +UIModules_BigMessage::SetMessage(""); +*** + +Void SetMl() { + declare Text FrameInstances; + + for (I, 0, 100) { + FrameInstances ^= """"""; + } + + + declare Text MLText = """ + + + +