commit ca2cf30ac16b9e46707d61d84f74ad46e9485765 Author: Beu Date: Fri Aug 6 17:27:21 2021 +0200 initial commit diff --git a/LastManStanding.Script.txt b/LastManStanding.Script.txt new file mode 100644 index 0000000..db7d219 --- /dev/null +++ b/LastManStanding.Script.txt @@ -0,0 +1,854 @@ +/** + * LastManStanding mode + */ +#Extends "Libs/Nadeo/TMNext/TrackMania/Modes/TMNextRoundsBase.Script.txt" + +#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race" +#Const Version "2021-06-14" +#Const ScriptName "Modes/TrackMania/LastManStanding.Script.txt" + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// Libraries +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +#Include "TextLib" as TL +#Include "MathLib" as ML +#Include "Libs/Nadeo/TMNext/TrackMania/Modes/Rounds/StateManager.Script.txt" as StateMgr +#Include "Libs/Nadeo/TMNext/TrackMania/Menu/Constants.Script.txt" as MenuConsts +#Include "Libs/Nadeo/ModeLibs/Common/Utils.Script.txt" as ModeUtils + +// UI from Race +#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/ScoresTable_Server.Script.txt" as UIModules_ScoresTable +#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/PauseMenuOnline_Server.Script.txt" as UIModules_PauseMenu_Online + +#Include "Libs/Nadeo/ModeLibs/Common/Debug.Script.txt" as Debug + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// Settings +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +#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_TimeBeforeMalus 10 as "Time Before Malus" +#Setting S_TimeBeforeNightmare 150 as "Time Before Nightmare" +#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_MalusDuration 5 as "Malus Duration" + +#Setting S_TrustClientSimu False +#Setting S_UseCrudeExtrapolation False + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// Constants +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +#Const C_ModeName "LastManStanding" +#Const Description "$zIn $<$t$6F9LastManStanding$> mode, The goal is to be the last player not to fall off the structure. Collisions are activated and you can push your opponents to win. From a certain time, malus are sent to all the players of the game to accelerate its end." + +#Const C_HudModulePath "" //< Path to the hud module +#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/TMNext/TrackMania/Rounds/Rounds.Script.txt" //< Url of the mania app +#Const C_FakeUsersNb 0 + +#Const C_Malus_Reset 0 +#Const C_Malus_ForceEngine 1 +#Const C_Malus_BackwardOnly 2 +#Const C_Malus_NoBrakes 3 +#Const C_Malus_NoEngine 4 +#Const C_Malus_NoSteer 5 +#Const C_Malus_SlowMotion 6 +#Const C_Malus_BoostDown 7 +#Const C_Malus_BoostUp 8 +#Const C_Malus_Boost2Down 9 +#Const C_Malus_Boost2Up 10 +#Const C_Malus_LockPlayer 11 +#Const C_Malus_AccelCoef25 12 +#Const C_Malus_AdherenceCoef25 13 +#Const C_Malus_ControlCoef25 14 +#Const C_Malus_GravityCoef25 15 + +#Const C_Malus_Nightmare 99 + +#Const C_Malus_Name [0 => "Reset", 1 => "ForceEngine", 2 => "BackwardOnly" , 3 => "NoBrakes", 4 => "NoEngine", +5 => "NoSteer", 6 => "SlowMotion", 7 => "BoostDown", 8 => "BoostUp", +9 => "SuperBoostDown", 10 => "SuperBoostUp", 11 => "LoseControl", +12 => "25% AccelCoef", 13 => "25% Adherence", 14 => "25% Control", +15 => "25% Gravity", 99 => "NightMare"] + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// 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; +Rounds_Settings_UseDefaultSpawnManagement = False; +*** + +***Match_Rules*** +*** +ModeInfo::SetName(C_ModeName); +ModeInfo::SetType(ModeInfo::C_Type_FreeForAll); +ModeInfo::SetRules(Description); +ModeInfo::SetStatusMessage(_("TYPE: Free for all\nOBJECTIVE: Be the last player not to fall off the structure.")); +*** + +***Match_LoadHud*** +*** +if (C_HudModulePath != "") Hud_Load(C_HudModulePath); +*** + +***Match_AfterLoadHud*** +*** +UIManager.UIAll.ScoreTableOnlyManialink = True; +ClientManiaAppUrl = C_ManiaAppUrl; +Race::SortScores(Race::C_Sort_TotalPoints); +UIModules_PauseMenu_Online::SetHelp(Description); +UIManager.UIAll.OverlayHideSpectatorControllers = True; +UIManager.UIAll.OverlayHideCountdown = True; +UIManager.UIAll.OverlayHideSpectatorInfos = True; +UIManager.UIAll.OverlayHideChrono = True; +UIManager.UIAll.OverlayHideCountdown = True; + +Markers::SetDefaultMarker_HudVisibility(CUIConfigMarker::EHudVisibility::Always); +UIManager.UIAll.LabelsVisibility = CUIConfig::EHudVisibility::Everything ; + +SetML(Null); +*** + +***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); +UsePvPCollisions = True; +UsePvECollisions = True; +StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]); +WarmUp::SetAvailability(True); +CarRank::Reset(); + +Debug::AddFakeUsers(C_FakeUsersNb); +//Debug::SetTargetSpeed([10.0, 100.0]); +*** + +***Match_InitMap*** +*** +declare Integer Map_ValidRoundsNb; +declare Boolean RankInitialized = False; + +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 Integer[Text] CustomTimes for This = []; + +declare Boolean ActiveMalus = False; +declare Boolean PendingMalus = False; +declare Integer NextStepMalusTime = 0; +declare Integer MalusIndex; +declare Integer MalusTime; + +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; +*** + +***Match_StartMap*** +*** +// Add bot when necessary +//Users_SetNbFakeUsers(C_FakeUsersNb, 0); +Race::SetRespawnBehaviour(Race::C_RespawnBehaviour_AlwaysGiveUp); + +CarRank::Reset(); + +// Warm up +/*UIModules_ScoresTable::SetFooterInfo(_("Warm up")); +MB_WarmUp(S_WarmUpNb, S_WarmUpDuration * 1000, S_WarmUpTimeout * 1000);*/ +*** + +***Match_StartRound*** +*** +Scores::Clear(); +SetMalusToAll(C_Malus_Reset); + +// WorkAround for longloading +declare StartMapTime = Now; +while (Players.count < 2 && Now < (StartMapTime + 3000)) { + MB_Yield(); +} + +// Initialize race +StartTime = Now + Race::C_SpawnDuration; +Map_TimeBeforeMalus = S_TimeBeforeMalus; +Map_TimeBeforeNightmare = S_TimeBeforeNightmare; +Map_MalusEveryNSecs = S_MalusEveryNSecs; +Map_NextMalusPreparationTime = S_NextMalusPreparationTime; +Map_MalusDuration = S_MalusDuration; +Map_RoundsPerMap = S_RoundsPerMap; +UpdateScoresTableFooter(); +MalusTime = GetTimeBeforeMalus(StartTime, S_TimeBeforeMalus, S_TimeBeforeNightmare); +Net_TimeBeforeMalus = MalusTime; +Net_NextMalus = -1; +Net_RoundsPerMap = Map_RoundsPerMap; +Net_CurrentRoundNb = Map_ValidRoundsNb + 1; + +// Spawn players for the race +---Rounds_CanSpawn--- + +declare Text[] AccountIdsOfPlayers for This = []; +declare CMapLandmark[] Landmarks = Map::GetFinishesAndMultilaps(); +declare CMapLandmark PlayerLM; +declare Integer LandmarkIndex for This = 0; +AccountIdsOfPlayers = []; + +// Suffle Players list to randomise spawn +declare CSmPlayer[] ShuffledPlayers = Players; +declare Integer i=0; +while(i Landmarks.count - 1 ) { + LandmarkIndex = 0; + } + if (Map::IsMultilap(Landmarks[LandmarkIndex])) { + PlayerLM = Landmarks[LandmarkIndex]; + } + LandmarkIndex = LandmarkIndex + 1 ; + } + Race::Start(Player, PlayerLM , StartTime); + AccountIdsOfPlayers.add(Player.User.WebServicesUserId); +} + +Net_NBPlayers = AccountIdsOfPlayers.count; + +UpdateCustomRanking(Null, -1); +StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]); +CarRank::Update(CarRank::C_SortCriteria_BestRace); +Race::EnableIntroDuringMatch(False); +UIManager.UIAll.SendChat("$<$ff3$> Stay the most time on the structure. $<$ff9GL HF!$>"); +*** + +***Match_PlayLoop*** +*** +// Update CarRank & UI Interface +if (!RankInitialized) { + RankInitialized = True; + Net_PlayersNbAlive = PlayersNbAlive; + Net_NBPlayers = AccountIdsOfPlayers.count; + foreach (Player in Players) { + CarRank::SetRank(Player, PlayersNbAlive); + } +} + +// Spawn players who have longloading +if (Now < StartTime + 3000) { + declare Boolean PlayerSpawned = False; + declare CMapLandmark[] Landmarks = Map::GetFinishesAndMultilaps(); + declare CMapLandmark PlayerLM = Null; + if (PlayersNbDead >= 1) { + foreach (Player in Players) { + if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned && !AccountIdsOfPlayers.exists(Player.User.WebServicesUserId)) { + PlayerSpawned = True; + PlayerLM = Null; + while (PlayerLM == Null) { + if (LandmarkIndex > Landmarks.count - 1 ) { + LandmarkIndex = 0; + } + if (Map::IsMultilap(Landmarks[LandmarkIndex])) { + PlayerLM = Landmarks[LandmarkIndex]; + } + LandmarkIndex = LandmarkIndex + 1 ; + } + Race::Start(Player, PlayerLM , StartTime); + AccountIdsOfPlayers.add(Player.User.WebServicesUserId); + + MB_Sleep(100); + while (!SetMalus(Player, C_Malus_Reset)) MB_Yield(); + } + } + } + if (PlayerSpawned) { + PlayerSpawned = False; + Net_NBPlayers = AccountIdsOfPlayers.count; + Net_PlayersNbAlive = PlayersNbAlive; + UpdateCustomRanking(Null, -1); + foreach (Player in Players) { + CarRank::SetRank(Player, PlayersNbAlive); + } + } +} + +// Manage race events +declare RacePendingEvents = Race::GetPendingEvents(); +foreach (Event in RacePendingEvents) { + //log("""Event.Type: {{{Event.Type}}} / Event.IsEndRace: {{{TL::ToText(Event.IsEndRace)}}} / Event.IsEndLap: {{{TL::ToText(Event.IsEndLap)}}} / Event.Player.User.Login: {{{Event.Player.User.Login}}}"""); + Race::ValidEvent(Event);// TODO : Check why event not regitered in NightMare (bot?) + + // Waypoint + if (Event.Type == Events::C_Type_Waypoint) { + if (Event.IsEndLap) { + Race::StopSkipOutro(Event.Player); + foreach (Player in Players) { + CarRank::SetRank(Player, PlayersNbAlive); + Net_PlayersNbAlive = PlayersNbAlive; + } + UpdateCustomRanking(Event.Player, Event.Type); + } + } else if (Event.Type == Events::C_Type_GiveUp) { + foreach (Player in Players) { + CarRank::SetRank(Player, PlayersNbAlive); + Net_PlayersNbAlive = PlayersNbAlive; + } + UpdateCustomRanking(Event.Player, Event.Type); + } +} + +// Manage mode events +foreach (Event in PendingEvents) { + if (Event.HasBeenPassed || Event.HasBeenDiscarded) continue; + Events::Invalid(Event); +} + +if (PlayersNbAlive <= 1 && PlayersNbDead >= 1) { //TODO just respawn in case of 1 player + Net_TimeBeforeMalus = -1; + MB_StopRound(); +} + + +// Check if a player is crushed +if (Now%1000 == 0) { + foreach (Player in Players) { + if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::Spawned && Player.Armor == 0) { + Race::StopSkipOutro(Player); + foreach (Player in Players) { + CarRank::SetRank(Player, PlayersNbAlive); + Net_PlayersNbAlive = PlayersNbAlive; + } + UpdateCustomRanking(Player, Events::C_Type_GiveUp); + } + } +} + +// 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) { + Map_TimeBeforeMalus = S_TimeBeforeMalus; + Map_TimeBeforeNightmare = S_TimeBeforeNightmare; + Map_MalusEveryNSecs = S_MalusEveryNSecs; + Map_NextMalusPreparationTime = S_NextMalusPreparationTime; + Map_MalusDuration = S_MalusDuration; + Map_RoundsPerMap = S_RoundsPerMap; + Net_RoundsPerMap = Map_RoundsPerMap; + UpdateScoresTableFooter(); + MalusTime = GetTimeBeforeMalus(StartTime, S_TimeBeforeMalus, S_TimeBeforeNightmare); + if (NextStepMalusTime == 0) { + Net_TimeBeforeMalus = MalusTime; + } + if (Map_MalusDuration <= 0 || (Map_TimeBeforeMalus < 0 && Map_TimeBeforeNightmare < 0)) { + Net_TimeBeforeMalus = -1; + Net_NextMalus = -1; + } +} + +// Run Malus +if (Players.count > 0 && S_MalusDuration > 0 && MalusTime != -1 && Now > MalusTime) { + if (Now > NextStepMalusTime) { + if (!ActiveMalus && !PendingMalus) { + if (S_TimeBeforeNightmare >= 0 && Now > (StartTime + (S_TimeBeforeNightmare * 1000))) { + MalusIndex = C_Malus_Nightmare; + } else { + declare Boolean AllPlayersInTurtle = True; + foreach (Player in Players) { + if (Player.WheelsContactCount > 1 || Player.Speed > 1) { + AllPlayersInTurtle = False; + break; + } + } + if (AllPlayersInTurtle) { + log("All players are in turtle"); + MalusIndex = ML::Rand(7, 10); // Boost if all players in Turtle + } else { + MalusIndex = ML::Rand(1, 15); + } + } + PendingMalus = True; + ActiveMalus = False; + NextStepMalusTime = Now + (S_NextMalusPreparationTime*1000); + + // Players UI update + Net_NextMalus = MalusIndex; + Net_TimeBeforeMalus = NextStepMalusTime; + } else if (PendingMalus && !ActiveMalus) { + SetMalusToAll(MalusIndex); + PendingMalus = False; + ActiveMalus = True; + NextStepMalusTime = Now + (S_MalusDuration*1000); + + UIManager.UIAll.BigMessageSound = CUIConfig::EUISound::Silence; + UIManager.UIAll.BigMessage = "Current Effect: "^C_Malus_Name[MalusIndex]; + + // Players UI update + Net_NextMalus = 0; + Net_TimeBeforeMalus = NextStepMalusTime; + } else if (!PendingMalus && ActiveMalus) { + if (MalusIndex != 99) { + SetMalusToAll(C_Malus_Reset); + PendingMalus = False; + ActiveMalus = False; + + NextStepMalusTime = Now + (S_MalusEveryNSecs*1000); + + UIManager.UIAll.BigMessageSound = CUIConfig::EUISound::Silence; + UIManager.UIAll.BigMessage = ""; + + // Players UI update + Net_NextMalus = -1; + Net_TimeBeforeMalus = NextStepMalusTime; + } else { + SetMalusToAll(C_Malus_Nightmare); + NextStepMalusTime = Now + (S_MalusDuration*1000); + } + } + } +} + +//Debug::Yield(); + +*** + +***Match_EndRound*** +*** +PendingMalus = False; +ActiveMalus = False; +Net_TimeBeforeMalus = -1; +Net_NextMalus = -1; +RankInitialized = False; +CustomTimes.clear(); + +Race::StopSkipOutroAll(); +StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]); + +if (Round_ForceEndRound || Round_SkipPauseRound) { + // 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(); + } +} else { + Map_ValidRoundsNb += 1; + Scores::SetPlayerWinner(Scores::GetBestPlayer(Race::C_Sort_RoundPoints)); + Race::SortScores(Race::C_Sort_TotalPoints); + Scores::EndRound(); + UIManager.UIAll.BigMessageSound = CUIConfig::EUISound::EndRound; + UIManager.UIAll.BigMessageSoundVariant = 0; + + declare Text Message = _("|Match|Draw"); + declare CSmScore WinnerScore <=> Scores::GetBestPlayer(Race::C_Sort_RoundPoints); + if (WinnerScore != Null) { + Message = TL::Compose(_("$<%1$> wins the match!"), Tools::GetNameWithClubTag(WinnerScore.User)); + } + + UIManager.UIAll.BigMessage = Message; + + MB_Sleep(5000); + UIManager.UIAll.BigMessage = ""; + UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible; + MB_Sleep((S_ChatTime*1000)/2); + UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal; + UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing; + + if (MapIsOver(Map_ValidRoundsNb)) MB_StopMatch(); +} + +*** + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // +// Functions +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // + +/** Get the Time Before the first Malus or NightMare Mode + * + * @param _StartTime The starting time of the map + * @param _TimeBeforeMalus The time before the first Malus + * @param _TimeBeforeNightmare The time before the NightMare mode + * + * @return The Malus Time or -1 in no Malus + */ + Integer GetTimeBeforeMalus(Integer _StartTime, Integer _TimeBeforeMalus, Integer _TimeBeforeNightmare) { + declare Integer MalusTime; + if (_TimeBeforeMalus >= 0 && (_TimeBeforeMalus < _TimeBeforeNightmare || _TimeBeforeNightmare == -1)) { + MalusTime = _StartTime + (_TimeBeforeMalus * 1000); + } else if (_TimeBeforeNightmare >= 0 && (_TimeBeforeNightmare < _TimeBeforeMalus || _TimeBeforeNightmare == _TimeBeforeMalus || _TimeBeforeMalus == -1)) { + MalusTime = _StartTime + (_TimeBeforeNightmare * 1000); + } else { + MalusTime = -1; + } + return MalusTime; +} + + +/** Update the Scores Table with hidden custom points + * + * @param _EliminatedPlayer The Player who is eliminated + * @param _EventType Type of event that led to the update (EndRace or GiveUp) + */ + Void UpdateCustomRanking(CSmPlayer _EliminatedPlayer, Integer _EventType) { + declare Integer[Text] CustomTimes for This; + declare Text[] AccountIdsOfPlayers for This; + foreach (Index => Score in Scores) { + if (Score == Null) continue; + declare CSmPlayer Player = GetPlayer(Score.User.Login); + if (Player == Null) continue; + if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned) { + + Scores::UpdatePlayerBestRaceIfBetter(Player); + if (_EliminatedPlayer != Null && _EliminatedPlayer == Player) { + if (_EventType == Events::C_Type_GiveUp) { + CustomTimes[Score.User.WebServicesUserId] = Now - StartTime; + } + UIManager.UIAll.SendChat("""$<$ff3$> Player $<$ff9{{{Player.User.Name}}}$> is eliminated"""); + } + } else { + Scores::SetPlayerRoundPoints(Score,PlayersNbDead); + } + } + UIModules_ScoresTable::DisplayOnly(AccountIdsOfPlayers); + UIModules_ScoresTable::SetCustomTimes(CustomTimes); +} + +/** Update the scores table footer text + * + */ + Void UpdateScoresTableFooter() { + declare Text Footer = ""; + if (S_MalusDuration <= 0 || (S_TimeBeforeMalus < 0 && S_TimeBeforeNightmare < 0)) { + Footer ^= "Malus disabled"; + } else { + declare Text[] Parts; + declare Message = ""; + if (S_TimeBeforeMalus >= 0) { + if (Parts.count > 0) Message ^= "\n"; + Message ^= """%{{{Parts.count + 1}}}{{{TL::TimeToText(S_TimeBeforeMalus*1000)}}}"""; + Parts.add("Time Before Malus: "); + } + if (S_TimeBeforeNightmare >= 0) { + if (Parts.count > 0) Message ^= "\n"; + Message ^= """%{{{Parts.count + 1}}}{{{TL::TimeToText(S_TimeBeforeNightmare*1000)}}}"""; + Parts.add("Time Before NM: "); + } + + switch (Parts.count) { + case 0: Footer = Message; + case 1: Footer = TL::Compose(Message, Parts[0]); + case 2: Footer = TL::Compose(Message, Parts[0], Parts[1]); + } + } + UIModules_ScoresTable::SetFooterInfo(Footer); +} + +/** Set Malus to a specific Players + * + * @param _Player Player + * @param _Type Malus Index + */ +Boolean SetMalus(CSmPlayer _Player, Integer _Type) { + if (_Player.SpawnStatus == CSmPlayer::ESpawnStatus::Spawned) { + if (_Type > 0) { + _Player.Dossard_Color = <1., 0., 0.>; + } else { + _Player.Dossard_Color = <1., 1., 1.>; + } + switch (_Type) { + case C_Malus_Reset: { + _Player.TrustClientSimu = True; + SetPlayerVehicle_ControlledByMode(_Player, False); + SetPlayerVehicle_ResetControlledModeValues(_Player); + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_Reset(_Player); + } + case C_Malus_ForceEngine: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_ForceEngine(_Player,True); + } + case C_Malus_NoEngine: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_NoEngine(_Player,True); + } + case C_Malus_BackwardOnly: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_Cruise(_Player,True,-999.); + } + case C_Malus_NoBrakes: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_NoBrakes(_Player,True); + } + case C_Malus_NoSteer: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_NoSteer(_Player,True); + } + case C_Malus_SlowMotion: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_SlowMotion(_Player,True); + } + case C_Malus_BoostDown: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_BoostDown(_Player,True); + } + case C_Malus_BoostUp: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_BoostUp(_Player,True); + } + case C_Malus_Boost2Down: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_Boost2Down(_Player,True); + } + case C_Malus_Boost2Up: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_Boost2Up(_Player,True); + } + case C_Malus_LockPlayer: { + _Player.TrustClientSimu = False; + SetPlayerVehicle_ControlledByMode(_Player, True); + } + case C_Malus_AccelCoef25: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_AccelCoef(_Player,0.25); + } + case C_Malus_AdherenceCoef25: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_AdherenceCoef(_Player,0.25); + } + case C_Malus_ControlCoef25: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_ControlCoef(_Player,0.25); + } + case C_Malus_GravityCoef25: { + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_GravityCoef(_Player,0.25); + } + // The goal is to kill all Players + case C_Malus_Nightmare: { + _Player.TrustClientSimu = False; + SetPlayerVehicle_ControlledByMode(_Player, True); + SetPlayerVehicle_TargetSpeedValue(_Player, ML::Rand(-500.,500.)); + SetPlayerVehicle_SteerValue(_Player,ML::Rand(-1.,1.)); + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_Boost2Up(_Player,True); + while (SetPlayer_DelayedIsFull(_Player)) MB_Yield(); + SetPlayer_Delayed_AdherenceCoef(_Player,0.1); + } + } + return True; + } + return False; +} + +/** Set Malus to all Players + * + * @param _Type Malus Index + */ + Void SetMalusToAll(Integer _Type) { + foreach (Player in Players) { + SetMalus(Player, _Type); + } +} + +/** Set the UI + * + * @param _Player Malus Index + */ +Void SetML(CSmPlayer _Player) { + declare TotalWidth = 44.5; + + declare Text MLText = """ + + + +