Compare commits
86 Commits
310e30c311
...
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 | |||
5076df7f37 | |||
7b939d0a62 | |||
122c864044 | |||
02b67fb469 | |||
aa0cc1b094 | |||
d22c8c7147 | |||
7843a689b2 | |||
7ae7e3f277 | |||
6077657acd | |||
a272d789d2 | |||
f963d81001 | |||
1e017b925d | |||
dac6f3916f | |||
0daabfdb8f | |||
fb05e863b4 | |||
10465dbf12 | |||
9ca01735b4 | |||
dec90303b9 | |||
63b200e8c4 | |||
4ca1c7cb8b | |||
eff6c28839 | |||
796c59c8f4 | |||
ba8426c0ae | |||
77daf13e58 | |||
5956325fea | |||
6c4ecfb9c0 | |||
10a403bec7 | |||
d84b22f0ef | |||
1fb905399d | |||
9af0cb4efa | |||
671f1fe8a8 | |||
48421ed7cb | |||
7e985b55b4 | |||
7878af83e1 | |||
5938c7e79f | |||
b9fcc221b4 | |||
81f062d54c | |||
8f0bc2de59 | |||
3a9c12cd0e | |||
fad3a3dc8e | |||
36ee7fc531 | |||
11f2683a3e | |||
90a2f893da | |||
d17b249c4f | |||
ca555c8cbe | |||
fb326d725b |
@ -381,11 +381,11 @@ Void SetHolidayShowdownControlML() {
|
||||
|
||||
declare Real GlobalEndPosY;
|
||||
if (Frame_UI.Visible) {
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_TOP.dds");
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_TOP.dds");
|
||||
GlobalEndPosY = -28.;
|
||||
AnimMgr.Add(Frame_UI, "<frame hidden=\"1\" />", Now, 250, CAnimManager::EAnimManagerEasing::Linear);
|
||||
} else {
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_BOTTOM.dds");
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_BOTTOM.dds");
|
||||
GlobalEndPosY = 0.;
|
||||
Frame_UI.Visible = True;
|
||||
}
|
||||
@ -761,13 +761,13 @@ Void SetHolidayShowdownControlML() {
|
||||
<frame id="frame-global" hidden="1" pos="-60 -63">
|
||||
<frame pos="-2 -25" id="frame-toggle">
|
||||
<quad id="Toggle_Bg_SettingButton" size="4 4" class="quad-base" opacity="0.5" halign="center" valign="center" bgcolor="000" scriptevents="1"/>
|
||||
<quad id="Toggle_Icon_SettingButton" size="4 4" class="quad-base" z-index="3" opacity="0.9" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_BOTTOM.dds" colorize="fff"/>
|
||||
<quad id="Toggle_Icon_SettingButton" size="4 4" class="quad-base" z-index="3" opacity="0.9" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_BOTTOM.dds" colorize="fff"/>
|
||||
</frame>
|
||||
<frame id="frame-UI">
|
||||
<quad id="quad-bg" z-index="-1" size="27 27" bgcolor="000" opacity="0.5" pos="0 0"/>
|
||||
<frame pos="-2 -21">
|
||||
<quad id="BindKey_SettingButton" size="4 4" class="quad-base" opacity="0.5" halign="center" valign="center" bgcolor="000" scriptevents="1"/>
|
||||
<quad size="4 4" class="quad-base" z-index="3" opacity="0.9" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/Icones_128_icon_settings_key_02.dds" colorize="fff"/>
|
||||
<quad size="4 4" class="quad-base" z-index="3" opacity="0.9" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/Icones_128_icon_settings_key_02.dds" colorize="fff"/>
|
||||
</frame>
|
||||
<frame id="Admin_Frame" pos="-2 -13">
|
||||
<frame pos="0 0">
|
||||
@ -873,12 +873,12 @@ Void SetHolidayShowdownLiveRaceML() {
|
||||
|
||||
declare Real GlobalEndPosX;
|
||||
if (Frame_UI.Visible) {
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_RIGHT_OBLIQUE.dds");
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_RIGHT_OBLIQUE.dds");
|
||||
Quad_Toggle.RelativePosition_V3.X = 5.;
|
||||
GlobalEndPosX = -220.;
|
||||
AnimMgr.Add(Frame_UI, "<frame hidden=\"1\" />", Now, 250, CAnimManager::EAnimManagerEasing::Linear);
|
||||
} else {
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds");
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds");
|
||||
Quad_Toggle.RelativePosition_V3.X = 0.;
|
||||
GlobalEndPosX = -160.;
|
||||
Frame_UI.Visible = True;
|
||||
@ -911,15 +911,15 @@ Void SetHolidayShowdownLiveRaceML() {
|
||||
|
||||
if (_PlayerInfo.RoundPoints > 0) {
|
||||
Label_Player_PointsValue.Value = "+" ^ _PlayerInfo.RoundPoints;
|
||||
Quad_Player_PointsIcon.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/256x256/Icones_256_icon_damier_02.dds");
|
||||
Quad_Player_PointsIcon.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/256x256/Icones_256_icon_damier_02.dds");
|
||||
Quad_Player_PointsBg.BgColor = <0.839,0.098,0.098>;
|
||||
} else if (_PlayerInfo.RaceTime > 0) {
|
||||
Label_Player_PointsValue.Value = TL::TimeToText(_PlayerInfo.RaceTime, True, True) ;
|
||||
Quad_Player_PointsIcon.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/256x256/Icones_256_icon_damier_02.dds");
|
||||
Quad_Player_PointsIcon.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/256x256/Icones_256_icon_damier_02.dds");
|
||||
Quad_Player_PointsBg.BgColor = <0.839,0.098,0.098>;
|
||||
} else if (_PlayerInfo.Eliminated) {
|
||||
Label_Player_PointsValue.Value = _("|DidNotFinish|DNF");
|
||||
Quad_Player_PointsIcon.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/256x256/Icones_256_icon_cross_01.dds");
|
||||
Quad_Player_PointsIcon.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/256x256/Icones_256_icon_cross_01.dds");
|
||||
Quad_Player_PointsBg.BgColor = <0.588,0.588,0.588>;
|
||||
} else {
|
||||
Label_Player_PointsValue.Value = TL::ToText(_PlayerInfo.Altitude);
|
||||
@ -1096,7 +1096,7 @@ Void SetHolidayShowdownLiveRaceML() {
|
||||
</framemodel>
|
||||
<frame id="frame-global" pos="-160 50">
|
||||
<frame pos="58 -2.5" id="frame-toggle">
|
||||
<quad id="Toggle_SettingButton" pos="0 0" size="4 4" class="quad-base" z-index="3" opacity="0.9" scriptevents="1" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds" colorize="fff"/>
|
||||
<quad id="Toggle_SettingButton" pos="0 0" size="4 4" class="quad-base" z-index="3" opacity="0.9" scriptevents="1" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds" colorize="fff"/>
|
||||
</frame>
|
||||
<frame id="frame-UI">
|
||||
<quad id="quad-bg" pos="0 0" z-index="-1" size="60 14" bgcolor="000" opacity="0.5" /><!-- 16 + (ML::Max(NB_Players,16) * 6) -->
|
||||
|
@ -1,10 +1,10 @@
|
||||
/**
|
||||
* LastManStanding mode
|
||||
*/
|
||||
#Extends "Libs/Nadeo/TMNext/TrackMania/Modes/TMNextRoundsBase.Script.txt"
|
||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
||||
|
||||
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||
#Const Version "2023-07-14"
|
||||
#Const Version "2024-05-08"
|
||||
#Const ScriptName "Modes/TM2020-Gamemodes/LastManStanding.Script.txt"
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
@ -12,21 +12,21 @@
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#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
|
||||
#Include "Libs/Nadeo/Trackmania/Modes/Rounds/StateManager.Script.txt" as StateMgr
|
||||
#Include "Libs/Nadeo/Trackmania/MainMenu/Constants.Script.txt" as MenuConsts
|
||||
#Include "Libs/Nadeo/CMGame/Modes/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 "ManiaApps/Nadeo/TMxSM/Race/UIModules/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
#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/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// 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_MapsPerMatch 4 as "Only used if S_KeepScoresBetweenRounds = True"
|
||||
|
||||
#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_NextMalusPreparationTime 10 as "Time given to players to prepare them before a Malus"
|
||||
#Setting S_MalusDuration 5 as "Malus Duration"
|
||||
#Setting S_KeepScoresBetweenRounds False
|
||||
|
||||
#Setting S_TrustClientSimu False
|
||||
#Setting S_UseCrudeExtrapolation False
|
||||
@ -47,9 +48,11 @@
|
||||
#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_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/Rounds.Script.txt" //< Url of the mania app
|
||||
#Const C_FakeUsersNb 0
|
||||
|
||||
#Const C_Callback_CustomChat_ChatMessage "CustomChat.GamemodeChatMessage"
|
||||
|
||||
#Const C_Malus_Reset 0
|
||||
#Const C_Malus_ForceEngine 1
|
||||
#Const C_Malus_BackwardOnly 2
|
||||
@ -92,18 +95,31 @@ Log::RegisterScript(StateMgr::ScriptName, StateMgr::Version);
|
||||
***Match_LoadLibraries***
|
||||
***
|
||||
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***
|
||||
***
|
||||
StateMgr::Unload();
|
||||
XmlRpc::UnregisterCallback(C_Callback_CustomChat_ChatMessage);
|
||||
***
|
||||
|
||||
***Match_Settings***
|
||||
***
|
||||
MB_Settings_UseDefaultTimer = False;
|
||||
MB_Settings_UseDefaultHud = (C_HudModulePath == "");
|
||||
MB_Settings_UseDefaultPodiumSequence = False;
|
||||
Rounds_Settings_UseDefaultSpawnManagement = False;
|
||||
MB_Settings_UseDefaultIntroSequence = False;
|
||||
***
|
||||
@ -127,9 +143,7 @@ UIManager.UIAll.ScoreTableOnlyManialink = True;
|
||||
ClientManiaAppUrl = C_ManiaAppUrl;
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
UIModules_PauseMenu_Online::SetHelp(Description);
|
||||
UIManager.UIAll.OverlayHideSpectatorControllers = True;
|
||||
UIManager.UIAll.OverlayHideSpectatorInfos = True;
|
||||
UIManager.UIAll.OverlayHideChrono = True;
|
||||
UIManager.UIAll.OverlayHideCountdown = True;
|
||||
|
||||
Markers::SetDefaultMarker_HudVisibility(CUIConfigMarker::EHudVisibility::Always);
|
||||
@ -156,6 +170,7 @@ foreach (Event in UIManager.PendingEvents) {
|
||||
if (Event.CustomEventType == "LMS_NotifyAFK") {
|
||||
if (Event.CustomEventData.count > 0) {
|
||||
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"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -169,6 +184,7 @@ StateMgr::Yield();
|
||||
Clans::SetClansNb(0);
|
||||
UsePvPCollisions = True;
|
||||
UsePvECollisions = True;
|
||||
Scores::SaveInScore(Scores::C_Points_Match);
|
||||
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
||||
WarmUp::SetAvailability(True);
|
||||
CarRank::Reset();
|
||||
@ -177,35 +193,8 @@ ResetNetworkVariables();
|
||||
|
||||
***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();
|
||||
UIModules_ScoresTable::SetCustomTimes([]);
|
||||
ResetCustomPoints();
|
||||
|
||||
// Map Intro
|
||||
declare Boolean MapIsCompatible;
|
||||
@ -220,6 +209,7 @@ if (!MapIsCompatible) {
|
||||
UIManager.UIAll.QueueMessage(3000, 1, CUIConfig::EMessageDisplay::Big, _("This map is not valid"));
|
||||
MB_Sleep(3000);
|
||||
MB_StopMap();
|
||||
MB_SetValidMap(False);
|
||||
} else if (S_IntroTime > 0) {
|
||||
declare netwrite Boolean Net_LMS_IsIntro for Teams[0] = False;
|
||||
Net_LMS_IsIntro = True;
|
||||
@ -252,13 +242,39 @@ CarRank::Reset();
|
||||
|
||||
***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***
|
||||
***
|
||||
Scores::Clear();
|
||||
UIModules_ScoresTable::SetCustomTimes([]);
|
||||
UIModules_ScoresTable::SetScoreMode(UIModules_ScoresTable::C_Mode_Points);
|
||||
Race::SortScores(Race::C_Sort_RoundPoints);
|
||||
|
||||
declare netwrite Integer Net_LMS_AFKIdleTime for Teams[0] = 120000;
|
||||
Net_LMS_AFKIdleTime = S_AFKIdleTime;
|
||||
@ -271,29 +287,29 @@ while (Players.count < 2 && Now < (StartMapTime + 3000)) {
|
||||
|
||||
// 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;
|
||||
Round_TimeBeforeMalus = S_TimeBeforeMalus;
|
||||
Round_TimeBeforeNightmare = S_TimeBeforeNightmare;
|
||||
Round_MalusEveryNSecs = S_MalusEveryNSecs;
|
||||
Round_NextMalusPreparationTime = S_NextMalusPreparationTime;
|
||||
Round_MalusDuration = S_MalusDuration;
|
||||
Round_RoundsPerMap = S_RoundsPerMap;
|
||||
UpdateScoresTableFooter();
|
||||
MalusTime = GetTimeBeforeMalus(StartTime, S_TimeBeforeMalus, S_TimeBeforeNightmare);
|
||||
Round_MalusTime = GetTimeBeforeMalus(StartTime, S_TimeBeforeMalus, S_TimeBeforeNightmare);
|
||||
Net_DisplayUI = True;
|
||||
Net_TimeBeforeMalus = MalusTime;
|
||||
Net_TimeBeforeMalus = Round_MalusTime;
|
||||
Net_NextMalus = -1;
|
||||
Net_RoundsPerMap = Map_RoundsPerMap;
|
||||
Net_CurrentRoundNb = Map_ValidRoundsNb + 1;
|
||||
MalusQueue = [];
|
||||
Net_RoundsPerMap = Round_RoundsPerMap;
|
||||
Net_CurrentRoundNb = MB_GetValidRoundCount();
|
||||
ResetCustomPoints();
|
||||
|
||||
// Spawn players for the race
|
||||
---Rounds_CanSpawn---
|
||||
|
||||
declare Text[] AccountIdsOfPlayers for This = [];
|
||||
declare Text[] LMS_AccountIdsOfPlayers for This = [];
|
||||
|
||||
declare CMapLandmark PlayerLM;
|
||||
declare Integer LandmarkIndex for This = 0;
|
||||
AccountIdsOfPlayers = [];
|
||||
declare Integer LMS_LandmarkIndex for This = 0;
|
||||
LMS_AccountIdsOfPlayers = [];
|
||||
|
||||
// Suffle Players list to randomise spawn
|
||||
declare CSmPlayer[Integer] ShuffledPlayers;
|
||||
@ -315,28 +331,29 @@ foreach (Player in ShuffledPlayers) {
|
||||
if (Player == Null) continue;
|
||||
PlayerLM = Null;
|
||||
while (PlayerLM == Null) {
|
||||
if (LandmarkIndex > Landmarks.count - 1 ) {
|
||||
LandmarkIndex = 0;
|
||||
if (LMS_LandmarkIndex > Landmarks.count - 1 ) {
|
||||
LMS_LandmarkIndex = 0;
|
||||
}
|
||||
if (Map::IsMultilap(Landmarks[LandmarkIndex])) {
|
||||
PlayerLM = Landmarks[LandmarkIndex];
|
||||
if (Map::IsMultilap(Landmarks[LMS_LandmarkIndex])) {
|
||||
PlayerLM = Landmarks[LMS_LandmarkIndex];
|
||||
}
|
||||
LandmarkIndex = LandmarkIndex + 1 ;
|
||||
LMS_LandmarkIndex = LMS_LandmarkIndex + 1 ;
|
||||
}
|
||||
Race::Start(Player, PlayerLM , StartTime);
|
||||
CarRank::SetRank(Player, Net_NBPlayers);
|
||||
AccountIdsOfPlayers.add(Player.User.WebServicesUserId);
|
||||
MalusQueue[Player.User.Login] = GetNewMalus(C_Malus_Reset, 1500);
|
||||
LMS_AccountIdsOfPlayers.add(Player.User.WebServicesUserId);
|
||||
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]);
|
||||
Race::EnableIntroDuringMatch(False);
|
||||
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***
|
||||
***
|
||||
// Manage race events
|
||||
@ -348,20 +365,23 @@ foreach (Event in RacePendingEvents) {
|
||||
|
||||
switch (Event.Type) {
|
||||
case Events::C_Type_Waypoint: {
|
||||
ThrottleUpdate = True;
|
||||
Round_ThrottleUpdate = True;
|
||||
|
||||
Scores::UpdatePlayerBestRaceIfBetter(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: {
|
||||
ThrottleUpdate = True;
|
||||
UpdateCustomRanking(Event.Player.User, True);
|
||||
Round_ThrottleUpdate = 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: {
|
||||
ThrottleUpdate = True;
|
||||
Round_ThrottleUpdate = True;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -373,28 +393,30 @@ foreach (Event in PendingEvents) {
|
||||
|
||||
if (Event.Type == CSmModeEvent::EType::OnPlayerRemoved) {
|
||||
if (Event.User == Null ) continue;
|
||||
if (!AccountIdsOfPlayers.exists(Event.User.WebServicesUserId)) continue;
|
||||
if (IsEliminated(Event.User, Null)) continue;
|
||||
ThrottleUpdate = True;
|
||||
UpdateCustomRanking(Event.User, True);
|
||||
if (!LMS_AccountIdsOfPlayers.exists(Event.User.WebServicesUserId)) continue;
|
||||
if (Round_AccountIdsOfEliminated.exists(Event.User.WebServicesUserId)) continue;
|
||||
Round_ThrottleUpdate = 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)
|
||||
if (!ThrottleUpdate && Net_PlayersNbAlive != PlayersNbAlive) {
|
||||
if (!Round_ThrottleUpdate && Net_PlayersNbAlive != PlayersNbAlive) {
|
||||
log("Trying to detect why the player count changed");
|
||||
foreach (Player in AllPlayers) {
|
||||
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 (IsEliminated(Player.User, Player.Score)) continue;
|
||||
UpdateCustomRanking(Player.User, True);
|
||||
ThrottleUpdate = True;
|
||||
if (Round_AccountIdsOfEliminated.exists(Player.User.WebServicesUserId)) continue;
|
||||
UpdateCustomRanking(Player.User, Player, True);
|
||||
Round_AccountIdsOfEliminated.add(Player.User.WebServicesUserId);
|
||||
Round_ThrottleUpdate = True;
|
||||
}
|
||||
}
|
||||
|
||||
if (ThrottleUpdate) {
|
||||
ThrottleUpdate = False;
|
||||
if (Round_ThrottleUpdate) {
|
||||
Round_ThrottleUpdate = False;
|
||||
Net_PlayersNbAlive = PlayersNbAlive;
|
||||
declare Integer Points = Net_NBPlayers - Net_PlayersNbAlive;
|
||||
foreach (Player in Players) {
|
||||
@ -405,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;
|
||||
MB_StopRound();
|
||||
}
|
||||
|
||||
|
||||
// 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;
|
||||
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) {
|
||||
Round_TimeBeforeMalus = S_TimeBeforeMalus;
|
||||
Round_TimeBeforeNightmare = S_TimeBeforeNightmare;
|
||||
Round_MalusEveryNSecs = S_MalusEveryNSecs;
|
||||
Round_NextMalusPreparationTime = S_NextMalusPreparationTime;
|
||||
Round_MalusDuration = S_MalusDuration;
|
||||
Round_RoundsPerMap = S_RoundsPerMap;
|
||||
Net_RoundsPerMap = Round_RoundsPerMap;
|
||||
UpdateScoresTableFooter();
|
||||
MalusTime = GetTimeBeforeMalus(StartTime, S_TimeBeforeMalus, S_TimeBeforeNightmare);
|
||||
if (NextStepMalusTime == 0) {
|
||||
Net_TimeBeforeMalus = MalusTime;
|
||||
Round_MalusTime = GetTimeBeforeMalus(StartTime, S_TimeBeforeMalus, S_TimeBeforeNightmare);
|
||||
if (Round_NextStepMalusTime == 0) {
|
||||
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_NextMalus = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Run Malus
|
||||
if (Players.count > 0 && S_MalusDuration > 0 && MalusTime != -1 && Now > MalusTime) {
|
||||
if (Now > NextStepMalusTime) {
|
||||
if (!ActiveMalus && !PendingMalus) {
|
||||
if (Players.count > 0 && S_MalusDuration > 0 && Round_MalusTime != -1 && Now > Round_MalusTime) {
|
||||
if (Now > Round_NextStepMalusTime) {
|
||||
if (!Round_ActiveMalus && !Round_PendingMalus) {
|
||||
if (S_TimeBeforeNightmare >= 0 && Now > (StartTime + (S_TimeBeforeNightmare * 1000))) {
|
||||
MalusIndex = C_Malus_Nightmare;
|
||||
Round_MalusIndex = C_Malus_Nightmare;
|
||||
} else if (AllPlayersAreInTurtle()) {
|
||||
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 {
|
||||
MalusIndex = ML::Rand(1, 15);
|
||||
Round_MalusIndex = ML::Rand(1, 15);
|
||||
}
|
||||
PendingMalus = True;
|
||||
ActiveMalus = False;
|
||||
NextStepMalusTime = Now + (S_NextMalusPreparationTime*1000);
|
||||
Round_PendingMalus = True;
|
||||
Round_ActiveMalus = False;
|
||||
Round_NextStepMalusTime = Now + (S_NextMalusPreparationTime*1000);
|
||||
|
||||
// Players UI update
|
||||
Net_NextMalus = MalusIndex;
|
||||
Net_TimeBeforeMalus = NextStepMalusTime;
|
||||
} else if (PendingMalus && !ActiveMalus) {
|
||||
Net_NextMalus = Round_MalusIndex;
|
||||
Net_TimeBeforeMalus = Round_NextStepMalusTime;
|
||||
} else if (Round_PendingMalus && !Round_ActiveMalus) {
|
||||
foreach (Player in Players) {
|
||||
MalusQueue[Player.User.Login] = GetNewMalus(MalusIndex);
|
||||
Round_MalusQueue[Player.User.Login] = GetNewMalus(Round_MalusIndex);
|
||||
}
|
||||
PendingMalus = False;
|
||||
ActiveMalus = True;
|
||||
NextStepMalusTime = Now + (S_MalusDuration*1000);
|
||||
Round_PendingMalus = False;
|
||||
Round_ActiveMalus = True;
|
||||
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
|
||||
Net_NextMalus = 0;
|
||||
Net_TimeBeforeMalus = NextStepMalusTime;
|
||||
} else if (!PendingMalus && ActiveMalus) {
|
||||
if (MalusIndex == 99) {
|
||||
Net_TimeBeforeMalus = Round_NextStepMalusTime;
|
||||
} else if (!Round_PendingMalus && Round_ActiveMalus) {
|
||||
if (Round_MalusIndex == 99) {
|
||||
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 {
|
||||
foreach (Player in Players) {
|
||||
MalusQueue[Player.User.Login] = GetNewMalus(C_Malus_Reset);
|
||||
Round_MalusQueue[Player.User.Login] = GetNewMalus(C_Malus_Reset);
|
||||
}
|
||||
PendingMalus = False;
|
||||
ActiveMalus = False;
|
||||
Round_PendingMalus = False;
|
||||
Round_ActiveMalus = False;
|
||||
|
||||
NextStepMalusTime = Now + (S_MalusEveryNSecs*1000);
|
||||
Round_NextStepMalusTime = Now + (S_MalusEveryNSecs*1000);
|
||||
|
||||
UIModules_BigMessage::SetMessage("");
|
||||
|
||||
// Players UI update
|
||||
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);
|
||||
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)) {
|
||||
Log::Log("[ApplyPhysics] Trying to set Event " ^ C_Malus_Name[Malus.MalusIndex] ^ " for " ^ Player.User.Name);
|
||||
if (SetMalus(Player, Malus.MalusIndex)) {
|
||||
MalusQueue.removekey(Login);
|
||||
Round_MalusQueue.removekey(Login);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -503,8 +525,7 @@ foreach (Login => Malus in MalusQueue) {
|
||||
|
||||
***Match_EndRound***
|
||||
***
|
||||
PendingMalus = False;
|
||||
ActiveMalus = False;
|
||||
UIModules_BigMessage::SetMessage("");
|
||||
Net_DisplayUI = False;
|
||||
Net_TimeBeforeMalus = -1;
|
||||
Net_NextMalus = -1;
|
||||
@ -521,44 +542,75 @@ if (Round_ForceEndRound || Round_SkipPauseRound) {
|
||||
if (!Round_SkipPauseRound) {
|
||||
ForcedEndRoundSequence();
|
||||
}
|
||||
MB_SetValidRound(False);
|
||||
} else {
|
||||
Map_ValidRoundsNb += 1;
|
||||
if (S_KeepScoresBetweenRounds) {
|
||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
||||
MB_Sleep((S_ChatTime*1000)/2);
|
||||
|
||||
declare CSmScore WinnerScore <=> Scores::GetBestPlayer(Scores::C_Sort_RoundPoints);
|
||||
if (WinnerScore == Null) {
|
||||
foreach (Score in Scores) {
|
||||
if (Score.BestRaceTimes.count <= 0 && Score.User != Null && AccountIdsOfPlayers.exists(Score.User.WebServicesUserId)) {
|
||||
declare CSmPlayer Player = GetPlayer(Score.User.Login);
|
||||
if (Player != Null && !Player.RequestsSpectate) {
|
||||
WinnerScore <=> Score;
|
||||
break;
|
||||
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);
|
||||
if (WinnerScore == Null) {
|
||||
foreach (Score in Scores) {
|
||||
if (Score.BestRaceTimes.count <= 0 && Score.User != Null && LMS_AccountIdsOfPlayers.exists(Score.User.WebServicesUserId)) {
|
||||
declare CSmPlayer Player = GetPlayer(Score.User.Login);
|
||||
if (Player != Null && !Player.RequestsSpectate) {
|
||||
WinnerScore <=> Score;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Scores::SetPlayerWinner(WinnerScore);
|
||||
|
||||
ModeUtils::PlaySound(CUIConfig::EUISound::EndRound, 0);
|
||||
|
||||
if (WinnerScore == Null) {
|
||||
UIModules_BigMessage::SetMessage(_("|Match|Draw"));
|
||||
} else {
|
||||
UIModules_BigMessage::SetMessage(_("$<%1$> wins the match!"), WinnerScore.User.WebServicesUserId);
|
||||
}
|
||||
|
||||
Scores::EndRound();
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
MB_Sleep((S_ChatTime*1000)/2);
|
||||
|
||||
UIModules_BigMessage::SetMessage("");
|
||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
||||
MB_Sleep((S_ChatTime*1000)/2);
|
||||
|
||||
Scores::Clear();
|
||||
}
|
||||
Scores::SetPlayerWinner(WinnerScore);
|
||||
|
||||
ModeUtils::PlaySound(CUIConfig::EUISound::EndRound, 0);
|
||||
|
||||
if (WinnerScore == Null) {
|
||||
UIModules_BigMessage::SetMessage(_("|Match|Draw"));
|
||||
} else {
|
||||
UIModules_BigMessage::SetMessage(_("$<%1$> wins the match!"), WinnerScore.User.WebServicesUserId);
|
||||
}
|
||||
|
||||
Scores::EndRound();
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
|
||||
MB_Sleep(5000);
|
||||
UIModules_BigMessage::SetMessage("");
|
||||
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();
|
||||
}
|
||||
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();
|
||||
}
|
||||
***
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
@ -583,30 +635,13 @@ Void ResetNetworkVariables() {
|
||||
Net_CurrentRoundNb = 0;
|
||||
}
|
||||
|
||||
|
||||
/** Check if a Player is already considered eliminated
|
||||
*
|
||||
* @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) {
|
||||
if (Score.User == Null) continue;
|
||||
if (Score.User == _User) {
|
||||
if (Score.PrevRaceTimes.count > 0) return True;
|
||||
else return False;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (_Score.PrevRaceTimes.count > 0) {
|
||||
return True;
|
||||
Void ResetCustomPoints() {
|
||||
declare Text[][Text] CustomPoints = [];
|
||||
foreach (Score in Scores) {
|
||||
if (Score.User == Null) continue;
|
||||
CustomPoints[Score.User.WebServicesUserId] = ["--:--.---"];
|
||||
}
|
||||
|
||||
return False;
|
||||
UIModules_ScoresTable::SetCustomPoints(CustomPoints);
|
||||
}
|
||||
|
||||
/** Detect if all players are in Turtle
|
||||
@ -630,7 +665,7 @@ Boolean AllPlayersAreInTurtle() {
|
||||
*
|
||||
* @return The Malus Time or -1 in no Malus
|
||||
*/
|
||||
Integer GetTimeBeforeMalus(Integer _StartTime, Integer _TimeBeforeMalus, Integer _TimeBeforeNightmare) {
|
||||
Integer GetTimeBeforeMalus(Integer _StartTime, Integer _TimeBeforeMalus, Integer _TimeBeforeNightmare) {
|
||||
declare Integer MalusTime;
|
||||
if (_TimeBeforeMalus >= 0 && (_TimeBeforeMalus < _TimeBeforeNightmare || _TimeBeforeNightmare == -1)) {
|
||||
MalusTime = _StartTime + (_TimeBeforeMalus * 1000);
|
||||
@ -646,23 +681,46 @@ Boolean AllPlayersAreInTurtle() {
|
||||
/** Update the Scores Table with hidden custom points
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
Void UpdateCustomRanking(CUser _User, Boolean _OverrideTime) {
|
||||
Void UpdateCustomRanking(CUser _User, CSmPlayer _Player, Boolean _OverrideTime) {
|
||||
if (_User == Null) return;
|
||||
|
||||
declare Text[][Text] CustomPoints = UIModules_ScoresTable::GetCustomPoints();
|
||||
|
||||
if (_OverrideTime) {
|
||||
declare Integer[Text] CustomTimes = UIModules_ScoresTable::GetCustomTimes();
|
||||
CustomTimes[_User.WebServicesUserId] = Now - StartTime;
|
||||
UIModules_ScoresTable::SetCustomTimes(CustomTimes);
|
||||
if (_OverrideTime || _Player == Null || _Player.RaceWaypointTimes.count == 0) {
|
||||
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""");
|
||||
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
|
||||
*
|
||||
*/
|
||||
Void UpdateScoresTableFooter() {
|
||||
Void UpdateScoresTableFooter() {
|
||||
declare Text Footer = "";
|
||||
if (S_MalusDuration <= 0 || (S_TimeBeforeMalus < 0 && S_TimeBeforeNightmare < 0)) {
|
||||
Footer ^= "Malus disabled";
|
||||
@ -679,11 +737,18 @@ Boolean AllPlayersAreInTurtle() {
|
||||
Message ^= """%{{{Parts.count + 1}}}{{{TL::TimeToText(S_TimeBeforeNightmare*1000)}}}""";
|
||||
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) {
|
||||
case 0: Footer = Message;
|
||||
case 1: Footer = TL::Compose(Message, Parts[0]);
|
||||
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);
|
||||
@ -1034,12 +1099,20 @@ Void SetML() {
|
||||
|
||||
|
||||
/** 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
|
||||
*/
|
||||
Boolean MapIsOver(Integer _ValidRoundsNb) {
|
||||
if (S_RoundsPerMap > 0 && _ValidRoundsNb >= S_RoundsPerMap) return True; //< There is a rounds limit and it is reached
|
||||
Boolean MapIsOver() {
|
||||
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;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
// #RequireContext CSmMode
|
||||
|
||||
#Include "MathLib" as ML
|
||||
#Include "Libs/Nadeo/CommonLibs/Common/Log.Script.txt" as Log
|
||||
#Include "Libs/Nadeo/TMxSM/Race/Events.Script.txt" as Events
|
||||
#Include "Libs/Nadeo/TMxSM/Race/Race.Script.txt" as Race
|
||||
#Include "Libs/Nadeo/CMGame/Utils/Log.Script.txt" as Log
|
||||
#Include "Libs/Nadeo/TMGame/Modes/Events.Script.txt" as Events
|
||||
#Include "Libs/Nadeo/TMGame/Modes/Race.Script.txt" as Race
|
||||
|
||||
#Const C_Malus_Reset 0
|
||||
#Const C_Malus_ForceEngine 1
|
||||
|
@ -532,12 +532,12 @@ Void SetML() {
|
||||
|
||||
declare Real GlobalEndPosX;
|
||||
if (Frame_UI.Visible) {
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_RIGHT_OBLIQUE.dds");
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_RIGHT_OBLIQUE.dds");
|
||||
Quad_Toggle.RelativePosition_V3.X = 5.;
|
||||
GlobalEndPosX = -220.;
|
||||
AnimMgr.Add(Frame_UI, "<frame hidden=\"1\" />", Now, 250, CAnimManager::EAnimManagerEasing::Linear);
|
||||
} else {
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds");
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds");
|
||||
Quad_Toggle.RelativePosition_V3.X = 0.;
|
||||
GlobalEndPosX = -160.;
|
||||
Frame_UI.Visible = True;
|
||||
@ -1076,7 +1076,7 @@ Void SetML() {
|
||||
</stylesheet>
|
||||
<frame id="frame-global" hidden="1" pos="-160 40">
|
||||
<frame pos="58 -2.5" id="frame-toggle" z-index=1>
|
||||
<quad id="Toggle_SettingButton" pos="0 0" size="4 4" class="quad-base" z-index="3" opacity="0.9" scriptevents="1" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds" colorize="fff"/>
|
||||
<quad id="Toggle_SettingButton" pos="0 0" size="4 4" class="quad-base" z-index="3" opacity="0.9" scriptevents="1" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds" colorize="fff"/>
|
||||
</frame>
|
||||
<frame id="frame-UI">
|
||||
<quad id="quad-bg" pos="0 0" z-index="-1" size="60 74" bgcolor="000" opacity="0.5" /> <!-- 85 for regular, 74 without spec control-->
|
||||
@ -1086,7 +1086,7 @@ Void SetML() {
|
||||
<label class="text-suffix" pos="30 -9" textsize="1.5" z-index="0" size="50 10" id="SubTitle" text=""/>
|
||||
<frame pos="58 -6">
|
||||
<quad id="BindKey_SettingButton" size="4 4" z-index="1" class="quad-base" opacity="0" halign="center" valign="center" bgcolor="fff" scriptevents="1"/>
|
||||
<quad size="4 4" class="quad-base" z-index="3" opacity="0.9" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/Icones_128_icon_settings_key_02.dds" colorize="fff"/>
|
||||
<quad size="4 4" class="quad-base" z-index="3" opacity="0.9" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/Icones_128_icon_settings_key_02.dds" colorize="fff"/>
|
||||
</frame>
|
||||
<frame pos="0.5 -6">
|
||||
<frame pos="30 0">
|
||||
|
276
TM_Alfadream.Script.txt
Normal file
276
TM_Alfadream.Script.txt
Normal file
@ -0,0 +1,276 @@
|
||||
#Extends "Modes/TrackMania/TM_Cup_Online.Script.txt"
|
||||
|
||||
#Setting S_PointsLimit 50
|
||||
#Setting S_RoundsPerMap 4
|
||||
#Setting S_PointsRepartition "10,6,4,3,0"
|
||||
#Setting S_PlayersPerMatch 12
|
||||
|
||||
#Const C_ML_TeamPicker "Custom_TeamPicker"
|
||||
|
||||
***Match_StartServer***
|
||||
***
|
||||
Clans::SetClansNb(6);
|
||||
Clans::SetUseForcedClans(True);
|
||||
WarmUp::SetSpawnInRequestedClan(False); // Must be disabled because Clans are forced
|
||||
***
|
||||
|
||||
***Match_StartMatch***
|
||||
***
|
||||
declare netwrite Integer[Text] Net_Custom_PlayersClan for Teams[0] ;
|
||||
Net_Custom_PlayersClan = [];
|
||||
|
||||
Teams[2].Name = "Cyan";
|
||||
Teams[3].Name = "Pink";
|
||||
Teams[4].Name = "Yellow";
|
||||
Teams[5].Name = "Green";
|
||||
***
|
||||
|
||||
|
||||
***Match_InitMap***
|
||||
***
|
||||
if (GetPlayersNb() < S_PlayersPerMatch) {
|
||||
SendPickManialink();
|
||||
declare netwrite Integer[Text] Net_Custom_PlayersClan for Teams[0] = [];
|
||||
|
||||
foreach (Player in AllPlayers) {
|
||||
if (Player.IsFakePlayer) {
|
||||
Net_Custom_PlayersClan[Player.User.WebServicesUserId] = ML::Rand(1,6);
|
||||
Player.Score.LadderClan = Net_Custom_PlayersClan[Player.User.WebServicesUserId];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
while (GetPlayersNb() < S_PlayersPerMatch && MB_MapIsRunning()) {
|
||||
MB_Yield();
|
||||
|
||||
foreach (Event in UIManager.PendingEvents) {
|
||||
if (Event.Type == CUIConfigEvent::EType::OnLayerCustomEvent && Event.CustomEventType == "Custom_Join_Clan") {
|
||||
declare CSmPlayer Player = GetPlayer(Event.UI);
|
||||
if (Player != Null) {
|
||||
Net_Custom_PlayersClan[Player.User.WebServicesUserId] = TL::ToInteger(Event.CustomEventData[0]);
|
||||
Player.Score.LadderClan = Net_Custom_PlayersClan[Player.User.WebServicesUserId];
|
||||
SetPlayerClan(Player, Net_Custom_PlayersClan[Player.User.WebServicesUserId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Layers::Detach(C_ML_TeamPicker);
|
||||
}
|
||||
declare Int2[] Custom_RoundsVersus for This = [];
|
||||
Custom_RoundsVersus = GenerateRoundVersus();
|
||||
***
|
||||
|
||||
***Match_InitRound***
|
||||
***
|
||||
declare Integer Custom_NbOfValidRounds for This = 0;
|
||||
Custom_NbOfValidRounds = G_NbOfValidRounds;
|
||||
|
||||
declare Integer[] ClansThatCanSpawn = GetClansThatCanSpawn();
|
||||
|
||||
if (ClansThatCanSpawn.count == 6) {
|
||||
UIModules_BigMessage::SetMessage("All teams play");
|
||||
} else {
|
||||
declare CTeam Team1 <=> Teams[ClansThatCanSpawn[0] - 1];
|
||||
declare CTeam Team2 <=> Teams[ClansThatCanSpawn[1] - 1];
|
||||
|
||||
UIModules_BigMessage::SetMessage(Team1.Name ^ " vs " ^ Team2.Name);
|
||||
}
|
||||
MB_Sleep(3000);
|
||||
UIModules_BigMessage::SetMessage("");
|
||||
***
|
||||
|
||||
***Match_Yield***
|
||||
***
|
||||
foreach (Event in PendingEvents) {
|
||||
if (Event.Type == CSmModeEvent::EType::OnPlayerAdded) {
|
||||
declare netwrite Integer[Text] Net_Custom_PlayersClan for Teams[0] = [];
|
||||
if (Net_Custom_PlayersClan.existskey(Event.Player.User.WebServicesUserId)) {
|
||||
Event.Player.Score.LadderClan = Net_Custom_PlayersClan[Event.Player.User.WebServicesUserId];
|
||||
SetPlayerClan(Event.Player, Net_Custom_PlayersClan[Event.Player.User.WebServicesUserId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
***
|
||||
|
||||
***Rounds_SpawnPlayer***
|
||||
***
|
||||
SetPlayerClan(Player, Player.Score.LadderClan);
|
||||
Race::Start(Player, StartTime);
|
||||
***
|
||||
|
||||
|
||||
***Rounds_CheckCanSpawn***
|
||||
***
|
||||
declare netwrite Integer[Text] Net_Custom_PlayersClan for Teams[0] = [];
|
||||
if (!Net_Custom_PlayersClan.existskey(_Player.Score.User.WebServicesUserId)) return False;
|
||||
|
||||
|
||||
// Copy of GetClansThatCanSpawn() checks
|
||||
declare Boolean OnePlayerIsFinalist = False;
|
||||
foreach (Score in Scores) {
|
||||
if (Scores::GetPlayerMatchPoints(Score) >= S_PointsLimit) {
|
||||
OnePlayerIsFinalist = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!OnePlayerIsFinalist) {
|
||||
declare Int2[] Custom_RoundsVersus for This = [];
|
||||
declare Integer Custom_NbOfValidRounds for This = 0;
|
||||
|
||||
if (Custom_RoundsVersus.existskey(Custom_NbOfValidRounds)) {
|
||||
if (Custom_RoundsVersus[Custom_NbOfValidRounds].X != Net_Custom_PlayersClan[_Player.Score.User.WebServicesUserId]
|
||||
&& Custom_RoundsVersus[Custom_NbOfValidRounds].Y != Net_Custom_PlayersClan[_Player.Score.User.WebServicesUserId]) {
|
||||
return False;
|
||||
}
|
||||
}
|
||||
}
|
||||
***
|
||||
|
||||
Integer GetPlayersNb() {
|
||||
declare netwrite Integer[Text] Net_Custom_PlayersClan for Teams[0] = [];
|
||||
return Net_Custom_PlayersClan.count;
|
||||
}
|
||||
|
||||
Int2[] GenerateRoundVersus() {
|
||||
declare Integer[Integer] SortingTeam;
|
||||
for (I, 1, 6) {
|
||||
SortingTeam[I] = ML::Rand(0, 10000);
|
||||
}
|
||||
|
||||
SortingTeam = SortingTeam.sort();
|
||||
|
||||
declare Integer[] SortedTeam;
|
||||
|
||||
foreach (ClanId => Rand in SortingTeam.sort()) {
|
||||
SortedTeam.add(ClanId);
|
||||
}
|
||||
|
||||
declare Int2[] RoundOrder;
|
||||
for (I, 0, 2) {
|
||||
RoundOrder.add(<SortedTeam[I * 2], SortedTeam[I * 2 + 1]>);
|
||||
}
|
||||
return RoundOrder;
|
||||
}
|
||||
|
||||
Integer[] GetClansThatCanSpawn() {
|
||||
declare Boolean OnePlayerIsFinalist = False;
|
||||
foreach (Score in Scores) {
|
||||
if (Scores::GetPlayerMatchPoints(Score) >= S_PointsLimit) {
|
||||
OnePlayerIsFinalist = True;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!OnePlayerIsFinalist) {
|
||||
declare Int2[] Custom_RoundsVersus for This = [];
|
||||
declare Integer Custom_NbOfValidRounds for This = 0;
|
||||
|
||||
if (Custom_RoundsVersus.existskey(Custom_NbOfValidRounds)) {
|
||||
return [Custom_RoundsVersus[Custom_NbOfValidRounds].X, Custom_RoundsVersus[Custom_NbOfValidRounds].Y];
|
||||
}
|
||||
}
|
||||
|
||||
return [1, 2, 3, 4, 5, 6];
|
||||
}
|
||||
|
||||
Void SendPickManialink() {
|
||||
declare Text Manialink = """
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<manialink version="3" name="{{{C_ML_TeamPicker}}}">
|
||||
<stylesheet>
|
||||
<style class="center" halign="center" valign="center" />
|
||||
</stylesheet>
|
||||
<framemodel id="framemodel-team">
|
||||
<label id="label-team-name" pos="0 -5" class="center" textsize=3 textfont="GameFontExtraBold"/>
|
||||
|
||||
<frame id="frame-players" pos="0 -15">
|
||||
<label pos="0 0" class="center" textsize=2 textfont="GameFontSemiBold"/>
|
||||
<label pos="0 -5" class="center" textsize=2 textfont="GameFontSemiBold"/>
|
||||
<label pos="0 -10" class="center" textsize=2 textfont="GameFontSemiBold"/>
|
||||
<label pos="0 -15" class="center" textsize=2 textfont="GameFontSemiBold"/>
|
||||
</frame>
|
||||
|
||||
<label id="label-join-clan" pos="0 -76" class="center" style="CardButtonSmallS" text="Join" scriptevents=2/>
|
||||
</framemodel>
|
||||
<frame id="frame-global">
|
||||
<quad id="background" class="center" z-index="-1" size="180 80" bgcolor="000" opacity="0.7" />
|
||||
|
||||
<frame id="frame-teams">
|
||||
<frameinstance modelid="framemodel-team" pos="-75 40" data-clanid="1"/>
|
||||
<frameinstance modelid="framemodel-team" pos="-45 40" data-clanid="2"/>
|
||||
<frameinstance modelid="framemodel-team" pos="-15 40" data-clanid="3"/>
|
||||
<frameinstance modelid="framemodel-team" pos="15 40" data-clanid="4"/>
|
||||
<frameinstance modelid="framemodel-team" pos="45 40" data-clanid="5"/>
|
||||
<frameinstance modelid="framemodel-team" pos="75 40" data-clanid="6"/>
|
||||
</frame>
|
||||
</frame>
|
||||
<script><!--
|
||||
#Include "TextLib" as TL
|
||||
#Include "TimeLib" as TimeLib
|
||||
|
||||
#Const C_AttachId {{{dump(C_ML_TeamPicker)}}}
|
||||
|
||||
main() {
|
||||
log("Init");
|
||||
|
||||
declare CMlFrame Frame_Teams <=> (Page.GetFirstChild("frame-teams") as CMlFrame);
|
||||
foreach (Key => Control in Frame_Teams.Controls) {
|
||||
if (!Teams.existskey(Key)) break;
|
||||
declare CMlFrame Frame <=> Control as CMlFrame;
|
||||
declare CMlLabel Label_TeamName <=> (Frame.GetFirstChild("label-team-name") as CMlLabel);
|
||||
Label_TeamName.Value = Teams[Key].Name;
|
||||
Label_TeamName.TextColor = Teams[Key].ColorPrimary;
|
||||
}
|
||||
|
||||
// Wait C++ initialize the player
|
||||
wait (InputPlayer != Null);
|
||||
|
||||
declare netread Integer[Text] Net_Custom_PlayersClan for Teams[0] = [];
|
||||
|
||||
while(True) {
|
||||
yield;
|
||||
|
||||
declare Text[][Integer] PlayerPerTeams = [];
|
||||
|
||||
foreach (Player in Players) {
|
||||
if (!Net_Custom_PlayersClan.existskey(Player.User.WebServicesUserId)) continue;
|
||||
|
||||
declare Integer TeamId = Net_Custom_PlayersClan[Player.User.WebServicesUserId] - 1;
|
||||
|
||||
if (!PlayerPerTeams.existskey(TeamId)) PlayerPerTeams[TeamId] = [];
|
||||
PlayerPerTeams[TeamId].add(Player.User.Name);
|
||||
}
|
||||
|
||||
foreach (TeamId => Control in Frame_Teams.Controls) {
|
||||
declare CMlFrame Frame_Team <=> Control as CMlFrame;
|
||||
declare CMlFrame Frame_Players <=> (Frame_Team.GetFirstChild("frame-players") as CMlFrame);
|
||||
foreach (Key => Control in Frame_Players.Controls) {
|
||||
declare CMlLabel Label <=> Control as CMlLabel ;
|
||||
//declare CMlLabel Label <=> (Frame_Player.GetFirstChild("") as CMlLabel);
|
||||
if (PlayerPerTeams.existskey(TeamId) && PlayerPerTeams[TeamId].existskey(Key)) {
|
||||
Label.Value = PlayerPerTeams[TeamId][Key];
|
||||
} else {
|
||||
Label.Value = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Event in PendingEvents) {
|
||||
if (Event.Type == CMlScriptEvent::Type::MouseClick) {
|
||||
if (Event.ControlId == "label-join-clan") {
|
||||
declare Text ClanId = Event.Control.Parent.DataAttributeGet("clanid");
|
||||
SendCustomEvent("Custom_Join_Clan", [ClanId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
--></script>
|
||||
</manialink>
|
||||
""";
|
||||
|
||||
Layers::Create(C_ML_TeamPicker, Manialink);
|
||||
Layers::SetType(C_ML_TeamPicker, CUILayer::EUILayerType::Normal);
|
||||
Layers::Attach(C_ML_TeamPicker);
|
||||
}
|
145
TM_AnimationEasingsExample.Script.txt
Normal file
145
TM_AnimationEasingsExample.Script.txt
Normal file
@ -0,0 +1,145 @@
|
||||
/**
|
||||
This gamemode is just an UI with all animations easings displayed.
|
||||
*/
|
||||
|
||||
#Extends "Modes/TrackMania/TM_TimeAttack_Online.Script.txt"
|
||||
|
||||
#Const C_Anims [
|
||||
"Linear",
|
||||
"QuadIn", "QuadOut", "QuadInOut",
|
||||
"CubicIn", "CubicOut", "CubicInOut",
|
||||
"QuartIn", "QuartOut", "QuartInOut",
|
||||
"QuintIn", "QuintOut", "QuintInOut",
|
||||
"SineIn", "SineOut", "SineInOut",
|
||||
"ExpIn", "ExpOut", "ExpInOut",
|
||||
"CircIn", "CircOut", "CircInOut",
|
||||
"BackIn", "BackOut", "BackInOut",
|
||||
"ElasticIn", "ElasticOut", "ElasticInOut",
|
||||
"ElasticIn2", "ElasticOut2", "ElasticInOut2",
|
||||
"BounceIn", "BounceOut", "BounceInOut"
|
||||
]
|
||||
|
||||
***Match_AfterLoadHud***
|
||||
***
|
||||
SetMl();
|
||||
***
|
||||
|
||||
Void SetMl() {
|
||||
declare Integer IndexX = 0;
|
||||
declare Integer IndexY = 0;
|
||||
|
||||
declare Text FrameInstances;
|
||||
|
||||
for (I, 0, C_Anims.count - 1) {
|
||||
FrameInstances ^= """<frameinstance modelid="framemodel" pos="{{{IndexX * 15.}}} {{{IndexY * -20.}}}"/>""";
|
||||
|
||||
if (IndexX == 4) {
|
||||
IndexX = 0;
|
||||
IndexY += 1;
|
||||
} else {
|
||||
IndexX += 1;
|
||||
}
|
||||
}
|
||||
|
||||
declare Text MLText = """
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<manialink version="3" name="AnimationEasingsExamples">
|
||||
<framemodel id="framemodel">
|
||||
<quad id="quad-background" size="15 18" halign="center" bgcolor="fff" opacity="0" scriptevents="1"/>
|
||||
<frame pos="0 -0.5">
|
||||
<quad id="quad-graph-brackground" size="14 14" halign="center" bgcolor="000"/>
|
||||
<graph id="graph" pos="0.5 -0.5" size="13 13" halign="center" min="0 -0.3" max="105. 1.3"/>
|
||||
</frame>
|
||||
<label id="label-name" size="14 10" pos="0 -15" textsize="1" halign="center" textcolor="ffffff" textfont="GameFontRegular"/>
|
||||
</framemodel>
|
||||
<frame z-index=20>
|
||||
<quad z-index="-2" halign="center" valign="center" size="250 150" bgcolor="222" />
|
||||
<frame id="frame-anims" pos="-115 68">
|
||||
{{{FrameInstances}}}
|
||||
</frame>
|
||||
<frame pos="-40 50">
|
||||
<quad size="150 0.2" bgcolor="aaa"/>
|
||||
<quad id="quad-point1" size="5 5" bgcolor="fff"/>
|
||||
<quad pos="0 -80" size="150 0.2" bgcolor="aaa"/>
|
||||
</frame>
|
||||
<frame pos="-30 -50">
|
||||
<quad pos="0 2.5" size="0.2 10" bgcolor="aaa"/>
|
||||
<quad id="quad-point2" size="5 5" bgcolor="fff"/>
|
||||
<quad pos="130 2.5" size="0.2 10" bgcolor="aaa"/>
|
||||
</frame>
|
||||
<label id="label-currentanimname" pos="40 -65" textsize="3" halign="center" textcolor="ffffff" textfont="GameFontSemiBold" text="Linear"/>
|
||||
</frame>
|
||||
<script><!--
|
||||
#Include "AnimLib" as AL
|
||||
#Include "TimeLib" as TiL
|
||||
#Include "MathLib" as ML
|
||||
|
||||
#Const ScriptName "AnimationEasingsExamples"
|
||||
#Const Version "2024-02-19"
|
||||
|
||||
#Const C_Anims {{{dump(C_Anims)}}}
|
||||
|
||||
#Const C_AnimationDuration 1500
|
||||
|
||||
Void InitFrameInstances() {
|
||||
declare CMlFrame Frame_Anims <=> Page.GetFirstChild("frame-anims") as CMlFrame;
|
||||
|
||||
declare Integer Index = 0;
|
||||
|
||||
foreach (AnimName in C_Anims) {
|
||||
declare CMlFrame Frame <=> Frame_Anims.Controls[Index] as CMlFrame;
|
||||
|
||||
declare Text AnimationName for Frame;
|
||||
AnimationName = AnimName;
|
||||
|
||||
declare CMlLabel Label_Name <=> Frame.GetFirstChild("label-name") as CMlLabel;
|
||||
Label_Name.Value = AnimName;
|
||||
|
||||
declare CMlGraph Graph = Frame.GetFirstChild("graph") as CMlGraph;
|
||||
declare CMlGraphCurve Curve = Graph.AddCurve();
|
||||
Curve.Width = 0.3;
|
||||
for (I, 0, 100) {
|
||||
Curve.Points.add(<I * 1., AL::Ease(AnimName, I * 1., 0., 1., 100.)>);
|
||||
}
|
||||
|
||||
Index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
InitFrameInstances();
|
||||
declare CMlLabel Label_CurrentAnimName <=> Page.GetFirstChild("label-currentanimname") as CMlLabel;
|
||||
declare CMlQuad Quad_Point1 <=> Page.GetFirstChild("quad-point1") as CMlQuad;
|
||||
declare CMlQuad Quad_Point2 <=> Page.GetFirstChild("quad-point2") as CMlQuad;
|
||||
|
||||
declare Integer Last_PlayAnimationTime;
|
||||
|
||||
while (True) {
|
||||
yield;
|
||||
|
||||
foreach (Event in PendingEvents) {
|
||||
if (Event.Type == CMlScriptEvent::Type::MouseOver) {
|
||||
(Event.Control as CMlQuad).Opacity = 0.2;
|
||||
} else if (Event.Type == CMlScriptEvent::Type::MouseOut) {
|
||||
(Event.Control as CMlQuad).Opacity = 0.;
|
||||
} else if (Event.Type == CMlScriptEvent::Type::MouseClick) {
|
||||
declare Text AnimationName for Event.Control.Parent;
|
||||
|
||||
Label_CurrentAnimName.Value = AnimationName;
|
||||
Last_PlayAnimationTime = Now;
|
||||
}
|
||||
}
|
||||
|
||||
Quad_Point1.RelativePosition_V3.X = AL::Ease("Linear", Now - Last_PlayAnimationTime * 1., 0., 145., C_AnimationDuration * 1.);
|
||||
Quad_Point1.RelativePosition_V3.Y = AL::Ease(Label_CurrentAnimName.Value, Now - Last_PlayAnimationTime * 1., 0., -75., C_AnimationDuration * 1.);
|
||||
|
||||
Quad_Point2.RelativePosition_V3.X = AL::Ease(Label_CurrentAnimName.Value, Now - Last_PlayAnimationTime * 1., 0., 125., C_AnimationDuration * 1.);
|
||||
}
|
||||
}
|
||||
--></script>
|
||||
</manialink>
|
||||
""";
|
||||
Layers::Create("AnimationEasingsExamples", MLText);
|
||||
Layers::SetType("AnimationEasingsExamples", CUILayer::EUILayerType::Normal);
|
||||
Layers::Attach("AnimationEasingsExamples");
|
||||
}
|
@ -3,10 +3,10 @@
|
||||
*/
|
||||
// #RequireContext CSmMode
|
||||
|
||||
#Extends "Libs/Nadeo/TMNext/TrackMania/Modes/TMNextBase.Script.txt"
|
||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaBase.Script.txt"
|
||||
|
||||
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||
#Const Version "2022-05-20"
|
||||
#Const Version "2023-09-25"
|
||||
#Const ScriptName "Modes/TM2020-Gamemodes/TM_CPKnockout.Script.txt"
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
@ -14,14 +14,14 @@
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Include "TextLib" as TL
|
||||
#Include "MathLib" as ML
|
||||
#Include "Libs/Nadeo/TMNext/TrackMania/Menu/Constants.Script.txt" as MenuConsts
|
||||
#Include "Libs/Nadeo/TMNext/TrackMania/Modes/Rounds/StateManager.Script.txt" as StateMgr
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/ScoresTable_Server.Script.txt" as UIModules_ScoresTable
|
||||
#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 "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/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
|
||||
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Settings
|
||||
@ -51,7 +51,7 @@
|
||||
#Const Description _("$zIn $<$t$6F9Laps$> mode, the goal is to drive as far as possible by passing $<$t$6F9checkpoints$>.\n\nThe laps mode takes place on multilap (cyclical) maps, and is played in one go for every map.\n\nWhen the time is up, the $<$t$6F9winner$> is the player who passed the most $<$t$6F9checkpoints$>. In case of draws, the winner is the player who passed the last checkpoint first.")
|
||||
|
||||
#Const C_HudModulePath "" //< Path to the hud module
|
||||
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/TMNext/TrackMania/Laps/Laps.Script.txt" //< Url of the mania app
|
||||
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/Laps.Script.txt" //< Url of the mania app
|
||||
|
||||
#Const C_UploadRecord True
|
||||
#Const C_DisplayRecordGhost False
|
||||
@ -387,6 +387,6 @@ Void EliminatePlayer(CSmPlayer _Player) {
|
||||
UIManager.UIAll.SendChat("Player $<$ff6" ^ _Player.User.Name ^ "$> is $<$f00eliminated$>");
|
||||
|
||||
// waiting GetCustomPoints function in ScoresTable_Server
|
||||
declare netwrite Text[][Text] Net_TMxSM_ScoresTable_CustomPoints for Teams[0];
|
||||
Net_TMxSM_ScoresTable_CustomPoints[_Player.User.WebServicesUserId] = [_("|Status|K.O."), "f00"];
|
||||
declare netwrite Text[][Text] Net_TMGame_ScoresTable_CustomPoints for Teams[0];
|
||||
Net_TMGame_ScoresTable_CustomPoints[_Player.User.WebServicesUserId] = [_("|Status|K.O."), "f00"];
|
||||
}
|
476
TM_CPKnockout_V2.Script.txt
Normal file
476
TM_CPKnockout_V2.Script.txt
Normal file
@ -0,0 +1,476 @@
|
||||
/**
|
||||
* CP Knockout V2 mode
|
||||
* Similar to TM_CPKnockout.Script.txt but allow multiple rounds, and can finish before the finish if not enough players
|
||||
*/
|
||||
|
||||
// #RequireContext CSmMode
|
||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
||||
|
||||
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||
#Const Version "2023-11-27"
|
||||
#Const ScriptName "Modes/TM2020-Gamemodes/TM_CPKnockout.Script.txt"
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Libraries
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Include "TextLib" as TL
|
||||
#Include "MathLib" as ML
|
||||
#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/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
|
||||
|
||||
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Settings
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Setting S_DisableGiveUp True as _("Disable give up")
|
||||
#Setting S_NumberOfFinishers 8
|
||||
#Setting S_FinishTimeout -1 as _("Finish timeout")
|
||||
#Setting S_WarmUpNb 1 as _("Number of warm up")
|
||||
#Setting S_WarmUpDuration 30 as _("Duration of one warm up")
|
||||
#Setting S_WarmUpTimeout -1 as _("Warm up timeout")
|
||||
|
||||
#Setting S_EliminatedPlayersNbRanks "2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0,2,0,0,0,0" as "Number of eliminated by checkpoint sorted from the finish to the start"
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Constants
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Const C_ModeName "CP Knockout"
|
||||
//L16N [Laps] Description of the mode rules
|
||||
#Const Description "Knockout per checkpoint game mode"
|
||||
|
||||
#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 True
|
||||
#Const C_CelebrateRecordMedal True
|
||||
|
||||
#Struct K_State {
|
||||
Integer NbAliveAfter;
|
||||
Integer NbFinishers;
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// 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_UseDefaultHud = (C_HudModulePath == "");
|
||||
MB_Settings_UseDefaultTimer = 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***
|
||||
***
|
||||
ClientManiaAppUrl = C_ManiaAppUrl;
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
UIModules_TimeGap::SetTimeGapMode(UIModules_TimeGap::C_TimeGapMode_BestRace);
|
||||
UIModules_Checkpoint::SetRankMode(UIModules_Checkpoint::C_RankMode_BestRace);
|
||||
UIModules_Checkpoint::SetVisibilityTimeDiff(False, True);
|
||||
UIModules_PauseMenu_Online::SetHelp(Description);
|
||||
Scores::SaveInScore(Scores::C_Points_Match);
|
||||
UIModules_ScoresTable::SetScoreMode(UIModules_ScoresTable::C_Mode_PrevTime);
|
||||
UIModules_ScoresTable::SetHideSpectators(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_StartServer***
|
||||
***
|
||||
// Initialize mode
|
||||
Clans::SetClansNb(0);
|
||||
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
||||
WarmUp::SetAvailability(True);
|
||||
Race::SetRespawnBehaviour(Race::C_RespawnBehaviour_Normal);
|
||||
Race::SetupRecord(
|
||||
MenuConsts::C_ScopeType_Season,
|
||||
MenuConsts::C_ScopeType_PersonalBest,
|
||||
MenuConsts::C_GameMode_Laps,
|
||||
"",
|
||||
C_UploadRecord,
|
||||
C_DisplayRecordGhost,
|
||||
C_DisplayRecordMedal,
|
||||
C_CelebrateRecordGhost,
|
||||
C_CelebrateRecordMedal
|
||||
);
|
||||
|
||||
Race::UseAutomaticDossardColor(False);
|
||||
***
|
||||
|
||||
***Match_InitMatch***
|
||||
***
|
||||
foreach (Score in Scores) {
|
||||
declare Boolean IsAlive for Score = False;
|
||||
IsAlive = False;
|
||||
}
|
||||
UIModules_ScoresTable::SetCustomPoints([]);
|
||||
UIModules_ScoresTable::SetCustomTimes([]);
|
||||
|
||||
declare Boolean Match_InitPlayers = True;
|
||||
declare Integer Match_PlayersEliminated;
|
||||
declare Integer Match_Players;
|
||||
***
|
||||
|
||||
***Match_StartMap***
|
||||
***
|
||||
CarRank::Reset();
|
||||
|
||||
if (S_WarmUpNb > 0) {
|
||||
foreach (Score in Scores) {
|
||||
WarmUp::CanPlay(Score, True);
|
||||
}
|
||||
|
||||
MB_WarmUp(S_WarmUpNb, S_WarmUpDuration * 1000, S_WarmUpTimeout * 1000);
|
||||
}
|
||||
|
||||
if (Match_InitPlayers) {
|
||||
Match_InitPlayers = False;
|
||||
|
||||
foreach (Score in Scores) {
|
||||
declare Boolean IsAlive for Score = False;
|
||||
IsAlive = True;
|
||||
Match_Players += 1;
|
||||
|
||||
if (Score.User == Null) continue;
|
||||
}
|
||||
}
|
||||
|
||||
StartTime = Now + Race::C_SpawnDuration;
|
||||
if (S_DisableGiveUp) {
|
||||
Race::SetRespawnBehaviour(Race::C_RespawnBehaviour_NeverGiveUp);
|
||||
} else {
|
||||
Race::SetRespawnBehaviour(Race::C_RespawnBehaviour_Normal);
|
||||
}
|
||||
***
|
||||
|
||||
***Match_InitRound***
|
||||
***
|
||||
declare Integer Round_NumberOfPlayers = 0;
|
||||
declare Integer Round_DossardsUpdateCooldown = 0;
|
||||
|
||||
declare Text Round_EliminatedPlayersNbRanks = "";
|
||||
declare K_State[Integer] Round_State;
|
||||
|
||||
declare Ident[] Round_EliminatedScores;
|
||||
***
|
||||
|
||||
***Match_StartRound***
|
||||
***
|
||||
Round_EliminatedPlayersNbRanks = S_EliminatedPlayersNbRanks;
|
||||
|
||||
declare Integer[Text] CustomTimes = [];
|
||||
|
||||
foreach (Player in Players) {
|
||||
Player.Dossard_Color = <1., 1., 1.>;
|
||||
if (Player.Score == Null || Player.User == Null) continue;
|
||||
declare Boolean IsAlive for Player.Score = False;
|
||||
if (IsAlive) {
|
||||
Scores::SetPlayerMatchPoints(Player.Score, Match_Players);
|
||||
CustomTimes[Player.User.WebServicesUserId] = 0;
|
||||
Round_NumberOfPlayers += 1;
|
||||
}
|
||||
}
|
||||
UIModules_ScoresTable::SetCustomTimes(CustomTimes);
|
||||
|
||||
Round_State = ComputeState(Round_NumberOfPlayers);
|
||||
log("Round_State: " ^ Round_State);
|
||||
|
||||
EndTime = -1;
|
||||
***
|
||||
|
||||
***Match_PlayLoop***
|
||||
***
|
||||
// Manage race events
|
||||
foreach (Event in Race::GetPendingEvents()) {
|
||||
Race::ValidEvent(Event);
|
||||
|
||||
// Waypoint
|
||||
if (Event.Type == Events::C_Type_Waypoint) {
|
||||
if (Event.Player != Null) {
|
||||
declare Integer NBOfCP = Event.Player.RaceWaypointTimes.count;
|
||||
|
||||
if (Round_State.existskey(NBOfCP)) {
|
||||
Round_State[NBOfCP].NbFinishers += 1;
|
||||
|
||||
// Proceed kick
|
||||
if (Round_State[NBOfCP].NbFinishers >= Round_State[NBOfCP].NbAliveAfter) {
|
||||
foreach (Player in Players) {
|
||||
if (Player.SpawnStatus != CSmPlayer::ESpawnStatus::Spawned) continue;
|
||||
|
||||
if (Player.RaceWaypointTimes.count < NBOfCP) {
|
||||
EliminatePlayer(Player, Match_PlayersEliminated);
|
||||
Match_PlayersEliminated += 1;
|
||||
Round_EliminatedScores.add(Player.Score.Id);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (CPNb => State in Round_State) {
|
||||
if (CPNb > Event.Player.RaceWaypointTimes.count) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Event.IsEndRace) {
|
||||
Scores::UpdatePlayerPrevRace(Event.Player);
|
||||
|
||||
declare Integer[Text] CustomTimes = UIModules_ScoresTable::GetCustomTimes();
|
||||
CustomTimes.removekey(Event.Player.User.WebServicesUserId);
|
||||
UIModules_ScoresTable::SetCustomTimes(CustomTimes);
|
||||
|
||||
if (EndTime <= 0) {
|
||||
EndTime = Race::GetFinishTimeout(S_FinishTimeout, Race::GetLapsNb(), Map);
|
||||
}
|
||||
}
|
||||
|
||||
// Update best race at each checkpoint to sort scores with C_Sort_BestRaceCheckpointsProgress
|
||||
Scores::UpdatePlayerBestRace(Event.Player);
|
||||
|
||||
CarRank::ThrottleUpdate(CarRank::C_SortCriteria_CurrentRace);
|
||||
|
||||
if (Round_DossardsUpdateCooldown == 0) {
|
||||
UpdateDossardColors(Round_State);
|
||||
Round_DossardsUpdateCooldown = Now + 1000;
|
||||
}
|
||||
}
|
||||
} else if (Event.Type == Events::C_Type_SkipOutro) {
|
||||
if (Event.Player != Null) {
|
||||
Race::StopSkipOutro(Event.Player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Manage mode events
|
||||
foreach (Event in PendingEvents) {
|
||||
if (Event.HasBeenPassed || Event.HasBeenDiscarded) continue;
|
||||
Events::Invalid(Event);
|
||||
}
|
||||
|
||||
if (Round_DossardsUpdateCooldown > 0 && Round_DossardsUpdateCooldown < Now) {
|
||||
Round_DossardsUpdateCooldown = 0;
|
||||
UpdateDossardColors(Round_State);
|
||||
}
|
||||
|
||||
if (Round_EliminatedPlayersNbRanks != S_EliminatedPlayersNbRanks) {
|
||||
Round_EliminatedPlayersNbRanks = S_EliminatedPlayersNbRanks;
|
||||
|
||||
Round_State = ComputeState(Round_NumberOfPlayers);
|
||||
}
|
||||
|
||||
if (Players.count > 0 && Round_NumberOfPlayers > 0 && PlayersNbAlive <= S_NumberOfFinishers) {
|
||||
MB_StopMatch();
|
||||
}
|
||||
***
|
||||
|
||||
***Match_EndRound***
|
||||
***
|
||||
Race::StopSkipOutroAll();
|
||||
EndTime = -1;
|
||||
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
||||
CarRank::Update(CarRank::C_SortCriteria_CurrentRace);
|
||||
|
||||
if (Round_ForceEndRound || Round_SkipPauseRound) {
|
||||
declare Text[][Text] CustomPoints = UIModules_ScoresTable::GetCustomPoints();
|
||||
foreach (Score in Scores) {
|
||||
if (!Round_EliminatedScores.exists(Score.Id)) continue;
|
||||
|
||||
declare Boolean IsAlive for Score = False;
|
||||
IsAlive = True;
|
||||
|
||||
if (Score.User == Null || !CustomPoints.existskey(Score.User.WebServicesUserId)) continue;
|
||||
CustomPoints.removekey(Score.User.WebServicesUserId);
|
||||
}
|
||||
UIModules_ScoresTable::SetCustomPoints(CustomPoints);
|
||||
|
||||
// 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 {
|
||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
||||
UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound;
|
||||
MB_Sleep(S_ChatTime / 2);
|
||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal;
|
||||
UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing;
|
||||
MB_Sleep(S_ChatTime / 2);
|
||||
}
|
||||
***
|
||||
|
||||
***Match_EndMap***
|
||||
***
|
||||
// Ensure that we stop the match (after a vote for the next map, ...)
|
||||
MB_StopMatch();
|
||||
|
||||
EndTime = -1;
|
||||
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
||||
|
||||
CarRank::Update(CarRank::C_SortCriteria_CurrentRace);
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
Scores::SetPlayerWinner(Scores::GetBestPlayer(Scores::C_Sort_MatchPoints));
|
||||
Race::StopSkipOutroAll();
|
||||
***
|
||||
|
||||
***Rounds_CanSpawn***
|
||||
***
|
||||
foreach (Score in Scores) {
|
||||
declare Boolean ModeRounds_CanSpawn for Score = True;
|
||||
ModeRounds_CanSpawn = CanSpawn(Score);
|
||||
}
|
||||
***
|
||||
|
||||
***Rounds_CheckCanSpawn***
|
||||
***
|
||||
// During PlayLoop Check
|
||||
declare Boolean IsAlive for _Player.Score = False;
|
||||
return IsAlive;
|
||||
---PouleParty_Rounds_CanSpawn---
|
||||
***
|
||||
|
||||
Boolean CanSpawn(CSmScore _Score) {
|
||||
// Before PlayLoop Check
|
||||
declare Boolean IsAlive for _Score = False;
|
||||
return IsAlive;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Functions
|
||||
*
|
||||
*/
|
||||
|
||||
/** Compute Match State based on S_EliminatedPlayersNbRanks and number of Players
|
||||
*
|
||||
* @return K_State[Integer]
|
||||
*/
|
||||
K_State[Integer] ComputeState(Integer _NbOfPlayer) {
|
||||
declare K_State[Integer] State;
|
||||
|
||||
declare Text[] KORepartition = TL::Split(",", S_EliminatedPlayersNbRanks);
|
||||
|
||||
declare Integer NBOfCP = Map::GetCheckpointsCount() + 1;
|
||||
declare Integer NbAliveAfter = _NbOfPlayer;
|
||||
|
||||
for (I, 0 , NBOfCP) {
|
||||
declare Integer CPIndex = NBOfCP - I;
|
||||
if (KORepartition.existskey(CPIndex) && KORepartition[CPIndex] != "0") {
|
||||
NbAliveAfter -= TL::ToInteger(KORepartition[CPIndex]);
|
||||
State[I] = K_State{
|
||||
NbAliveAfter = NbAliveAfter
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return State;
|
||||
}
|
||||
|
||||
/** Update Dossard Color of Players depending of the CP and the Rank
|
||||
*
|
||||
* @return Void
|
||||
*/
|
||||
Void UpdateDossardColors(K_State[Integer] _Round_State) {
|
||||
Log::Log("UpdateDossardColors");
|
||||
declare Integer Rank = 1;
|
||||
|
||||
foreach (Score in Scores) {
|
||||
if (Score.User == Null) continue;
|
||||
|
||||
declare CSmPlayer Player = GetPlayer(Score.User.Login);
|
||||
if (Player == Null) continue;
|
||||
if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned) continue;
|
||||
|
||||
declare Integer NbAlive;
|
||||
|
||||
if (_Round_State.existskey(Player.CurrentLapNumber + 1)) {
|
||||
NbAlive = _Round_State[Player.CurrentLapNumber + 1].NbAliveAfter;
|
||||
} else {
|
||||
// get first CP
|
||||
foreach (Value in _Round_State) {
|
||||
NbAlive = Value.NbAliveAfter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Rank > NbAlive) {
|
||||
Player.Dossard_Color = <1., 0., 0.>;
|
||||
} else {
|
||||
Player.Dossard_Color = <1., 1., 1.>;
|
||||
}
|
||||
|
||||
Rank += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/** Eliminate Player and send a message in a Chat
|
||||
*
|
||||
* @return Void
|
||||
*/
|
||||
Void EliminatePlayer(CSmPlayer _Player, Integer _Eliminated) {
|
||||
if (_Player == Null || _Player.Score == Null) return;
|
||||
if (_Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned) return;
|
||||
log("EliminatePlayer: " ^ _Player.User.Name ^ " (" ^ _Player.User.Login ^ ")");
|
||||
Race::StopSkipOutro(_Player);
|
||||
UIManager.UIAll.SendChat("Player $<$ff6" ^ _Player.User.Name ^ "$> is $<$f00eliminated$>");
|
||||
|
||||
declare Text[][Text] CustomPoints = UIModules_ScoresTable::GetCustomPoints();
|
||||
CustomPoints[_Player.User.WebServicesUserId] = [_("|Status|K.O."), "f00"];
|
||||
UIModules_ScoresTable::SetCustomPoints(CustomPoints);
|
||||
|
||||
Scores::SetPlayerMatchPoints(_Player.Score, _Eliminated);
|
||||
|
||||
declare Boolean IsAlive for _Player.Score = False;
|
||||
IsAlive = False;
|
||||
}
|
689
TM_ClimbTheMap.Script.txt
Normal file
689
TM_ClimbTheMap.Script.txt
Normal file
@ -0,0 +1,689 @@
|
||||
/**
|
||||
* 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 = <ML::CeilingInteger(Start.Position.Y), ML::CeilingInteger(FinishAltitude)>;
|
||||
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 ^= """<frameinstance modelid="framemodel-marker" hidden="1"/>""";
|
||||
}
|
||||
|
||||
|
||||
declare Text MLText = """
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<manialink version="3" name="{{{C_MlId_LiveAltitude}}}">
|
||||
<stylesheet>
|
||||
<style class="background" bgcolor="000" opacity="0.8" textcolor="000"/>
|
||||
</stylesheet>
|
||||
|
||||
<framemodel id="framemodel-dot">
|
||||
<quad class="background" size="1 1"/>
|
||||
<label id="label-dotaltitude" class="background" pos="2 0" valign="center" textsize="1" textfont="GameFontExtraBold"/>
|
||||
</framemodel>
|
||||
<framemodel id="framemodel-marker">
|
||||
<quad class="background" size="2 3.5" valign="center" image="https://files.virtit.fr/TrackMania/UI/triangle.png"/>
|
||||
<quad id="quad-background" class="background" pos="2 0" size="0 3.5" valign="center"/>
|
||||
<label id="label-playername" pos="3 -0.2" valign="center2" textsize="1" textcolor="888" textfont="GameFontSemiBold"/>
|
||||
</framemodel>
|
||||
<frame id="frame-global" hidden="1" pos="-150 75">
|
||||
<frame z-index="-10">
|
||||
<quad class="background" size="1 121"/>
|
||||
<frame id="frame-dots" pos="1 -120">
|
||||
<frameinstance modelid="framemodel-dot" pos="0 0"/>
|
||||
<frameinstance modelid="framemodel-dot" pos="0 15"/>
|
||||
<frameinstance modelid="framemodel-dot" pos="0 30"/>
|
||||
<frameinstance modelid="framemodel-dot" pos="0 45"/>
|
||||
<frameinstance modelid="framemodel-dot" pos="0 60"/>
|
||||
<frameinstance modelid="framemodel-dot" pos="0 75"/>
|
||||
<frameinstance modelid="framemodel-dot" pos="0 90"/>
|
||||
<frameinstance modelid="framemodel-dot" pos="0 105"/>
|
||||
<frameinstance modelid="framemodel-dot" pos="0 120"/>
|
||||
</frame>
|
||||
</frame>
|
||||
<quad id="quad-invisiblebutton" pos="-10 10" size="45 140" scriptevents="1"/>
|
||||
<frame pos="2 -120">
|
||||
<frameinstance id="frame-marker-wr" modelid="framemodel-marker" z-index="-1" hidden="1"/>
|
||||
<frameinstance id="frame-marker-pb" modelid="framemodel-marker" z-index="-2" hidden="1"/>
|
||||
<frameinstance id="frame-marker-owner" modelid="framemodel-marker" z-index="2" hidden="1"/>
|
||||
<frame id="frame-markers-live" >
|
||||
{{{FrameInstances}}}
|
||||
</frame>
|
||||
</frame>
|
||||
</frame>
|
||||
<script><!--
|
||||
|
||||
#Include "TextLib" as TL
|
||||
#Include "MathLib" as ML
|
||||
|
||||
#Const C_Type_Live 0
|
||||
#Const C_Type_Owner 1
|
||||
#Const C_Type_PB 2
|
||||
#Const C_Type_WR 3
|
||||
|
||||
Real GetPosition(Int2 _AltitudeOfWaypoints, Integer _Altitude) {
|
||||
if (_AltitudeOfWaypoints.Y == _AltitudeOfWaypoints.X) return 0.;
|
||||
return (120. * (_Altitude - _AltitudeOfWaypoints.X)) / (_AltitudeOfWaypoints.Y - _AltitudeOfWaypoints.X);
|
||||
}
|
||||
|
||||
Void UpdateMarker(CMlFrame _Frame, Int2 _AltitudeOfWaypoints, Integer _AnimationDuration, Integer _Type, Text _Name, Integer _Altitude) {
|
||||
_Frame.Visible = True;
|
||||
|
||||
declare Text PlayerName for _Frame;
|
||||
|
||||
// Change Label only if name changed
|
||||
declare Boolean ChangeLabel = (PlayerName != _Name);
|
||||
PlayerName = _Name;
|
||||
|
||||
declare CMlLabel Label_PlayerName <=> (_Frame.GetFirstChild("label-playername") as CMlLabel);
|
||||
|
||||
if (ChangeLabel && _Type != C_Type_PB) {
|
||||
Label_PlayerName.Value = _Name;
|
||||
declare CMlQuad Quad_Background <=> (_Frame.GetFirstChild("quad-background") as CMlQuad);
|
||||
Quad_Background.Size.X = Label_PlayerName.ComputeWidth(_Name) + 2.5;
|
||||
}
|
||||
|
||||
AnimMgr.Flush(_Frame);
|
||||
if (_AnimationDuration <= 0 || ChangeLabel) {
|
||||
_Frame.RelativePosition_V3.Y = GetPosition(_AltitudeOfWaypoints, _Altitude);
|
||||
} else {
|
||||
AnimMgr.Add(_Frame, "<a pos=\"0 " ^ GetPosition(_AltitudeOfWaypoints, _Altitude) ^ "\"/>", _AnimationDuration, CAnimManager::EAnimManagerEasing::Linear);
|
||||
}
|
||||
}
|
||||
|
||||
Void SetMarkerStyle(CMlFrame _Frame, Integer _Type) {
|
||||
declare CMlLabel Label_PlayerName <=> (_Frame.GetFirstChild("label-playername") as CMlLabel);
|
||||
|
||||
switch (_Type) {
|
||||
case C_Type_Live: {
|
||||
Label_PlayerName.TextColor = <.5, .5, .5>;
|
||||
Label_PlayerName.TextFont = "GameFontSemiBold";
|
||||
_Frame.ZIndex = 0.;
|
||||
}
|
||||
case C_Type_Owner: {
|
||||
Label_PlayerName.TextColor = <1., 1., 1.>;
|
||||
Label_PlayerName.TextFont = "GameFontExtraBold";
|
||||
}
|
||||
case C_Type_PB: {
|
||||
Label_PlayerName.TextColor = <0.251,0.741,0.239>;
|
||||
Label_PlayerName.TextFont = "GameFontExtraBold";
|
||||
Label_PlayerName.Value = "Personal Best";
|
||||
|
||||
declare CMlQuad Quad_Background <=> (_Frame.GetFirstChild("quad-background") as CMlQuad);
|
||||
Quad_Background.Size.X = Label_PlayerName.ComputeWidth(Label_PlayerName.Value) + 2.5;
|
||||
}
|
||||
case C_Type_WR: {
|
||||
Label_PlayerName.TextColor = <1.,0.733,0.043>;
|
||||
Label_PlayerName.TextFont = "GameFontExtraBold";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
declare CMlFrame Frame_Global <=> (Page.GetFirstChild("frame-global") as CMlFrame) ;
|
||||
|
||||
declare CMlFrame Frame_Dots <=> (Frame_Global.GetFirstChild("frame-dots") as CMlFrame);
|
||||
declare CMlFrame Frame_MarkersLive <=> (Frame_Global.GetFirstChild("frame-markers-live") as CMlFrame);
|
||||
|
||||
declare CMlFrame[] LiveMarkers;
|
||||
foreach (Control in Frame_MarkersLive.Controls) {
|
||||
LiveMarkers.add(Control as CMlFrame);
|
||||
}
|
||||
|
||||
declare CMlFrame Frame_Marker_WR <=> (Frame_Global.GetFirstChild("frame-marker-wr") as CMlFrame);
|
||||
SetMarkerStyle(Frame_Marker_WR, C_Type_WR);
|
||||
declare CMlFrame Frame_Marker_PB <=> (Frame_Global.GetFirstChild("frame-marker-pb") as CMlFrame);
|
||||
SetMarkerStyle(Frame_Marker_PB, C_Type_PB);
|
||||
declare CMlFrame Frame_Marker_Owner <=> (Frame_Global.GetFirstChild("frame-marker-owner") as CMlFrame);
|
||||
SetMarkerStyle(Frame_Marker_Owner, C_Type_Owner);
|
||||
|
||||
wait(InputPlayer != Null);
|
||||
Frame_Global.Visible = True;
|
||||
|
||||
declare netread Integer Net_ClimbTheMap_UpdateFrequency for Teams[0] = 500;
|
||||
|
||||
declare netread Integer Net_ClimbTheMap_AltitudeOfWR for Teams[0];
|
||||
declare netread Text Net_ClimbTheMap_NamefWR for Teams[0];
|
||||
|
||||
declare netread Int2 Net_ClimbTheMap_AltitudeOfWaypoints for Teams[0];
|
||||
declare netread Integer Net_ClimbTheMap_AltitudeOfWaypoints_Update for Teams[0];
|
||||
|
||||
declare Integer Last_AltitudeOfWaypoints = -1;
|
||||
|
||||
declare netread Integer[Text] Net_ClimbTheMap_AltitudePerName for Teams[0];
|
||||
declare netread Integer Net_ClimbTheMap_AltitudePerName_Update for Teams[0];
|
||||
|
||||
declare Integer Last_AltitudePerName = -1;
|
||||
|
||||
declare Boolean Last_PageWasVisible;
|
||||
|
||||
while (True) {
|
||||
yield;
|
||||
|
||||
if (PageIsVisible) {
|
||||
if (Last_AltitudeOfWaypoints != Net_ClimbTheMap_AltitudeOfWaypoints_Update) {
|
||||
Last_AltitudeOfWaypoints = Net_ClimbTheMap_AltitudeOfWaypoints_Update;
|
||||
|
||||
foreach (Key => Control in Frame_Dots.Controls) {
|
||||
declare CMlFrame Frame <=> (Control as CMlFrame);
|
||||
declare CMlLabel Label_DotAltidude <=> (Frame.GetFirstChild("label-dotaltitude") as CMlLabel);
|
||||
|
||||
if (Key == 0) {
|
||||
Label_DotAltidude.Value = "Start";
|
||||
} else if (Key == Frame_Dots.Controls.count - 1) {
|
||||
Label_DotAltidude.Value = "Finish";
|
||||
} else {
|
||||
Label_DotAltidude.Value = TL::ToText((Net_ClimbTheMap_AltitudeOfWaypoints.Y - Net_ClimbTheMap_AltitudeOfWaypoints.X) / (Frame_Dots.Controls.count - 1) * Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Last_AltitudePerName != Net_ClimbTheMap_AltitudePerName_Update) {
|
||||
Last_AltitudePerName = Net_ClimbTheMap_AltitudePerName_Update;
|
||||
|
||||
declare Integer AnimationDuration = 0;
|
||||
if (Last_PageWasVisible) AnimationDuration = Net_ClimbTheMap_UpdateFrequency;
|
||||
|
||||
if (Net_ClimbTheMap_AltitudeOfWR > 0) {
|
||||
UpdateMarker(Frame_Marker_WR, Net_ClimbTheMap_AltitudeOfWaypoints, AnimationDuration, C_Type_WR, Net_ClimbTheMap_NamefWR, Net_ClimbTheMap_AltitudeOfWR);
|
||||
}
|
||||
|
||||
declare CSmPlayer Owner <=> InputPlayer;
|
||||
if (GUIPlayer != Null && GUIPlayer.User != Null) {
|
||||
Owner <=> GUIPlayer;
|
||||
}
|
||||
|
||||
declare Text OwnerName = Owner.User.Name;
|
||||
|
||||
declare netread Integer Net_ClimbTheMap_AltitudeOfPB for Owner;
|
||||
if (Net_ClimbTheMap_AltitudeOfPB > 0 && Net_ClimbTheMap_AltitudeOfPB != Net_ClimbTheMap_AltitudeOfWR) {
|
||||
UpdateMarker(Frame_Marker_PB, Net_ClimbTheMap_AltitudeOfWaypoints, AnimationDuration, C_Type_PB, OwnerName, Net_ClimbTheMap_AltitudeOfPB);
|
||||
} else {
|
||||
Frame_Marker_PB.Visible = False;
|
||||
}
|
||||
|
||||
declare Integer I;
|
||||
|
||||
declare Boolean OwnerIsPassed;
|
||||
|
||||
foreach (Name => Altitude in Net_ClimbTheMap_AltitudePerName) {
|
||||
if (!LiveMarkers.existskey(I)) break;
|
||||
|
||||
if (OwnerName == Name) {
|
||||
UpdateMarker(Frame_Marker_Owner, Net_ClimbTheMap_AltitudeOfWaypoints, AnimationDuration, C_Type_Owner, Name, Altitude);
|
||||
OwnerIsPassed = True;
|
||||
} else {
|
||||
UpdateMarker(LiveMarkers[I], Net_ClimbTheMap_AltitudeOfWaypoints, AnimationDuration, C_Type_Live, Name, Altitude);
|
||||
|
||||
I += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!OwnerIsPassed) {
|
||||
Frame_Marker_Owner.Visible = False;
|
||||
}
|
||||
|
||||
while (LiveMarkers.existskey(I) && LiveMarkers[I].Visible) {
|
||||
LiveMarkers[I].Visible = False;
|
||||
I += 1;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Event in PendingEvents) {
|
||||
if (Event.Type == CMlScriptEvent::Type::MouseClick && Event.ControlId == "quad-invisiblebutton") {
|
||||
TriggerPageAction("Trackmania.ClimbTheMap.ShowAltitudeRecords");
|
||||
}
|
||||
}
|
||||
|
||||
Last_PageWasVisible = True;
|
||||
} else {
|
||||
Last_PageWasVisible = False;
|
||||
}
|
||||
}
|
||||
}
|
||||
--></script>
|
||||
</manialink>
|
||||
""";
|
||||
Layers::Create(C_MlId_LiveAltitude, MLText);
|
||||
Layers::SetType(C_MlId_LiveAltitude, CUILayer::EUILayerType::Normal);
|
||||
Layers::Attach(C_MlId_LiveAltitude);
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
#Extends "Modes/TrackMania/TM_TimeAttack_Online.Script.txt"
|
||||
|
||||
#Include "Libs/Nadeo/Trackmania/MainMenu/Constants.Script.txt" as MenuConsts
|
||||
|
||||
#Setting S_TrustClientSimu False
|
||||
#Setting S_UseCrudeExtrapolation False
|
||||
|
||||
|
97
TM_ControllerBenchmark.Script.txt
Normal file
97
TM_ControllerBenchmark.Script.txt
Normal file
@ -0,0 +1,97 @@
|
||||
#Extends "Modes/TrackMania/TM_TimeAttack_Online.Script.txt"
|
||||
|
||||
#Setting S_RandomizedFakePlayersTimeMax 120000 as "Max time to connect or disconnect a player (0 = disabled)"
|
||||
#Setting S_RandomizedFakePlayersTimeMin 1000 as "Min time to connect or disconnect a player (0 = disabled)"
|
||||
#Setting S_RandomizedSpeedMax 100.
|
||||
#Setting S_RandomizedSpeedMin 30.
|
||||
|
||||
#Const C_CheckSpawnedTime 1000
|
||||
|
||||
***Match_InitMap***
|
||||
***
|
||||
declare Integer Map_CheckSpawnedPlayers;
|
||||
declare Integer Map_CheckConnectPlayer;
|
||||
***
|
||||
|
||||
***Match_StartMap***
|
||||
***
|
||||
if (ServerAdmin != Null) {
|
||||
MB_Sleep(500); // Wait a bit before if bot already exits
|
||||
log("Spawning " ^ServerAdmin.ServerInfo.MaxPlayerCount - 5 - Players.count ^ " bots");
|
||||
Users_SetNbFakeUsers(ML::Max(0, ServerAdmin.ServerInfo.MaxPlayerCount - 5 - Players.count) , 0);
|
||||
}
|
||||
if (RandomizedFakePlayersEnabled()) Map_CheckConnectPlayer = Now + ML::Rand(1000,120000);
|
||||
***
|
||||
|
||||
***Match_PlayLoop***
|
||||
***
|
||||
foreach (Event in Race::GetPendingEvents()) {
|
||||
if (Event.Type == Events::C_Type_StartLine) {
|
||||
ApplyRandomSpeed(Event.Player);
|
||||
}
|
||||
}
|
||||
|
||||
// check if a player joined the server or if spectator became player
|
||||
if (Map_CheckSpawnedPlayers < Now) {
|
||||
Map_CheckSpawnedPlayers = Now + C_CheckSpawnedTime;
|
||||
|
||||
foreach (Player in Players) {
|
||||
if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::Spawned && Player.TrustClientSimu) {
|
||||
ApplyRandomSpeed(Player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (RandomizedFakePlayersEnabled() && Map_CheckConnectPlayer < Now && ServerAdmin != Null) {
|
||||
Map_CheckConnectPlayer = Now + ML::Rand(1000,120000);
|
||||
if (Players.count >= ServerAdmin.ServerInfo.MaxPlayerCount) {
|
||||
log("Removing randomly FakePlayer");
|
||||
DisconnectRandomBot();
|
||||
} else if (ML::Rand(0, 1) == 1) {
|
||||
log("Removing randomly FakePlayer");
|
||||
DisconnectRandomBot();
|
||||
} else {
|
||||
log("Adding Fakeplayer");
|
||||
Users_CreateFake("", 0);
|
||||
}
|
||||
}
|
||||
***
|
||||
|
||||
Void ApplyRandomSpeed(CSmPlayer _Player) {
|
||||
if (_Player == Null) return;
|
||||
if (_Player.SpawnStatus != CSmPlayer::ESpawnStatus::Spawned) return;
|
||||
|
||||
declare Integer Race_RaceState for _Player = Race::C_RaceState_Waiting;
|
||||
|
||||
if (Race_RaceState == 3 && _Player.IsFakePlayer) {
|
||||
Race::EndIntro(_Player);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Race_RaceState != Race::C_RaceState_Racing) return;
|
||||
|
||||
_Player.TrustClientSimu = False;
|
||||
SetPlayerVehicle_ControlledByMode(_Player, True);
|
||||
SetPlayerVehicle_TargetSpeedValue(_Player, ML::Rand(S_RandomizedSpeedMin, S_RandomizedSpeedMax));
|
||||
}
|
||||
|
||||
Void DisconnectRandomBot() {
|
||||
if (Players.count <= 0) return;
|
||||
declare Integer Index = ML::Rand(0, Players.count - 1);
|
||||
declare Integer Iteration = 0;
|
||||
while (Players.count > 0 && Iteration < Players.count) {
|
||||
if (!Players.existskey(Index)) {
|
||||
Index = 0;
|
||||
}
|
||||
|
||||
if (Players[Index].IsFakePlayer) {
|
||||
Users_DestroyFake(Players[Index].User);
|
||||
return;
|
||||
}
|
||||
Iteration += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Boolean RandomizedFakePlayersEnabled() {
|
||||
return (S_RandomizedFakePlayersTimeMin > 0 && S_RandomizedFakePlayersTimeMax > 0);
|
||||
}
|
@ -3,17 +3,17 @@
|
||||
* This mode is used to debug your modes during development.
|
||||
* To use it, you just have to change the line below to put the path of your mode:
|
||||
*/
|
||||
#Extends "Modes/TrackMania/TM_TimeAttack_Online.Script.txt"
|
||||
#Extends "Modes/TrackMania/TM_Rounds_Online.Script.txt"
|
||||
|
||||
// #RequireContext CSmMode
|
||||
#Include "TimeLib" as TiL
|
||||
#Include "MathLib" as ML
|
||||
#Include "TextLib" as TL
|
||||
#Include "TimeLib" as DebugMode_TiL // @mslint-disable-line include-use-common-namespace
|
||||
#Include "MathLib" as DebugMode_ML // @mslint-disable-line include-use-common-namespace
|
||||
#Include "TextLib" as DebugMode_TL // @mslint-disable-line include-use-common-namespace
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Constants
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Const C_DebugMode_Version "2023-09-01"
|
||||
#Const C_DebugMode_Version "2024-05-08"
|
||||
#Const C_DebugMode_MainUI "DebugMode_MainUI"
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
@ -29,7 +29,7 @@ log("~~~~~~~~~~~~~ Starting DebugMode v" ^ C_DebugMode_Version ^ " ~~~~~~~~~~~~~
|
||||
log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
log("~~ Launched at: "^ System.CurrentLocalDateText ^" ("^ System.CurrentTimezone ^")");
|
||||
log("~~ Server version: "^ System.ExeVersion);
|
||||
log("~~ TitlePack version: "^ TL::Split(" ", LoadedTitle.TitleVersion)[0]);
|
||||
log("~~ TitlePack version: "^ DebugMode_TL::Split(" ", LoadedTitle.TitleVersion)[0]);
|
||||
log("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
***
|
||||
|
||||
@ -45,7 +45,7 @@ if (S_DebugLib_RestrictUIto == "") {
|
||||
Layers::Attach(C_DebugMode_MainUI);
|
||||
} else {
|
||||
Layers::Detach(C_DebugMode_MainUI);
|
||||
foreach (Login in TL::Split(",", S_DebugLib_RestrictUIto)) {
|
||||
foreach (Login in DebugMode_TL::Split(",", S_DebugLib_RestrictUIto)) {
|
||||
declare CSmPlayer Player = GetPlayer(Login);
|
||||
if (Player != Null) Layers::Attach(C_DebugMode_MainUI, Player);
|
||||
}
|
||||
@ -63,13 +63,13 @@ foreach (Event in UIManager.PendingEvents) {
|
||||
}
|
||||
case "DebugMode.Bots.DelBots": {
|
||||
declare Text Name = Event.CustomEventData[0];
|
||||
declare Integer NumberOfBots = TL::ToInteger(Event.CustomEventData[1]);
|
||||
declare Integer NumberOfBots = DebugMode_TL::ToInteger(Event.CustomEventData[1]);
|
||||
if (NumberOfBots == -1) {
|
||||
Users_DestroyAllFakes();
|
||||
} else {
|
||||
foreach (Player in AllPlayers) {
|
||||
if (Player.IsFakePlayer) {
|
||||
if (Name == "" || TL::ToLowerCase(Player.User.Name) == TL::ToLowerCase(Name)) {
|
||||
if (Name == "" || DebugMode_TL::ToLowerCase(Player.User.Name) == DebugMode_TL::ToLowerCase(Name)) {
|
||||
Net_DebugMode_Logs = Prefix ^ "Bots: Remove bot \"" ^ Player.User.Name ^ "\"\n" ^ Net_DebugMode_Logs;
|
||||
Users_DestroyFake(Player.User);
|
||||
NumberOfBots -= 1;
|
||||
@ -83,9 +83,9 @@ foreach (Event in UIManager.PendingEvents) {
|
||||
}
|
||||
case "DebugMode.Bots.AddBots": {
|
||||
declare Text Name = Event.CustomEventData[0];
|
||||
declare Integer TeamId = TL::ToInteger(Event.CustomEventData[1]);
|
||||
declare Integer TeamId = DebugMode_TL::ToInteger(Event.CustomEventData[1]);
|
||||
if (TeamId == -1 && Event.CustomEventData[1] == "") TeamId = 0;
|
||||
declare Integer NumberOfBots = TL::ToInteger(Event.CustomEventData[2]);
|
||||
declare Integer NumberOfBots = DebugMode_TL::ToInteger(Event.CustomEventData[2]);
|
||||
if (TeamId != -1 && Teams.existskey(TeamId)) {
|
||||
for (I, 1, NumberOfBots) {
|
||||
declare CUser User = Users_CreateFake(Name, TeamId);
|
||||
@ -114,13 +114,13 @@ foreach (Event in UIManager.PendingEvents) {
|
||||
}
|
||||
}
|
||||
declare Real[] Speed;
|
||||
foreach (SpeedText in TL::Split(",", Event.CustomEventData[0])) {
|
||||
Speed.add(ML::Clamp(TL::ToReal(SpeedText), -999., 999.));
|
||||
foreach (SpeedText in DebugMode_TL::Split(",", Event.CustomEventData[0])) {
|
||||
Speed.add(DebugMode_ML::Clamp(DebugMode_TL::ToReal(SpeedText), -999., 999.));
|
||||
}
|
||||
if (Speed.count > 0) {
|
||||
declare Real[] Steer;
|
||||
foreach (SteerText in TL::Split(",", Event.CustomEventData[1])) {
|
||||
Steer.add(ML::Clamp(TL::ToReal(SteerText), -1., 1.));
|
||||
foreach (SteerText in DebugMode_TL::Split(",", Event.CustomEventData[1])) {
|
||||
Steer.add(DebugMode_ML::Clamp(DebugMode_TL::ToReal(SteerText), -1., 1.));
|
||||
}
|
||||
|
||||
foreach (Target in Targets) {
|
||||
@ -131,7 +131,7 @@ foreach (Event in UIManager.PendingEvents) {
|
||||
|
||||
declare Real SteerValue;
|
||||
if (Steer.count == 2) {
|
||||
SteerValue = ML::Rand(Steer[0] , Steer[1]);
|
||||
SteerValue = DebugMode_ML::Rand(Steer[0] , Steer[1]);
|
||||
} else if (Steer.count == 1) {
|
||||
SteerValue = Steer[0];
|
||||
} else {
|
||||
@ -143,7 +143,7 @@ foreach (Event in UIManager.PendingEvents) {
|
||||
if (Speed.count == 1) {
|
||||
SpeedValue = Speed[0];
|
||||
} else {
|
||||
SpeedValue = ML::Rand(Speed[0], Speed[1]);
|
||||
SpeedValue = DebugMode_ML::Rand(Speed[0], Speed[1]);
|
||||
}
|
||||
SetPlayerVehicle_TargetSpeedValue(Target, SpeedValue);
|
||||
|
||||
@ -219,13 +219,13 @@ foreach (Event in UIManager.PendingEvents) {
|
||||
|
||||
***DebugMode_FindPlayer***
|
||||
***
|
||||
if (TL::Length(Event.CustomEventData[0]) == 36) {
|
||||
if (DebugMode_TL::Length(Event.CustomEventData[0]) == 36) {
|
||||
Player <=> ModeUtils::GetPlayerFromAccountId(Event.CustomEventData[0]);
|
||||
} else if (TL::Length(Event.CustomEventData[0]) == 22) {
|
||||
} else if (DebugMode_TL::Length(Event.CustomEventData[0]) == 22) {
|
||||
Player <=> GetPlayer(Event.CustomEventData[0]);
|
||||
} else {
|
||||
foreach (TmpPlayer in AllPlayers) {
|
||||
if (TL::ToLowerCase(Event.CustomEventData[0]) == TL::ToLowerCase(TmpPlayer.User.Name)) {
|
||||
if (DebugMode_TL::ToLowerCase(Event.CustomEventData[0]) == DebugMode_TL::ToLowerCase(TmpPlayer.User.Name)) {
|
||||
Player <=> TmpPlayer;
|
||||
break;
|
||||
}
|
||||
@ -254,7 +254,7 @@ foreach (Event in RacePendingEvents) {
|
||||
+++DebugMode_LogFormat+++
|
||||
Net_DebugMode_Logs = Prefix ^ "CSmMode PendingEvent: " ^ Event.Type ^"\n" ^ Net_DebugMode_Logs;
|
||||
if (Event.Type == CSmModeEvent::EType::OnPlayerAdded) {
|
||||
if (Event.Player != Null && TL::Split(",", S_DebugLib_RestrictUIto).exists(Event.Player.User.Login)) {
|
||||
if (Event.Player != Null && DebugMode_TL::Split(",", S_DebugLib_RestrictUIto).exists(Event.Player.User.Login)) {
|
||||
Layers::Attach(C_DebugMode_MainUI, Event.Player);
|
||||
}
|
||||
}
|
||||
@ -287,7 +287,7 @@ Net_DebugMode_Logs = Prefix ^ "Race Pending Event: " ^ Type ^"\n" ^ Net_DebugMod
|
||||
|
||||
***DebugMode_LogFormat***
|
||||
***
|
||||
declare Integer CurrentTimeStamp = TL::ToInteger(TiL::GetCurrent());
|
||||
declare Integer CurrentTimeStamp = DebugMode_TL::ToInteger(DebugMode_TiL::GetCurrent());
|
||||
|
||||
declare Integer Hours = (CurrentTimeStamp / 3600) % 24;
|
||||
declare Integer Minutes = (CurrentTimeStamp / 60) % 60;
|
||||
@ -303,8 +303,8 @@ else Prefix ^= Seconds;
|
||||
Prefix ^= "] ";
|
||||
|
||||
declare netwrite Text Net_DebugMode_Logs for Teams[0];
|
||||
if (TL::Length(Net_DebugMode_Logs) > 1000000) {
|
||||
Net_DebugMode_Logs = TL::SubString(Net_DebugMode_Logs, 0, 1000000);
|
||||
if (DebugMode_TL::Length(Net_DebugMode_Logs) > 1000000) {
|
||||
Net_DebugMode_Logs = DebugMode_TL::SubString(Net_DebugMode_Logs, 0, 1000000);
|
||||
}
|
||||
|
||||
declare netwrite Integer Net_DebugMode_Logs_Serial for Teams[0];
|
||||
@ -548,6 +548,10 @@ Text GetManialink() {
|
||||
#Include "MathLib" as ML
|
||||
// #RequireContext CSmMlScriptIngame
|
||||
|
||||
#Const ScriptName {{{dump(C_DebugMode_MainUI)}}}
|
||||
#Const Version {{{dump(C_DebugMode_Version)}}}
|
||||
|
||||
|
||||
Real ComputeBackgroundSize(CMlLabel _Label, Text _Value, Real _MaxLineWidth) {
|
||||
declare Real SpaceSize = _Label.ComputeWidth(" ");
|
||||
|
||||
|
624
TM_EnduroCup.Script.txt
Normal file
624
TM_EnduroCup.Script.txt
Normal file
@ -0,0 +1,624 @@
|
||||
/**
|
||||
* EnduroCup mode
|
||||
*/
|
||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
||||
|
||||
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||
#Const Version "2024-12-23"
|
||||
#Const ScriptName "Modes/TM2020-Gamemodes/TM_EnduroCup.Script.txt"
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// 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
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Settings
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Setting S_CheckpointsWithoutElimination -1 as "Number of checkpoint without elimination. Negative value = Nb of Laps"
|
||||
#Setting S_TimeToReachCheckpoint 30000 as "Default time to reach checkpoint in ms"
|
||||
#Setting S_TimeReductionEveryCP 500 as "Reduction of time in ms applied to each checkpoint"
|
||||
|
||||
#Setting S_PointsLimit 0 as _("Points limit")
|
||||
#Setting S_RoundsPerMap 4 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 False 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")
|
||||
|
||||
// Default settings that sould be ignored
|
||||
#Setting S_InfiniteLaps True
|
||||
#Setting S_ForceLapsNb 0
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Constants
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Const C_ModeName "EnduroCup"
|
||||
//L16N [Rounds] Description of the mode rules
|
||||
#Const Description "" // TODO CHANGE
|
||||
|
||||
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/Rounds.Script.txt" //< Url of the mania app
|
||||
#Const C_FakeUsersNb 0
|
||||
|
||||
// Server can allow up to 500ms of latency from the clients, so the double should be enough
|
||||
#Const C_LatencyCompensation 1000
|
||||
#Const C_CleanCheckDelay 1000
|
||||
#Const C_ScriptName_Timer "EnduroCup_Timer"
|
||||
|
||||
#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
|
||||
|
||||
#Struct K_CheckpointState {
|
||||
Integer RaceTime;
|
||||
Integer TimeToReach;
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// 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 = True;
|
||||
***
|
||||
|
||||
***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;
|
||||
|
||||
// Unload default UI
|
||||
UIModules::UnloadModules(["UIModule_Rounds_SmallScoresTable"]);
|
||||
|
||||
CreateTimerUI();
|
||||
***
|
||||
|
||||
***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);
|
||||
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
|
||||
);
|
||||
|
||||
declare netwrite K_CheckpointState[Integer] Net_EnduroCup_Checkpoints for Teams[0] = [];
|
||||
Net_EnduroCup_Checkpoints = [];
|
||||
***
|
||||
|
||||
***Match_InitMap***
|
||||
***
|
||||
declare Integer Map_ValidRoundsNb;
|
||||
declare Boolean Map_Skipped;
|
||||
|
||||
declare Integer Map_PointsLimit = S_PointsLimit;
|
||||
declare Integer Map_RoundsPerMap = S_RoundsPerMap;
|
||||
declare Integer Map_MapsPerMatch = S_MapsPerMatch;
|
||||
|
||||
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb);
|
||||
***
|
||||
|
||||
***Match_StartMap***
|
||||
***
|
||||
// Set inifinite laps (ignoring server settings)
|
||||
Race::SetLapsSettings(True, 0);
|
||||
|
||||
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 Integer Round_CheckpointsWithoutElimination;
|
||||
declare netwrite K_CheckpointState[Integer] Net_EnduroCup_Checkpoints for Teams[0] = [];
|
||||
|
||||
declare Integer Last_CheckAlivePlayers = 0;
|
||||
|
||||
while (Players.count < 2 && MB_MapIsRunning()) {
|
||||
MB_Yield();
|
||||
}
|
||||
***
|
||||
|
||||
***Match_StartRound***
|
||||
***
|
||||
if (S_CheckpointsWithoutElimination < 0) {
|
||||
Round_CheckpointsWithoutElimination = (Map::GetCheckpointsCount() + 1) * ML::Abs(Round_CheckpointsWithoutElimination);
|
||||
} else {
|
||||
Round_CheckpointsWithoutElimination = S_CheckpointsWithoutElimination;
|
||||
}
|
||||
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb);
|
||||
StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]);
|
||||
***
|
||||
|
||||
***Rounds_PlayerSpawned***
|
||||
***
|
||||
CarRank::ThrottleUpdate(CarRank::C_SortCriteria_CurrentRace);
|
||||
***
|
||||
|
||||
***Rounds_AfterSpawningPlayers***
|
||||
***
|
||||
// Prevent late players to spawn
|
||||
foreach (Score in Scores) {
|
||||
Rounds_SetCanSpawn(Score, False);
|
||||
}
|
||||
***
|
||||
|
||||
***Match_PlayLoop***
|
||||
***
|
||||
// Manage race events
|
||||
foreach (Event in Race::GetPendingEvents()) {
|
||||
Race::ValidEvent(Event);
|
||||
|
||||
if (Event.Player == Null) continue;
|
||||
|
||||
// Waypoint
|
||||
if (Event.Type == Events::C_Type_Waypoint) {
|
||||
CarRank::ThrottleUpdate(CarRank::C_SortCriteria_CurrentRace);
|
||||
|
||||
if (Event.IsEndLap) {
|
||||
Scores::UpdatePlayerBestLapIfBetter(Event.Player);
|
||||
}
|
||||
|
||||
// Update the PrevRace at each checkpoint to be able to sort scores even if someone disconnect or something
|
||||
Scores::UpdatePlayerPrevRace(Event.Player);
|
||||
|
||||
declare Integer NumberOfCP = Event.Player.RaceWaypointTimes.count;
|
||||
|
||||
if (Round_CheckpointsWithoutElimination < NumberOfCP) {
|
||||
// Player is first
|
||||
if (!Net_EnduroCup_Checkpoints.existskey(NumberOfCP) || Net_EnduroCup_Checkpoints[NumberOfCP].RaceTime > Event.RaceTime) {
|
||||
Net_EnduroCup_Checkpoints[NumberOfCP] = K_CheckpointState {
|
||||
RaceTime = Event.RaceTime,
|
||||
TimeToReach = S_TimeToReachCheckpoint - S_TimeReductionEveryCP * (NumberOfCP - Round_CheckpointsWithoutElimination)
|
||||
};
|
||||
|
||||
if (Last_CheckAlivePlayers == 0) Last_CheckAlivePlayers = Now + C_CleanCheckDelay;
|
||||
} else if (Net_EnduroCup_Checkpoints[NumberOfCP].RaceTime + Net_EnduroCup_Checkpoints[NumberOfCP].TimeToReach < Event.RaceTime) {
|
||||
EliminatePlayer(Event.Player);
|
||||
}
|
||||
}
|
||||
} else if (Event.Type == Events::C_Type_GiveUp) {
|
||||
EliminatePlayer(Event.Player);
|
||||
}
|
||||
}
|
||||
|
||||
// Manage mode events
|
||||
foreach (Event in PendingEvents) {
|
||||
if (Event.HasBeenPassed || Event.HasBeenDiscarded) continue;
|
||||
Events::Invalid(Event);
|
||||
}
|
||||
|
||||
// Check if players need to be unspawned
|
||||
if (Last_CheckAlivePlayers > 0 && Last_CheckAlivePlayers < Now) {
|
||||
Last_CheckAlivePlayers = 0;
|
||||
|
||||
foreach (Player in Players) {
|
||||
if (Player.SpawnStatus != CSmPlayer::ESpawnStatus::Spawned) continue;
|
||||
|
||||
// Use get because it's more optimized
|
||||
declare K_CheckpointState NextCheckpointState = Net_EnduroCup_Checkpoints.get(Player.RaceWaypointTimes.count + 1, K_CheckpointState{});
|
||||
if (NextCheckpointState.RaceTime == 0) continue;
|
||||
|
||||
if (NextCheckpointState.RaceTime + NextCheckpointState.TimeToReach + C_LatencyCompensation < Player.CurrentRaceTime) {
|
||||
EliminatePlayer(Player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Server info change
|
||||
if (
|
||||
Map_PointsLimit != S_PointsLimit ||
|
||||
Map_RoundsPerMap != S_RoundsPerMap ||
|
||||
Map_MapsPerMatch != S_MapsPerMatch
|
||||
) {
|
||||
Map_PointsLimit = S_PointsLimit;
|
||||
Map_RoundsPerMap = S_RoundsPerMap;
|
||||
Map_MapsPerMatch = S_MapsPerMatch;
|
||||
|
||||
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb);
|
||||
}
|
||||
***
|
||||
|
||||
***Rounds_CheckStopRound***
|
||||
***
|
||||
// End the round
|
||||
// If All players finished
|
||||
if (Players.count > 0 && PlayersNbAlive <= 1) {
|
||||
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();
|
||||
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
||||
CarRank::Update(CarRank::C_SortCriteria_CurrentRace);
|
||||
|
||||
Net_EnduroCup_Checkpoints = [];
|
||||
|
||||
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
|
||||
ComputePoints();
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
||||
UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound;
|
||||
MB_Sleep(5000);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
***
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Functions
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
/** 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]));
|
||||
}
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
/// Compute round points of the prev race
|
||||
Void ComputePoints() {
|
||||
declare Ident[][Integer][Integer] Ranking = [];
|
||||
|
||||
foreach (Score in Scores) {
|
||||
declare Integer NumberOfCP = Score.PrevRaceTimes.count;
|
||||
|
||||
declare Integer LastTime = 0;
|
||||
if (NumberOfCP > 0) LastTime = Score.PrevRaceTimes[-1];
|
||||
|
||||
if (!Ranking.existskey(NumberOfCP)) Ranking[NumberOfCP] = [];
|
||||
if (!Ranking[NumberOfCP].existskey(LastTime)) Ranking[NumberOfCP][LastTime] = [];
|
||||
Ranking[NumberOfCP][LastTime].add(Score.Id);
|
||||
}
|
||||
|
||||
declare Integer[] PointsRepartition = PointsRepartition::GetPointsRepartition();
|
||||
declare Integer Index = 0;
|
||||
|
||||
foreach (Times in Ranking.sortkeyreverse()) {
|
||||
foreach (ScoresId in Times.sortkey()) {
|
||||
foreach (ScoreId in ScoresId) {
|
||||
if (!Scores.existskey(ScoreId)) continue;
|
||||
|
||||
declare CSmScore Score <=> Scores[ScoreId];
|
||||
|
||||
declare Integer Points = 0;
|
||||
if (PointsRepartition.count > 0) {
|
||||
if (PointsRepartition.existskey(Index)) {
|
||||
Points = PointsRepartition[Index];
|
||||
} else {
|
||||
Points = PointsRepartition[-1];
|
||||
}
|
||||
}
|
||||
Scores::SetPlayerRoundPoints(Score, Points);
|
||||
Index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
/// Compute the map scores
|
||||
Void ComputeScores() {
|
||||
Scores::EndRound();
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
/** Eliminate Player
|
||||
*
|
||||
* @param _Player Player to eliminate
|
||||
*/
|
||||
Void EliminatePlayer(CSmPlayer _Player) {
|
||||
Race::StopSkipOutro(_Player);
|
||||
|
||||
if (_Player.User != Null) {
|
||||
UIManager.UIAll.SendChat("$f00" ^ _Player.User.Name ^" elimitated");
|
||||
}
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
/** 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;
|
||||
}
|
||||
|
||||
Void CreateTimerUI() {
|
||||
declare Text Manialink = """
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<manialink version="3" name="{{{C_ScriptName_Timer}}}">
|
||||
<frame id="frame-global" pos="0 30" hidden="1">
|
||||
<label id="label-timer" halign="center" valign="center" textprefix="$s" textsize="5" textfont="RajdhaniMono" textcolor="ffffff" hidden="1"/>
|
||||
</frame>
|
||||
<script><!--
|
||||
#Include "TextLib" as TL
|
||||
|
||||
#Const ScriptName {{{dump(C_ScriptName_Timer)}}}
|
||||
#Const Version {{{dump(Version)}}}
|
||||
|
||||
{{{dumptype(K_CheckpointState)}}}
|
||||
|
||||
main() {
|
||||
log("Init "^ ScriptName ^ " v"^ Version);
|
||||
declare CMlFrame Frame_Global <=> (Page.GetFirstChild("frame-global") as CMlFrame);
|
||||
declare CMlLabel Label_Timer <=> (Frame_Global.GetFirstChild("label-timer") as CMlLabel);
|
||||
|
||||
// Wait C++ initialize the player
|
||||
wait (InputPlayer != Null);
|
||||
|
||||
declare netread K_CheckpointState[Integer] Net_EnduroCup_Checkpoints for Teams[0] = [];
|
||||
|
||||
while(True) {
|
||||
yield;
|
||||
|
||||
if (!PageIsVisible) continue;
|
||||
|
||||
Frame_Global.Visible = (GUIPlayer != Null && GUIPlayer.SpawnStatus == CSmPlayer::ESpawnStatus::Spawned);
|
||||
|
||||
if (!Frame_Global.Visible) continue;
|
||||
|
||||
declare K_CheckpointState CheckpointState = Net_EnduroCup_Checkpoints.get(GUIPlayer.RaceWaypointTimes.count + 1, K_CheckpointState {});
|
||||
|
||||
Label_Timer.Visible = (CheckpointState.RaceTime > 0 && CheckpointState.RaceTime < GUIPlayer.CurrentRaceTime);
|
||||
|
||||
if (!Label_Timer.Visible) continue;
|
||||
|
||||
declare Integer TimeLeft = CheckpointState.RaceTime + CheckpointState.TimeToReach - GUIPlayer.CurrentRaceTime;
|
||||
|
||||
if (TimeLeft <= 0) {
|
||||
Label_Timer.Value = "$f0000:00.000";
|
||||
} else {
|
||||
Label_Timer.Value = TL::TimeToText(TimeLeft, True, True);
|
||||
}
|
||||
}
|
||||
}
|
||||
--></script>
|
||||
</manialink>
|
||||
""";
|
||||
|
||||
|
||||
Layers::Create(C_ScriptName_Timer, Manialink);
|
||||
Layers::SetType(C_ScriptName_Timer, CUILayer::EUILayerType::Normal);
|
||||
Layers::Attach(C_ScriptName_Timer);
|
||||
}
|
@ -2,10 +2,10 @@
|
||||
/*
|
||||
* This mode uses the TMGL World Cup mode and removes all references to it
|
||||
*/
|
||||
#Extends "Modes/TrackMania/TM_Final86TMGL_Online.Script.txt"
|
||||
#Extends "Modes/TrackMania/Deprecated/TM_Final86TMGL_Online.Script.txt"
|
||||
// #RequireContext CSmMode
|
||||
|
||||
#Include "ManiaApps/Nadeo/TMNext/TrackMania/UIModules/TMGLMarkers_Server.Script.txt" as UIModules_TMGLMarkers
|
||||
#Include "Libs/Nadeo/Trackmania/Modes/UIModules/TMGLMarkers_Server.Script.txt" as UIModules_TMGLMarkers
|
||||
|
||||
#Setting S_PointsLimit 100
|
||||
#Setting S_RoundsPerMap 3
|
||||
|
@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Official Knockout mode with delayed countdown (Finish Timeout)
|
||||
*/
|
||||
#Extends "Libs/Nadeo/TMNext/TrackMania/Modes/TMNextRoundsBase.Script.txt"
|
||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
||||
|
||||
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||
#Const Version "2023-06-21"
|
||||
#Const Version "2023-10-16"
|
||||
#Const ScriptName "Modes/TrackMania/TM_Knockout_Online.Script.txt"
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
@ -12,21 +12,24 @@
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Include "TextLib" as TL
|
||||
#Include "MathLib" as ML
|
||||
#Include "Libs/Nadeo/CommonLibs/Common/Semver.Script.txt" as Semver
|
||||
#Include "Libs/Nadeo/TMNext/TrackMania/Modes/Knockout/StateManager.Script.txt" as StateMgr
|
||||
#Include "Libs/Nadeo/TMNext/TrackMania/Menu/Constants.Script.txt" as MenuConsts
|
||||
#Include "ManiaApps/Nadeo/TMNext/TrackMania/Knockout/UIModules/KnockoutInfo_Server.Script.txt" as UIModules_KnockoutInfo
|
||||
#Include "ManiaApps/Nadeo/TMNext/TrackMania/TimeAttack/UIModules/BestRaceViewer_Server.Script.txt" as UIModules_BestRaceViewer
|
||||
#Include "ManiaApps/Nadeo/TMNext/TrackMania/Knockout/UIModules/KnockedOutPlayers_Server.Script.txt" as UIModules_KnockedOutPlayers
|
||||
#Include "ManiaApps/Nadeo/TMNext/TrackMania/Knockout/UIModules/KnockoutReward_Server.Script.txt" as UIModules_KnockoutReward
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/PauseMenuOnline_Server.Script.txt" as UIModules_PauseMenu_Online
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/Checkpoint_Server.Script.txt" as UIModules_Checkpoint
|
||||
#Include "Libs/Nadeo/CMGame/Utils/Semver.Script.txt" as Semver
|
||||
#Include "Libs/Nadeo/CMGame/Modes/Utils.Script.txt" as ModeUtils
|
||||
#Include "Libs/Nadeo/Trackmania/Modes/Knockout/StateManager.Script.txt" as StateMgr
|
||||
#Include "Libs/Nadeo/Trackmania/MainMenu/Constants.Script.txt" as MenuConsts
|
||||
#Include "Libs/Nadeo/Trackmania/Modes/Knockout/UIModules/KnockoutInfo_Server.Script.txt" as UIModules_KnockoutInfo
|
||||
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/ScoresTable_Server.Script.txt" as UIModules_ScoresTable
|
||||
#Include "Libs/Nadeo/Trackmania/Modes/TimeAttack/UIModules/BestRaceViewer_Server.Script.txt" as UIModules_BestRaceViewer
|
||||
#Include "Libs/Nadeo/Trackmania/Modes/Knockout/UIModules/KnockedOutPlayers_Server.Script.txt" as UIModules_KnockedOutPlayers
|
||||
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
#Include "Libs/Nadeo/Trackmania/Modes/Knockout/UIModules/KnockoutReward_Server.Script.txt" as UIModules_KnockoutReward
|
||||
#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
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Settings
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Setting S_FinishTimeout 5 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_RoundsPerMap -1 as _("Number of rounds per map") ///< Number of round to play on one map before going to the next one
|
||||
#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")
|
||||
@ -66,7 +69,7 @@
|
||||
#Const Description _("$zIn $<$t$6F9Knockout$> mode, the goal is to be the last player standing. \n\nYou play a series of races as in Round mode. $<$t$6F9At the end of each race, the last players are eliminated$>!\n\nThe winner is the player who eliminates all of their opponents.")
|
||||
|
||||
#Const C_HudModulePath "" //< Path to the hud module
|
||||
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/TMNext/TrackMania/Knockout/Knockout.Script.txt" //< Url of the mania app
|
||||
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/Knockout.Script.txt" //< Url of the mania app
|
||||
#Const C_FakeUsersNb 0
|
||||
|
||||
#Const C_Callback_Elimination "Trackmania.Knockout.Elimination"
|
||||
@ -509,6 +512,7 @@ if (Round_ForceEndRound || Round_SkipPauseRound) {
|
||||
if (!Round_SkipPauseRound) {
|
||||
ForcedEndRoundSequence();
|
||||
}
|
||||
MB_SetValidRound(False);
|
||||
} else {
|
||||
// Eliminate players that did not finish in time
|
||||
declare Ident[] EliminatedPlayersScoresIds = []; // Score.Id
|
||||
|
@ -3,10 +3,10 @@
|
||||
*/
|
||||
// #RequireContext CSmMode
|
||||
|
||||
#Extends "Libs/Nadeo/TMNext/TrackMania/Modes/TMNextBase.Script.txt"
|
||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaBase.Script.txt"
|
||||
|
||||
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||
#Const Version "2022-10-22"
|
||||
#Const Version "2023-09-25"
|
||||
#Const ScriptName "Modes/TM2020-Gamemodes/TM_LapsKnockout.Script.txt"
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
@ -14,15 +14,15 @@
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Include "TextLib" as TL
|
||||
#Include "MathLib" as ML
|
||||
#Include "Libs/Nadeo/TMNext/TrackMania/Menu/Constants.Script.txt" as MenuConsts
|
||||
#Include "Libs/Nadeo/TMNext/TrackMania/Modes/Laps/StateManager.Script.txt" as StateMgr
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/ScoresTable_Server.Script.txt" as UIModules_ScoresTable
|
||||
#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/LapsCommon/Libs/Constants.Script.txt" as LibLaps_Constants
|
||||
#Include "Libs/Nadeo/Trackmania/MainMenu/Constants.Script.txt" as MenuConsts
|
||||
#Include "Libs/Nadeo/Trackmania/Modes/Laps/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/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
|
||||
#Include "Libs/Nadeo/Trackmania/Modes/LapsCommon/Libs/Constants.Script.txt" as LibLaps_Constants
|
||||
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Settings
|
||||
@ -69,7 +69,7 @@
|
||||
#Const Description _("$zIn $<$t$6F9Laps$> mode, the goal is to drive as far as possible by passing $<$t$6F9checkpoints$>.\n\nThe laps mode takes place on multilap (cyclical) maps, and is played in one go for every map.\n\nWhen the time is up, the $<$t$6F9winner$> is the player who passed the most $<$t$6F9checkpoints$>. In case of draws, the winner is the player who passed the last checkpoint first.")
|
||||
|
||||
#Const C_HudModulePath "" //< Path to the hud module
|
||||
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/TMNext/TrackMania/Laps/Laps.Script.txt" //< Url of the mania app
|
||||
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/Laps.Script.txt" //< Url of the mania app
|
||||
#Const C_FakeUsersNb 0
|
||||
|
||||
#Const C_UploadRecord True
|
||||
@ -258,8 +258,7 @@ if (Last_NbLapsWithoutKO == 1) {
|
||||
***Match_PlayLoop***
|
||||
***
|
||||
// Manage race events
|
||||
declare RacePendingEvents = Race::GetPendingEvents();
|
||||
foreach (Event in RacePendingEvents) {
|
||||
foreach (Event in Race::GetPendingEvents()) {
|
||||
Race::ValidEvent(Event);
|
||||
|
||||
// Waypoint
|
||||
@ -293,8 +292,8 @@ foreach (Event in RacePendingEvents) {
|
||||
|
||||
if (Event.IsEndRace) {
|
||||
// waiting GetCustomPoints function in ScoresTable_Server
|
||||
declare netwrite Text[][Text] Net_TMxSM_ScoresTable_CustomPoints for Teams[0];
|
||||
declare Text[][Text] CustomTimes = Net_TMxSM_ScoresTable_CustomPoints;
|
||||
declare netwrite Text[][Text] Net_TMGame_ScoresTable_CustomPoints for Teams[0];
|
||||
declare Text[][Text] CustomTimes = Net_TMGame_ScoresTable_CustomPoints;
|
||||
foreach (Player in Players) {
|
||||
if (Player != Event.Player) {
|
||||
CustomTimes[Player.User.WebServicesUserId] = [_("|Status|K.O."), "f00"];
|
||||
|
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);
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
|
||||
#Extends "Modes/TrackMania/TM_Rounds_Online.Script.txt"
|
||||
|
||||
#Include "Libs/Nadeo/TMNext/TrackMania/Menu/Constants.Script.txt" as MenuConsts
|
||||
#Include "Libs/Nadeo/Trackmania/MainMenu/Constants.Script.txt" as MenuConsts
|
||||
|
||||
#Include "Modes/TM2020-Gamemodes/Libs/Malus.Script.txt" as Malus
|
||||
|
||||
@ -99,8 +99,8 @@ Void AddMalusUI() {
|
||||
declare CMlFrame Frame_Global <=> (Page.GetFirstChild("frame-global") as CMlFrame);
|
||||
declare CMlLabel Label_Message <=> (Frame_Global.GetFirstChild("label-message") as CMlLabel);
|
||||
|
||||
G_Sound_CountDown = Audio.CreateSound("file://Media/Manialinks/Nadeo/TMConsole/MapEditor/Sounds/MenuDialogHidden.wav", 1.0, False, False, False);
|
||||
G_Sound_Malus = Audio.CreateSound("file://Media/Manialinks/Nadeo/TMConsole/MapEditor/Sounds/MenuDialogDisplayed.wav", 1.0, False, False, False);
|
||||
G_Sound_CountDown = Audio.CreateSound("https://files.virtit.fr/TrackMania/UI/MenuDialogHidden-06471.wav", 1.0, False, False, False);
|
||||
G_Sound_Malus = Audio.CreateSound("https://files.virtit.fr/TrackMania/UI/MenuDialogDisplayed-21252.wav", 1.0, False, False, False);
|
||||
|
||||
// Wait C++ initialize the player
|
||||
wait (InputPlayer != Null);
|
||||
|
1660
TM_MultiLivesKnockout.Script.txt
Normal file
1660
TM_MultiLivesKnockout.Script.txt
Normal file
File diff suppressed because it is too large
Load Diff
@ -5,10 +5,10 @@
|
||||
*/
|
||||
|
||||
// #RequireContext CSmMode
|
||||
#Extends "Libs/Nadeo/TMNext/TrackMania/Modes/TMNextRoundsBase.Script.txt"
|
||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
||||
|
||||
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||
#Const Version "2023-08-08"
|
||||
#Const Version "2023-10-16"
|
||||
#Const ScriptName "Modes/TM2020-Gamemodes/TM_ReverseCup.Script.txt"
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
@ -16,14 +16,14 @@
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Include "TextLib" as TL
|
||||
#Include "MathLib" as ML
|
||||
#Include "Libs/Nadeo/CommonLibs/Common/Semver.Script.txt" as Semver
|
||||
#Include "Libs/Nadeo/TMNext/TrackMania/Menu/Constants.Script.txt" as Menu_Const
|
||||
#Include "Libs/Nadeo/TMNext/TrackMania/Modes/CupCommon/Constants.Script.txt" as CupCommon_Const
|
||||
#Include "Libs/Nadeo/TMNext/TrackMania/Modes/Cup/StateManager.Script.txt" as StateMgr
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/ScoresTable_Server.Script.txt" as UIModules_ScoresTable
|
||||
#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/TMxSM/Race/UIModules/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
#Include "Libs/Nadeo/CMGame/Utils/Semver.Script.txt" as Semver
|
||||
#Include "Libs/Nadeo/Trackmania/MainMenu/Constants.Script.txt" as Menu_Const
|
||||
#Include "Libs/Nadeo/Trackmania/Modes/CupCommon/Constants.Script.txt" as CupCommon_Const
|
||||
#Include "Libs/Nadeo/Trackmania/Modes/Cup/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/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/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Settings
|
||||
@ -55,7 +55,7 @@
|
||||
#Const Description _("$zThe cup mode consists of $<$t$6F9a series of races on multiple maps$>.\n\nWhen you finish a race in a bad $<$t$6F9position$>, you loose $<$t$6F9points$> substracted from your total.\nServers might propose warmup races to get familiar with a map first.\n\nTo win, you must be the last player with points. Once you are a LastChance, if you finish a race last you will be eliminated.The cup mode ends once there is one player left.")
|
||||
|
||||
#Const C_HudModulePath "" //< Path to the hud module
|
||||
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/TMNext/TrackMania/Cup/Cup.Script.txt" //< Url of the mania app
|
||||
#Const C_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/Cup.Script.txt" //< Url of the mania app
|
||||
#Const C_FakeUsersNb 0
|
||||
|
||||
#Const C_UploadRecord True
|
||||
@ -136,9 +136,7 @@ UIModules_Checkpoint::SetRankMode(UIModules_Checkpoint::C_RankMode_CurrentRace);
|
||||
UIModules_PauseMenu_Online::SetHelp(Description);
|
||||
|
||||
// Hide SM Overlay
|
||||
UIManager.UIAll.OverlayHideSpectatorControllers = True;
|
||||
UIManager.UIAll.OverlayHideSpectatorInfos = True;
|
||||
UIManager.UIAll.OverlayHideChrono = True;
|
||||
UIManager.UIAll.OverlayHideCountdown = True;
|
||||
|
||||
// Unload default UI
|
||||
@ -160,8 +158,8 @@ foreach (Event in PendingEvents) {
|
||||
if (Server_MatchInfo.RegistrationClosed && !Server_MatchInfo.Participants.exists(Event.Player.User.Login)) {
|
||||
Scores::SetPlayerMatchPoints(Event.Player.Score, C_Points_Spectator);
|
||||
// Equivalent of getCustomPoints:
|
||||
declare netwrite Text[][Text] Net_TMxSM_ScoresTable_CustomPoints for Teams[0] = [];
|
||||
Net_TMxSM_ScoresTable_CustomPoints[Event.Player.User.WebServicesUserId] = [C_Text_Spectator, C_Color_Spectator];
|
||||
declare netwrite Text[][Text] Net_TMGame_ScoresTable_CustomPoints for Teams[0] = [];
|
||||
Net_TMGame_ScoresTable_CustomPoints[Event.Player.User.WebServicesUserId] = [C_Text_Spectator, C_Color_Spectator];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -442,6 +440,9 @@ StateMgr::ForcePlayersStates([CupCommon_Const::C_State_Waiting]);
|
||||
CarRank::Update(CarRank::C_SortCriteria_CurrentRace);
|
||||
UpdateLiveRaceUI();
|
||||
|
||||
// Compute round points before the callback
|
||||
ComputeLatestRaceScores(Round_NbPlayersInThisRound);
|
||||
|
||||
if (Semver::Compare(XmlRpc::GetApiVersion(), ">=", "2.1.1")) {
|
||||
Scores::XmlRpc_SendScores(Scores::C_Section_PreEndRound, "");
|
||||
}
|
||||
@ -455,11 +456,10 @@ if (Round_ForceEndRound || Round_SkipPauseRound || Round_Skipped) {
|
||||
if (!Round_SkipPauseRound) {
|
||||
ForcedEndRoundSequence();
|
||||
}
|
||||
MB_SetValidRound(False);
|
||||
DisplayCustomPoints();
|
||||
MB_Sleep(3000);
|
||||
} else {
|
||||
// Get the last round points
|
||||
ComputeLatestRaceScores(Round_NbPlayersInThisRound);
|
||||
+++Cup_EndRound_BeforeScoresUpdate+++
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
||||
@ -503,26 +503,27 @@ if (MB_MatchIsRunning()) {
|
||||
***
|
||||
ModeUtils::PlaySound(CUIConfig::EUISound::EndRound, 0);
|
||||
|
||||
declare Text[] WinnersNames;
|
||||
foreach (Player in Players) {
|
||||
if(!Spectators.exists(Player)) {
|
||||
if (Scores::GetPlayerMatchPoints(Player.Score) >= C_Points_LastChance) {
|
||||
if (Player.User.ClubTag == "") {
|
||||
WinnersNames.add(Player.User.Name);
|
||||
} else {
|
||||
WinnersNames.add("[$<"^Player.User.ClubTag^"$>] " ^ Player.User.Name);
|
||||
}
|
||||
}
|
||||
if (!MB_Private_SkipPodiumSequence) {
|
||||
declare Text[] WinnersNames;
|
||||
foreach (Player in Players) {
|
||||
if(!Spectators.exists(Player)) {
|
||||
if (Scores::GetPlayerMatchPoints(Player.Score) >= C_Points_LastChance) {
|
||||
if (Player.User.ClubTag == "") {
|
||||
WinnersNames.add(Player.User.Name);
|
||||
} else {
|
||||
WinnersNames.add("[$<"^Player.User.ClubTag^"$>] " ^ Player.User.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(WinnersNames.count >= 1) {
|
||||
UIModules_BigMessage::SetMessage(TL::Compose(_("$<%1$> wins the match!"), TL::Join(", ", WinnersNames)));
|
||||
UIManager.UIAll.SendChat(TL::Compose(_("$<%1$> wins the match!"), TL::Join(", ", WinnersNames)));
|
||||
} else {
|
||||
UIModules_BigMessage::SetMessage(_("|Match|Draw"));
|
||||
}
|
||||
}
|
||||
|
||||
if(WinnersNames.count >= 1) {
|
||||
UIModules_BigMessage::SetMessage(TL::Compose(_("$<%1$> wins the match!"), TL::Join(", ", WinnersNames)));
|
||||
UIManager.UIAll.SendChat(TL::Compose(_("$<%1$> wins the match!"), TL::Join(", ", WinnersNames)));
|
||||
} else {
|
||||
UIModules_BigMessage::SetMessage(_("|Match|Draw"));
|
||||
}
|
||||
|
||||
***
|
||||
|
||||
***Match_PodiumSequence***
|
||||
@ -923,7 +924,7 @@ Void SetManialink_LiveRace() {
|
||||
</framemodel>
|
||||
<frame id="frame-global" pos="-160 50">
|
||||
<frame id="frame-toggle" pos="58 -2.5" >
|
||||
<quad id="Toggle_SettingButton" pos="0 0" size="4 4" class="quad-base" z-index="3" opacity="0.9" scriptevents="1" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds" colorize="fff"/>
|
||||
<quad id="Toggle_SettingButton" pos="0 0" size="4 4" class="quad-base" z-index="3" opacity="0.9" scriptevents="1" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds" colorize="fff"/>
|
||||
</frame>
|
||||
<frame id="frame-UI">
|
||||
<quad id="quad-bg" pos="0 0" z-index="-1" size="60 14" bgcolor="000" opacity="0.5" /><!-- 16 + (ML::Max(NB_Players,16) * 5) -->
|
||||
@ -1029,12 +1030,12 @@ Void SetManialink_LiveRace() {
|
||||
|
||||
declare Real GlobalEndPosX;
|
||||
if (Frame_UI.Visible) {
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_RIGHT_OBLIQUE.dds");
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_RIGHT_OBLIQUE.dds");
|
||||
Quad_Toggle.RelativePosition_V3.X = 5.;
|
||||
GlobalEndPosX = -220.;
|
||||
AnimMgr.Add(Frame_UI, "<frame hidden=\"1\" />", Now, 250, CAnimManager::EAnimManagerEasing::Linear);
|
||||
} else {
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds");
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds");
|
||||
Quad_Toggle.RelativePosition_V3.X = 0.;
|
||||
GlobalEndPosX = -160.;
|
||||
Frame_UI.Visible = True;
|
||||
|
657
TM_RoundsNearest.Script.txt
Normal file
657
TM_RoundsNearest.Script.txt
Normal file
@ -0,0 +1,657 @@
|
||||
/**
|
||||
* Rounds Nearest.
|
||||
* Nearest you are of the S_TargetTime, more points you win
|
||||
*/
|
||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
||||
|
||||
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||
#Const Version "2025-05-08"
|
||||
#Const ScriptName "Modes/TM2020-Gamemodes/TM_RoundsNearest.Script.txt"
|
||||
|
||||
// #RequireContext CSmMode
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// 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/Trackmania/Modes/Rounds/UIModules/SmallScoresTable_Server.Script.txt" as UIModules_SmallScoresTable
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Settings
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Setting S_BonusForPerfect 50
|
||||
#Setting S_FinishTimeout 10 as _("Finish timeout")
|
||||
#Setting S_MapsPerMatch 0 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_PointsLimit 50 as _("Points limit")
|
||||
#Setting S_PointsRepartition "5,4,3,2,1,0"
|
||||
#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_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
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#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_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
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// 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_SmallScoresTable::ScriptName, UIModules_SmallScoresTable::Version);
|
||||
***
|
||||
|
||||
***Match_LoadLibraries***
|
||||
***
|
||||
StateMgr::Load();
|
||||
***
|
||||
|
||||
***Match_UnloadLibraries***
|
||||
***
|
||||
StateMgr::Unload();
|
||||
***
|
||||
|
||||
***Match_Settings***
|
||||
***
|
||||
MB_Settings_UseDefaultHud = (C_HudModulePath == "");
|
||||
***
|
||||
|
||||
***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;
|
||||
|
||||
UIModules_ScoresTable::DisplayRoundPoints(True);
|
||||
UIModules::UnloadModules(["UIModule_Race_TimeGap"]);
|
||||
***
|
||||
|
||||
***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_StartMatch***
|
||||
***
|
||||
UIModules_ScoresTable::SetCustomPoints([]);
|
||||
UIModules_SmallScoresTable::ResetCustomTimes();
|
||||
UIModules_SmallScoresTable::ResetCustomResults();
|
||||
***
|
||||
|
||||
***Match_InitMap***
|
||||
***
|
||||
declare Integer Map_ValidRoundsNb;
|
||||
declare Boolean Map_Skipped;
|
||||
|
||||
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb);
|
||||
***
|
||||
|
||||
***Match_StartMap***
|
||||
***
|
||||
Map_Skipped = True;
|
||||
CarRank::Reset();
|
||||
UIModules_ScoresTable::SetCustomPoints(GetWinnersCustomPoints());
|
||||
|
||||
// Warm up
|
||||
foreach (Score in Scores) {
|
||||
WarmUp::CanPlay(Score, CanSpawn(Score));
|
||||
}
|
||||
|
||||
UIModules_ScoresTable::SetFooterInfo(_("Warm up"));
|
||||
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***
|
||||
***
|
||||
UpdateScoresTableFooter(S_PointsLimit, S_RoundsPerMap, S_MapsPerMatch, Map_ValidRoundsNb);
|
||||
StateMgr::ForcePlayersStates([StateMgr::C_State_Playing]);
|
||||
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***
|
||||
***
|
||||
CarRank::ThrottleUpdate(CarRank::C_SortCriteria_CurrentRace);
|
||||
***
|
||||
|
||||
***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) {
|
||||
CarRank::ThrottleUpdate(CarRank::C_SortCriteria_CurrentRace);
|
||||
if (Event.Player != Null) {
|
||||
if (Event.IsEndRace) {
|
||||
Scores::UpdatePlayerBestRaceIfBetter(Event.Player);
|
||||
Scores::UpdatePlayerBestLapIfBetter(Event.Player);
|
||||
Scores::UpdatePlayerPrevRace(Event.Player);
|
||||
ComputeLatestRaceScores(False);
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
***
|
||||
|
||||
***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;
|
||||
// Get the last round points
|
||||
ComputeLatestRaceScores(True);
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound;
|
||||
MB_Sleep(S_ChatTime * 1000 / 3);
|
||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
||||
MB_Sleep(S_ChatTime * 1000 / 3);
|
||||
// Add them to the total scores
|
||||
ComputeScores();
|
||||
UIModules_ScoresTable::SetCustomPoints(GetWinnersCustomPoints());
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
MB_Sleep(S_ChatTime * 1000 / 3);
|
||||
UIModules_BigMessage::SetMessage("");
|
||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal;
|
||||
UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing;
|
||||
|
||||
// Match is over, we have all the winners
|
||||
if (MatchIsOver(MB_GetMapCount(), Map_Skipped)) {
|
||||
MB_StopMatch();
|
||||
}
|
||||
// Map is over, we played all the rounds
|
||||
else if (MapIsOver(Map_ValidRoundsNb)) {
|
||||
MB_StopMap();
|
||||
}
|
||||
}
|
||||
UIModules_SmallScoresTable::ResetCustomTimes();
|
||||
UIModules_SmallScoresTable::ResetCustomResults();
|
||||
***
|
||||
|
||||
***Match_EndMap***
|
||||
***
|
||||
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);
|
||||
}
|
||||
}
|
||||
***
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Functions
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
/** 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]));
|
||||
}
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
/** 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
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
Text DeltaTimeToText(Integer _Time) {
|
||||
if (_Time < 1000) {
|
||||
return TL::FormatReal(_Time / 1000., 3, False, False);
|
||||
}
|
||||
|
||||
declare Real TimeWithoutMs = _Time / 10.;
|
||||
declare Real TimeInSeconds = TimeWithoutMs / 100.;
|
||||
if (TimeInSeconds <= 10.) {
|
||||
return TL::FormatReal(TimeInSeconds, 2, False, False);
|
||||
} else if (TimeInSeconds <= 100.) {
|
||||
return TL::FormatReal(TimeInSeconds, 1, False, False);
|
||||
}
|
||||
return TL::ToText(ML::FloorInteger(TimeInSeconds));
|
||||
}
|
||||
|
||||
Text FormatPlayerName(Text _Name) {
|
||||
return "$<$fff" ^ _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
|
||||
Void ComputeLatestRaceScores(Boolean _IsEndRound) {
|
||||
Race::SortScores(Race::C_Sort_PrevRaceTime);
|
||||
|
||||
// Points distributed between all players
|
||||
|
||||
declare Text[][Text] CustomPoints = GetWinnersCustomPoints();
|
||||
declare CSmScore[][Integer] ScoresPerAbsoluteDelta;
|
||||
declare Integer[Text] CustomTimes;
|
||||
declare Text[Text] CustomResult;
|
||||
foreach (Score in Scores) {
|
||||
if (Score.User == Null) continue;
|
||||
if (Scores::GetPlayerPrevRaceTime(Score) <= 0) continue;
|
||||
declare Integer Delta = Scores::GetPlayerPrevRaceTime(Score) - S_TargetTime;
|
||||
declare Integer AbsoluteDelta = ML::Abs(Delta);
|
||||
if (!ScoresPerAbsoluteDelta.existskey(AbsoluteDelta)) ScoresPerAbsoluteDelta[AbsoluteDelta] = [];
|
||||
ScoresPerAbsoluteDelta[AbsoluteDelta].add(Score);
|
||||
|
||||
declare Text TextDelta;
|
||||
if (Delta >= 0) TextDelta = "+";
|
||||
else TextDelta = "-";
|
||||
|
||||
TextDelta ^= DeltaTimeToText(AbsoluteDelta);
|
||||
if (S_PointsLimit < 0 || Scores::GetPlayerMatchPoints(Score) < S_PointsLimit) {
|
||||
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);
|
||||
|
||||
ScoresPerAbsoluteDelta = ScoresPerAbsoluteDelta.sortkey();
|
||||
|
||||
declare Integer I = 0;
|
||||
declare Integer[] PointsRepartition = PointsRepartition::GetPointsRepartition();
|
||||
declare Text Names;
|
||||
|
||||
foreach (Delta => CustomScores in ScoresPerAbsoluteDelta) {
|
||||
// Attribute less points if they have the same time
|
||||
if (S_SendSameTimeMessage && _IsEndRound && CustomScores.count > 1) {
|
||||
I += CustomScores.count - 1;
|
||||
foreach (Key => Score in CustomScores) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
UIManager.UIAll.SendChat("$ff3" ^ Names^ " have the same time");
|
||||
}
|
||||
|
||||
Names = "";
|
||||
declare Boolean UpdateBigMessage;
|
||||
|
||||
foreach (Key => Score in CustomScores) {
|
||||
declare Integer Points;
|
||||
|
||||
if (PointsRepartition.existskey(I)) {
|
||||
Points = PointsRepartition[I];
|
||||
} else if (PointsRepartition.count > 0) {
|
||||
Points = PointsRepartition[PointsRepartition.count - 1];
|
||||
}
|
||||
|
||||
if (Delta == 0) {
|
||||
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");
|
||||
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);
|
||||
}
|
||||
|
||||
if (UpdateBigMessage) {
|
||||
UIModules_BigMessage::SetMessage(Names ^ " did the perfect time");
|
||||
}
|
||||
|
||||
I += 1;
|
||||
}
|
||||
|
||||
UIModules_SmallScoresTable::SetCustomTimes(CustomTimes);
|
||||
UIModules_SmallScoresTable::SetCustomResults(CustomResult);
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
/// Compute the map scores
|
||||
Void ComputeScores() {
|
||||
if (S_PointsLimit <= 0) {
|
||||
Scores::EndRound();
|
||||
return;
|
||||
}
|
||||
|
||||
declare Integer NbOfWinners = 0;
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
|
||||
foreach (Score in Scores) {
|
||||
// Already won
|
||||
if (Scores::GetPlayerMatchPoints(Score) >= S_PointsLimit) {
|
||||
Scores::SetPlayerMatchPoints(Score, S_PointsLimit + 1 + S_NbOfWinners - NbOfWinners);
|
||||
NbOfWinners += 1;
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
|
||||
Scores::AddPlayerMapPoints(Score, Scores::GetPlayerRoundPoints(Score));
|
||||
Scores::SetPlayerRoundPoints(Score, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
/** Check if we should go to the next map
|
||||
*
|
||||
* @return True if it is the case, false otherwise
|
||||
*/
|
||||
Boolean MapIsOver(Integer _ValidRoundsNb) {
|
||||
if (_ValidRoundsNb >= S_RoundsPerMap) return True;
|
||||
return False;
|
||||
}
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
/** Check if we should go to the next match
|
||||
*
|
||||
* @param _MapsPerMatch Number of maps to play to complete a match
|
||||
* @param _MapSkipped if map was skipped
|
||||
*
|
||||
* @return True if it is the case, false otherwise
|
||||
*/
|
||||
Boolean MatchIsOver(Integer _MapCount, Boolean _MapSkipped) {
|
||||
Log::Log("[Cup] MatchIsOver() check | S_PointsLimit : "^S_PointsLimit);
|
||||
if (S_PointsLimit > 0) {
|
||||
declare Integer NbOfScoreWinners = 0;
|
||||
foreach (Score in Scores) {
|
||||
if (Scores::GetPlayerMatchPoints(Score) > S_PointsLimit) NbOfScoreWinners += 1;
|
||||
}
|
||||
declare Integer NbOfPlayerWinners = 0;
|
||||
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 (
|
||||
_MapCount >= S_MapsPerMatch || //< ... stop the match if the maps limit is reached and the match is not a tie
|
||||
(_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;
|
||||
}
|
||||
}
|
||||
// If there is a rounds limit but no maps limit, continue to play until another limit is reached
|
||||
else if (S_RoundsPerMap >= 1) {
|
||||
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;
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Royal Rounds mode
|
||||
*/
|
||||
#Extends "Libs/Nadeo/TMNext/TrackMania/Modes/TMNextRoundsBase.Script.txt"
|
||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
||||
|
||||
#Const CompatibleMapTypes "TrackMania\\TM_Royal,TM_Royal"
|
||||
#Const Version "2021-08-21"
|
||||
#Const Version "2024-04-05"
|
||||
#Const ScriptName "Modes/TrackMania/TM_RoyalRounds_Online.Script.txt"
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
@ -12,17 +12,17 @@
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#Include "TextLib" as TL
|
||||
#Include "MathLib" as ML
|
||||
#Include "Libs/Nadeo/CommonLibs/Common/Semver.Script.txt" as Semver
|
||||
#Include "Libs/Nadeo/CMGame/Utils/Semver.Script.txt" as Semver
|
||||
|
||||
#Include "Libs/Nadeo/ModeLibs/Common/Utils.Script.txt" as ModeUtils
|
||||
#Include "Libs/Nadeo/TMNext/TrackMania/Menu/Constants.Script.txt" as MenuConsts
|
||||
#Include "Libs/Nadeo/TMNext/TrackMania/Modes/Rounds/StateManager.Script.txt" as StateMgr
|
||||
#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 "ManiaApps/Nadeo/TMxSM/Race/UIModules/Checkpoint_Server.Script.txt" as UIModules_Checkpoint
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/Chrono_Server.Script.txt" as UIModules_Chrono
|
||||
#Include "ManiaApps/Nadeo/TMNext/TrackMania/Rounds/UIModules/SmallScoresTable_Server.Script.txt" as UIModules_SmallScoresTable
|
||||
#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
|
||||
@ -51,7 +51,7 @@
|
||||
#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/TMNext/TrackMania/Rounds/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
|
||||
|
||||
@ -119,9 +119,7 @@ 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.OverlayHideSpectatorControllers = True;
|
||||
UIManager.UIAll.OverlayHideSpectatorInfos = True;
|
||||
UIManager.UIAll.OverlayHideChrono = True;
|
||||
UIManager.UIAll.OverlayHideCountdown = True;
|
||||
|
||||
SetML();
|
||||
@ -325,7 +323,7 @@ UIModules_Chrono::SetTimeOffset(Player, SpecificOffset);
|
||||
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
|
||||
CurrentRanking[0] = []; // Init white section
|
||||
declare Boolean IsStartRound = True;
|
||||
|
||||
// Reset players for the race
|
||||
@ -351,8 +349,7 @@ IsStartRound = False;
|
||||
***Match_PlayLoop***
|
||||
***
|
||||
// Manage race events
|
||||
declare RacePendingEvents = Race::GetPendingEvents();
|
||||
foreach (Event in RacePendingEvents) {
|
||||
foreach (Event in Race::GetPendingEvents()) {
|
||||
if (Event.Type == Events::C_Type_SkipOutro && C_DisableSkipOutro) {
|
||||
Race::InvalidEvent(Event);
|
||||
} else {
|
||||
@ -365,7 +362,7 @@ foreach (Event in RacePendingEvents) {
|
||||
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];
|
||||
if (!CurrentRanking.existskey(CurrentSegment)) CurrentRanking[CurrentSegment] = [];
|
||||
CurrentRanking[CurrentSegment][Event.Player.User.WebServicesUserId] = Event.Player.StartTime - StartTime + Event.RaceTime;
|
||||
|
||||
// Update Ranking of the current players
|
||||
@ -386,8 +383,8 @@ foreach (Event in RacePendingEvents) {
|
||||
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;
|
||||
if (CurrentSegment < S_SegmentsPerRound) { // TODO Try to keep CP diff a the bottom of the screen
|
||||
declare Boolean ModeRounds_CanSpawn for Event.Player.Score = Rounds_Settings_CanSpawnDefault;
|
||||
ModeRounds_CanSpawn = True;
|
||||
CurrentSegment = CurrentSegment + 1;
|
||||
Race::StopSkipScoresTable(Event.Player);
|
||||
@ -415,7 +412,6 @@ foreach (Event in RacePendingEvents) {
|
||||
CurrentSegment = CurrentSegment - 1;
|
||||
}
|
||||
if (UpdateRankingTimer == 0) UpdateRankingTimer = Now + 1000;
|
||||
declare Boolean ModeRounds_CanSpawn for Event.Player.Score = Rounds_Settings_CanSpawnDefault;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -510,7 +506,8 @@ if (Round_ForceEndRound || Round_SkipPauseRound) {
|
||||
***
|
||||
Race::StopSkipOutroAll();
|
||||
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")) {
|
||||
Scores::XmlRpc_SendScores(Scores::C_Section_PreEndRound, "");
|
||||
@ -525,14 +522,17 @@ if (Round_ForceEndRound || Round_SkipPauseRound || Round_Skipped) {
|
||||
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);
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible;
|
||||
MB_Sleep(3000);
|
||||
// Add them to the total scores
|
||||
ComputeScores();
|
||||
Race::SortScores(Race::C_Sort_TotalPoints);
|
||||
@ -545,6 +545,7 @@ if (Round_ForceEndRound || Round_SkipPauseRound || Round_Skipped) {
|
||||
MB_StopMap();
|
||||
}
|
||||
}
|
||||
StateMgr::ForcePlayersStates([StateMgr::C_State_Waiting]);
|
||||
CustomTimes.clear();
|
||||
CurrentRanking.clear();
|
||||
UIModules_SmallScoresTable::ResetCustomTimes();
|
||||
@ -639,20 +640,18 @@ Integer GetFinishTimeout(Integer _FinishTimeout) {
|
||||
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)}}}""";
|
||||
}
|
||||
} 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)}}}""";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -679,7 +678,7 @@ Integer GetFinishTimeout(Integer _FinishTimeout) {
|
||||
Points = PointsRepartition[PointsRepartition.count - 1];
|
||||
}
|
||||
declare Integer CurrentSegment for Player.Score;
|
||||
if (CurrentSegment == S_SegmentsPerRound) {
|
||||
if (CurrentSegment == S_SegmentsPerRound && Scores::GetPlayerPrevRaceTime(Player.Score) > 0) {
|
||||
Points += BonusForFinishers;
|
||||
}
|
||||
Scores::SetPlayerRoundPoints(Player.Score, Points);
|
||||
@ -948,9 +947,9 @@ Boolean MapIsOver(Boolean _UseTieBreak, Integer _PointsLimit, Integer _ValidRoun
|
||||
<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/TMNext/Menus/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds" halign="center" colorize="000"/>
|
||||
<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/TMNext/Menus/Icons/128x128/ICON_ARROW_RIGHT_OBLIQUE.dds" halign="center" colorize="1b2"/>
|
||||
<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>
|
||||
""";
|
||||
|
@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Seeding Time Attack mode
|
||||
*/
|
||||
#Extends "Libs/Nadeo/TMNext/TrackMania/Modes/TMNextBase.Script.txt"
|
||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaBase.Script.txt"
|
||||
|
||||
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||
#Const Version "2021-10-16"
|
||||
#Const Version "2023-09-25"
|
||||
#Const ScriptName "Modes/TM2020-Gamemodes/TM_SeedingTimeAttack_Online.Script.txt"
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
@ -12,20 +12,20 @@
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#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/ModeLibs/Common/Utils.Script.txt" as ModeUtils
|
||||
#Include "Libs/Nadeo/CommonLibs/Common/Semver.Script.txt" as Semver
|
||||
#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/Modes/TrophyRanking.Script.txt" as TrophyRanking
|
||||
#Include "Libs/Nadeo/Trackmania/MainMenu/Constants.Script.txt" as MenuConsts
|
||||
#Include "Libs/Nadeo/CMGame/Modes/Utils.Script.txt" as ModeUtils
|
||||
#Include "Libs/Nadeo/CMGame/Utils/Semver.Script.txt" as Semver
|
||||
|
||||
// UI from Race
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/TimeGap_Server.Script.txt" as UIModules_TimeGap
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/Checkpoint_Server.Script.txt" as UIModules_Checkpoint
|
||||
#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 "ManiaApps/Nadeo/TMNext/TrackMania/TimeAttack/UIModules/EndMatchTrophy_Server.Script.txt" as UIModules_EndMatchTrophy
|
||||
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/TimeGap_Server.Script.txt" as UIModules_TimeGap
|
||||
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/BigMessage_Server.Script.txt" as UIModules_BigMessage
|
||||
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/Checkpoint_Server.Script.txt" as UIModules_Checkpoint
|
||||
#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/Trackmania/Modes/TimeAttack/UIModules/EndMatchTrophy_Server.Script.txt" as UIModules_EndMatchTrophy
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
@ -48,7 +48,7 @@
|
||||
#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 key.\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_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/TimeAttack.Script.txt" //< Url of the mania app
|
||||
#Const C_FakeUsersNb 0
|
||||
|
||||
#Const C_UploadRecord True
|
||||
@ -170,7 +170,7 @@ UIModules_Record::SetSpecialVisibility(False);
|
||||
|
||||
***Match_InitMatch***
|
||||
***
|
||||
declare Task::LibCommonTask_K_Task Match_TrophyTask;
|
||||
declare Task::K_Task Match_TrophyTask;
|
||||
declare Integer Match_TrophyTaskEndTime;
|
||||
declare Integer Match_MatchDuration;
|
||||
declare Boolean Match_CanForceTrophyRankUpdate for This = False;
|
||||
@ -670,12 +670,12 @@ Void SetML() {
|
||||
|
||||
declare Real GlobalEndPosX;
|
||||
if (Frame_UI.Visible) {
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_RIGHT_OBLIQUE.dds");
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_RIGHT_OBLIQUE.dds");
|
||||
Quad_Toggle.RelativePosition_V3.X = 5.;
|
||||
GlobalEndPosX = -215.;
|
||||
AnimMgr.Add(Frame_UI, "<frame hidden=\"1\" />", Now, 250, CAnimManager::EAnimManagerEasing::Linear);
|
||||
} else {
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds");
|
||||
Quad_Toggle.ChangeImageUrl("file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds");
|
||||
Quad_Toggle.RelativePosition_V3.X = 0.;
|
||||
GlobalEndPosX = -160.;
|
||||
Frame_UI.Visible = True;
|
||||
@ -826,7 +826,7 @@ Void SetML() {
|
||||
</framemodel>
|
||||
<frame id="frame-global" pos="-160 30">
|
||||
<frame pos="53 -2.5" id="frame-toggle" z-index="1" >
|
||||
<quad id="quad-toggle" pos="0 0" size="4 4" class="quad-base" z-index="3" opacity="0.9" scriptevents="1" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/TMNext/Menus/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds" colorize="fff"/>
|
||||
<quad id="quad-toggle" pos="0 0" size="4 4" class="quad-base" z-index="3" opacity="0.9" scriptevents="1" halign="center" valign="center" image="file://Media/Manialinks/Nadeo/CMGame/Utils/Icons/128x128/ICON_ARROW_LEFT_OBLIQUE.dds" colorize="fff"/>
|
||||
</frame>
|
||||
<frame id="frame-ui" z-index="1">
|
||||
<quad pos="0 0" z-index="0" size="55 69" bgcolor="000" opacity="0.5"/>
|
||||
|
@ -3,10 +3,10 @@
|
||||
* Quick script ordered by Bergie for Telialigaen
|
||||
*/
|
||||
// #RequireContext CSmMode
|
||||
#Extends "Libs/Nadeo/TMNext/TrackMania/Modes/TMNextRoundsBase.Script.txt"
|
||||
#Extends "Modes/Nadeo/Trackmania/Base/TrackmaniaRoundsBase.Script.txt"
|
||||
|
||||
#Const CompatibleMapTypes "TrackMania\\TM_Race,TM_Race"
|
||||
#Const Version "2022-01-22"
|
||||
#Const Version "2023-10-16"
|
||||
#Const ScriptName "Modes/TrackMania/TM_TimeAttackRounds_Online.Script.txt"
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
@ -14,18 +14,18 @@
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
#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/ModeLibs/Common/Utils.Script.txt" as ModeUtils
|
||||
#Include "Libs/Nadeo/CommonLibs/Common/Semver.Script.txt" as Semver
|
||||
#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/Modes/TrophyRanking.Script.txt" as TrophyRanking
|
||||
#Include "Libs/Nadeo/Trackmania/MainMenu/Constants.Script.txt" as MenuConsts
|
||||
#Include "Libs/Nadeo/CMGame/Modes/Utils.Script.txt" as ModeUtils
|
||||
#Include "Libs/Nadeo/CMGame/Utils/Semver.Script.txt" as Semver
|
||||
|
||||
// UI from Race
|
||||
#Include "ManiaApps/Nadeo/TMxSM/Race/UIModules/Checkpoint_Server.Script.txt" as UIModules_Checkpoint
|
||||
#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 "ManiaApps/Nadeo/TMNext/TrackMania/TimeAttack/UIModules/EndMatchTrophy_Server.Script.txt" as UIModules_EndMatchTrophy
|
||||
#Include "Libs/Nadeo/TMGame/Modes/Base/UIModules/Checkpoint_Server.Script.txt" as UIModules_Checkpoint
|
||||
#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/Trackmania/Modes/TimeAttack/UIModules/EndMatchTrophy_Server.Script.txt" as UIModules_EndMatchTrophy
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
|
||||
// Settings
|
||||
@ -50,7 +50,7 @@
|
||||
#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 key.\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_ManiaAppUrl "file://Media/ManiaApps/Nadeo/Trackmania/Modes/TimeAttack.Script.txt" //< Url of the mania app
|
||||
#Const C_FakeUsersNb 0
|
||||
|
||||
#Const C_UploadRecord True
|
||||
@ -168,7 +168,7 @@ UIModules_Record::SetSpecialVisibility(False);
|
||||
|
||||
***Match_InitMatch***
|
||||
***
|
||||
declare Task::LibCommonTask_K_Task Match_TrophyTask;
|
||||
declare Task::K_Task Match_TrophyTask;
|
||||
declare Integer Match_TrophyTaskEndTime;
|
||||
declare Integer Match_MatchDuration;
|
||||
declare Boolean Match_CanForceTrophyRankUpdate for This = False;
|
||||
@ -366,6 +366,7 @@ if (Round_ForceEndRound || Round_SkipPauseRound || Round_Skipped) {
|
||||
if (!Round_SkipPauseRound) {
|
||||
ForcedEndRoundSequence();
|
||||
}
|
||||
MB_SetValidRound(False);
|
||||
} else {
|
||||
Map_ValidRoundsNb += 1;
|
||||
ComputeLatestRaceScores();
|
||||
|
239
TM_WaitYourMate.Script.txt
Normal file
239
TM_WaitYourMate.Script.txt
Normal file
@ -0,0 +1,239 @@
|
||||
// #RequireContext CSmMode
|
||||
#Extends "Modes/TrackMania/TM_TimeAttack_Online.Script.txt"
|
||||
|
||||
#Const C_MLID_WaitingMessage "WaitYourMate_WaitingMessage"
|
||||
|
||||
#Setting S_ForceRespawnWhenAhead False as ""
|
||||
#Setting S_TeamsConfig "" as "json of the Team attribution" // {"56f8dd9f-8581-444e-a2ad-1e8e89eed1a4":1,"752c4db6-26f9-44a3-9c80-08264d1665dc":1}
|
||||
|
||||
|
||||
***Match_Yield***
|
||||
***
|
||||
foreach (Event in PendingEvents) {
|
||||
if (Event.Type == CSmModeEvent::EType::OnPlayerAdded) {
|
||||
if (S_TeamsConfig == "") continue;
|
||||
if (Event.Player == Null || Event.Player.User == Null || Event.Player.Score == Null) continue;
|
||||
|
||||
declare Integer[Text] TeamsConfig;
|
||||
TeamsConfig.fromjson(S_TeamsConfig);
|
||||
|
||||
declare Integer ClanId = TeamsConfig.get(Event.Player.User.WebServicesUserId, 0);
|
||||
Event.Player.Score.LadderClan = ClanId;
|
||||
if (Event.Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned) SetPlayerClan(Event.Player, ClanId);
|
||||
log("Attributing Player "^ Event.Player.User.Name ^"to the clan " ^ ClanId);
|
||||
}
|
||||
}
|
||||
***
|
||||
|
||||
***Match_AfterLoadHud***
|
||||
***
|
||||
UIModules::UnloadModules(["UIModule_Race_TimeGap"]);
|
||||
|
||||
LoadManialinks();
|
||||
***
|
||||
|
||||
***Match_StartMatch***
|
||||
***
|
||||
Clans::SetClansNb(30);
|
||||
***
|
||||
|
||||
***Match_InitMap***
|
||||
***
|
||||
foreach (Player in AllPlayers) {
|
||||
declare netwrite Boolean Net_WaitYourMate_IsWaiting for Player = False;
|
||||
Net_WaitYourMate_IsWaiting = False;
|
||||
Player.TrustClientSimu = S_TrustClientSimu;
|
||||
}
|
||||
|
||||
declare Text[] Map_LoginsToRespawn;
|
||||
declare Text Map_TeamsConfig;
|
||||
***
|
||||
|
||||
***Match_StartMap***
|
||||
***
|
||||
Map_TeamsConfig = S_TeamsConfig;
|
||||
if (S_TeamsConfig != "") {
|
||||
declare Integer[Text] TeamsConfig ;
|
||||
TeamsConfig.fromjson(S_TeamsConfig);
|
||||
|
||||
foreach (Player in AllPlayers) {
|
||||
if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::Spawned) continue;
|
||||
declare Integer ClanId = TeamsConfig.get(Player.User.WebServicesUserId, 0);
|
||||
Player.Score.LadderClan = ClanId;
|
||||
SetPlayerClan(Player, ClanId);
|
||||
log("Attributing Player "^ Player.User.Name ^"to the clan " ^ ClanId);
|
||||
|
||||
declare CUIConfig UI <=> UIManager.GetUI(Player);
|
||||
if (UI == Null) continue;
|
||||
UI.SendChat("$ff3You play for the team $fff"^ ClanId);
|
||||
}
|
||||
}
|
||||
|
||||
Race::SetRespawnBehaviour(Race::C_RespawnBehaviour_NeverGiveUp);
|
||||
***
|
||||
|
||||
***Match_PlayLoop***
|
||||
***
|
||||
foreach (Event in Race::GetPendingEvents()) {
|
||||
if (Event.Player == Null) continue;
|
||||
switch (Event.Type) {
|
||||
case Events::C_Type_Waypoint: {
|
||||
if (Event.IsEndRace) continue;
|
||||
|
||||
UpdateClanStates(Event.Player.Score.LadderClan);
|
||||
|
||||
if (S_ForceRespawnWhenAhead) {
|
||||
Map_LoginsToRespawn.add(Event.Player.User.Login);
|
||||
}
|
||||
}
|
||||
case Events::C_Type_StartLine: {
|
||||
if (S_TeamsConfig != "") {
|
||||
declare Integer[Text] TeamsConfig ;
|
||||
TeamsConfig.fromjson(S_TeamsConfig);
|
||||
|
||||
declare Integer ClanId = TeamsConfig.get(Event.Player.User.WebServicesUserId, 0);
|
||||
Event.Player.Score.LadderClan = ClanId;
|
||||
|
||||
if (Event.Player.Score.LadderClan != Event.Player.CurrentClan) {
|
||||
log("Player spawned on the wrong team, but should not cause any issue");
|
||||
}
|
||||
}
|
||||
UpdateClanStates(Event.Player.Score.LadderClan);
|
||||
}
|
||||
case Events::C_Type_GiveUp: {
|
||||
UpdateClanStates(Event.Player.Score.LadderClan);
|
||||
}
|
||||
case Events::C_Type_Respawn: {
|
||||
UpdateClanStates(Event.Player.Score.LadderClan);
|
||||
Map_LoginsToRespawn.remove(Event.Player.User.Login);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (LoginToRespawn in Map_LoginsToRespawn) {
|
||||
declare CSmPlayer Player <=> GetPlayer(LoginToRespawn) ;
|
||||
if (Player == Null) {
|
||||
Map_LoginsToRespawn.remove(LoginToRespawn);
|
||||
} else {
|
||||
RespawnPlayer(Player);
|
||||
}
|
||||
}
|
||||
|
||||
if (Map_TeamsConfig != S_TeamsConfig) {
|
||||
Map_TeamsConfig = S_TeamsConfig;
|
||||
|
||||
if (S_TeamsConfig != "") {
|
||||
declare Integer[Text] TeamsConfig ;
|
||||
TeamsConfig.fromjson(S_TeamsConfig);
|
||||
|
||||
foreach (Player in AllPlayers) {
|
||||
declare Integer ClanId = TeamsConfig.get(Player.User.WebServicesUserId, 0);
|
||||
Player.Score.LadderClan = ClanId;
|
||||
|
||||
if (Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned) {
|
||||
log("Attributing Player "^ Player.User.Name ^" to the clan " ^ ClanId);
|
||||
SetPlayerClan(Player, ClanId);
|
||||
} else {
|
||||
log("Can't apply Player "^ Player.User.Name ^" to the clan " ^ ClanId ^ " because they is playing");
|
||||
}
|
||||
|
||||
UpdateClanStates(Player.Score.LadderClan);
|
||||
|
||||
declare CUIConfig UI <=> UIManager.GetUI(Player);
|
||||
if (UI == Null) continue;
|
||||
UI.SendChat("$ff3You play for the team $fff"^ ClanId);
|
||||
}
|
||||
}
|
||||
}
|
||||
***
|
||||
|
||||
Void ApplyModeToPlayer(CSmPlayer _Player, Boolean _Lock) {
|
||||
if (_Player == Null) return;
|
||||
if (_Player.SpawnStatus == CSmPlayer::ESpawnStatus::NotSpawned) return;
|
||||
|
||||
if (_Lock) {
|
||||
_Player.TrustClientSimu = False;
|
||||
} else {
|
||||
_Player.TrustClientSimu = S_TrustClientSimu;
|
||||
}
|
||||
SetPlayerVehicle_ControlledByMode(_Player, _Lock);
|
||||
|
||||
declare netwrite Boolean Net_WaitYourMate_IsWaiting for _Player = False;
|
||||
Net_WaitYourMate_IsWaiting = _Lock;
|
||||
}
|
||||
|
||||
Void LockPlayer(CSmPlayer _Player) {
|
||||
ApplyModeToPlayer(_Player, True);
|
||||
}
|
||||
|
||||
Void UnlockPlayer(CSmPlayer _Player) {
|
||||
ApplyModeToPlayer(_Player, False);
|
||||
}
|
||||
|
||||
Void UpdateClanStates(Integer _ClanId) {
|
||||
log("UpdateClanStates _ClanId: " ^ _ClanId);
|
||||
declare CSmPlayer[][Integer] TeammatesPerCheckpoint;
|
||||
|
||||
foreach (Player in Players) {
|
||||
if (Player.Score == Null) continue;
|
||||
if (Player.Score.BestRaceTimes.count > 0) continue;
|
||||
if (Player.Score.LadderClan != _ClanId) continue;
|
||||
|
||||
if (!TeammatesPerCheckpoint.existskey(Player.RaceWaypointTimes.count)) TeammatesPerCheckpoint[Player.RaceWaypointTimes.count] = [];
|
||||
TeammatesPerCheckpoint[Player.RaceWaypointTimes.count].add(Player);
|
||||
}
|
||||
|
||||
if (TeammatesPerCheckpoint.count == 1) {
|
||||
foreach (TeamMates in TeammatesPerCheckpoint) {
|
||||
foreach (Player in TeamMates) {
|
||||
UnlockPlayer(Player);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
TeammatesPerCheckpoint = TeammatesPerCheckpoint.sortkey();
|
||||
|
||||
declare Integer MinCheckpointNumber = -1;
|
||||
foreach (Checkpoint => TeamMates in TeammatesPerCheckpoint) {
|
||||
if (MinCheckpointNumber == -1) MinCheckpointNumber = Checkpoint;
|
||||
|
||||
foreach (Player in TeamMates) {
|
||||
ApplyModeToPlayer(Player, (Checkpoint != MinCheckpointNumber));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Void LoadManialinks() {
|
||||
declare Text MLText = """
|
||||
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
|
||||
<manialink version="3" name="{{{C_MLID_WaitingMessage}}}">
|
||||
<label id="label-waiting" pos="0 35" halign="center" valign="center" textfont="GameFontBlack" textsize="10" textprefix="$s" text="Waiting for your mate" hidden="1"/>
|
||||
<script><!--
|
||||
#Include "TimeLib" as TiL
|
||||
#Include "MathLib" as ML
|
||||
|
||||
main() {
|
||||
declare CMlLabel Label_Waiting <=> (Page.GetFirstChild("label-waiting") as CMlLabel);
|
||||
|
||||
wait (InputPlayer != Null);
|
||||
|
||||
while (True) {
|
||||
yield;
|
||||
|
||||
if (!PageIsVisible) continue;
|
||||
|
||||
if (GUIPlayer == Null) {
|
||||
Label_Waiting.Visible = False;
|
||||
} else {
|
||||
declare netread Boolean Net_WaitYourMate_IsWaiting for GUIPlayer = False;
|
||||
Label_Waiting.Visible = Net_WaitYourMate_IsWaiting;
|
||||
}
|
||||
}
|
||||
}
|
||||
--></script>
|
||||
</manialink>
|
||||
""";
|
||||
Layers::Create(C_MLID_WaitingMessage, MLText);
|
||||
Layers::SetType(C_MLID_WaitingMessage, CUILayer::EUILayerType::Normal);
|
||||
Layers::Attach(C_MLID_WaitingMessage);
|
||||
}
|
Reference in New Issue
Block a user