What I want to show you is the Gamemode-Script example I found in the Game Data.
It is called "TMSplitScreen_Competition.Script.txt"
I always thought that it's not possible to create your own gamemodes so far.
I will post the script here, so you can have a look at it:
Code: Select all
// *********************** ** ** * *
// * SplitScreen competition
// ******************* ** ** * *
#RequireContext CTrackManiaRaceRules
#Setting PointsLimit 10
#Const RoundPoints [10,6,4,3,2,1]
// === Scores ===
Boolean IsRaceBetter(CTMRaceResultNod _Race1, CTMRaceResultNod _Race2)
{
declare Comp = _Race1.Compare(_Race2, CTMRaceResultNod::ETMRaceResultCriteria::Time);
return Comp > 0;
}
Text GetFrameMapTimes()
{
declare Integer[Text] Times;
Times["Gold"] = Map.MapParameters.GoldTime;
foreach (Score in Scores) {
if (Score.BestRace.Time != -1)
Times[Score.PlayerInfo.Name] = Score.BestRace.Time;
}
Times = Times.sort();
declare Frame = """ <frame posn="-160 86">
<label posn="1 3" style="TextRaceStaticSmall" sizen="50 2" halign="left" text="$oBest times:" />""";
declare Line = 0;
foreach (Name => Time in Times) {
Frame ^= """<label posn="2 {{{-3*Line}}}"
style="TextRaceStaticSmall" sizen="25 3" halign="left" text="{{{Name}}}" />
<label posn="30 {{{-3*Line}}}" style="TextPlayerCardScore" sizen="25 3" halign="left"
text="$fff{{{Util_TimeToText(Time, True)}}}" />""";
Line += 1;
}
Frame ^= """</frame>""" ;
return Frame;
}
// === Race rounds ==
Void Race_DoRound()
{
UIManager.UIAll.UISequence = CGamePlaygroundUIConfig::EUISequence::Playing;
CutOffTimeLimit = 0;
Players_SpawnAll(Now + 2000);
foreach(Score, Scores) {
Score.PrevRaceDeltaPoints = 0;
}
sleep(500);
Synchro_DoBarrier();
declare RoundArrivalOrder = Integer[Text];
declare RoundArrivalOrderDirty = True;
declare MatchBestTime for Map = -1;
UIManager.UIAll.ManialinkPage = GetFrameMapTimes();
declare NbContestants = Players.count;
while( PlayersRacing.count > 0) {
RunGame();
EndRaceSequence_Update();
if (MatchEndRequested)
return;
// Process events queue
foreach(Event, PendingEvents) {
PassOn(Event);
if (Event.Type == CTmRaceRulesEvent::EType::WayPoint && Event.IsEndRace) {
declare Player <=> Event.Player;
assert(Player.CurRace.Time == Event.RaceTime);
Player.Score.PrevRace = Player.CurRace;
if (IsRaceBetter(Player.CurRace, Player.Score.BestRace)) {
Player.Score.BestRace = Player.CurRace;
if (MatchBestTime == -1 || MatchBestTime > Event.RaceTime) {
MatchBestTime = Event.RaceTime;
EndRaceSequence_Add(Player, "$iNew match record! $o$112" ^
Util_TimeToText(Event.RaceTime, True));
} else {
EndRaceSequence_Add(Player, "Personal record! $o$112" ^
Util_TimeToText(Event.RaceTime, True));
}
} else {
EndRaceSequence_Add(Player, "");
}
assert(!RoundArrivalOrder.existskey(Player.Id));
RoundArrivalOrder[Player.Id] = Event.RaceTime;
RoundArrivalOrderDirty = True;
if (CutOffTimeLimit == 0) { // grace preiod for the others to finish
CutOffTimeLimit = Now + 5000 + Map.MapParameters.GoldTime/6;
}
}
}
// Refresh score table.
if (RoundArrivalOrderDirty) {
RoundArrivalOrderDirty = False;
RoundArrivalOrder = RoundArrivalOrder.sort();
declare Index = 0;
declare Table = """<scoretable type="time">""";
foreach (PlayerId => Time in RoundArrivalOrder) {
declare Player <=> Players[PlayerId]; // faut le PlayerInfo
declare ScoreInc = 0;
if (Index < RoundPoints.count)
ScoreInc = RoundPoints[Index];
else
ScoreInc = 1;
Index += 1;
if (Index == NbContestants) // last players gets no points.
ScoreInc = 0;
Table ^= """<score login="{{{Player.Login}}}" value="{{{Time}}}"
inc="{{{ScoreInc}}}"/>""";
}
Table ^= """</scoretable>""";
UIManager.UIAll.SmallScoreTable = Table;
UIManager.UIAll.ManialinkPage = GetFrameMapTimes();
}
// end grace period if needed
if (CutOffTimeLimit != 0 && Now > CutOffTimeLimit) {
Players_UnspawnAll();
}
}
Synchro_DoBarrier();
// endround sequence
sleep(1000);
UIManager.UIAll.UISequence = CGamePlaygroundUIConfig::EUISequence::EndRound;
UIManager.UIAll.ScoreTableVisibility = CGamePlaygroundUIConfig::EVisibility::ForcedVisible;
// Compute result (TODO: not duplicate the ScoreInc computation)
declare Index = 0;
foreach (PlayerId => Time in RoundArrivalOrder) {
declare Player <=> Players[PlayerId];
declare ScoreInc = 0;
if (Index < RoundPoints.count)
ScoreInc = RoundPoints[Index];
else
ScoreInc = 1;
Index += 1;
if (Index == NbContestants) // last players gets no points.
ScoreInc = 0;
Player.Score.PrevRaceDeltaPoints = ScoreInc;
}
sleep(2000);
foreach(Score, Scores) {
Score.Points += Score.PrevRaceDeltaPoints;
Score.PrevRaceDeltaPoints = 0;
}
Scores_Sort(::ETMScoreSortOrder::TotalPoints);
sleep(2500);
UIManager.UIAll.SmallScoreTable = "";
UIManager.UIAll.ScoreTableVisibility = CGamePlaygroundUIConfig::EVisibility::Normal;
sleep(500);
Synchro_DoBarrier();
}
// === Main ===
main()
{
UIManager.ResetAll();
log("restart...");
IndependantLaps = False;
UiRounds = True;
AutoRespawnToGiveup = False;
// loop on the playlist
while( !ServerShutdownRequested ) {
UIManager.UIAll.ScoreTableVisibility = CGamePlaygroundUIConfig::EVisibility::ForcedVisible;
UIManager.UIAll.UISequence = CGamePlaygroundUIConfig::EUISequence::Intro;
LoadMap();
Players_UnspawnAll();
Scores_Clear();
// ============ Séquence d'intro:
{
sleep(0); // flush loading lag.
UIManager.UIAll.ManialinkPage = GetFrameMapTimes();
UIManager.UIAll.ScoreTableVisibility = CGamePlaygroundUIConfig::EVisibility::ForcedVisible;
UIManager.UIAll.UISequence = CGamePlaygroundUIConfig::EUISequence::Intro;
sleep(4000); // HACK; should be "wait(sequences over)"
UIManager.UIAll.ScoreTableVisibility = CGamePlaygroundUIConfig::EVisibility::Normal;
UIManager.UIAll.UISequence = CGamePlaygroundUIConfig::EUISequence::Playing;
foreach(Player, Players) {
declare UI <=> UIManager.UI[Player];
UI.BigMessage = Player.PlayerInfo.Name;
UI.BigMessageAvatar = Player.PlayerInfo.Login;
UI.BigMessageAvatarVariant = CGamePlaygroundUIConfig::EAvatarVariant::Default;
Player.RaceStartTime = Now + 100000; // spawn the cars.
}
sleep(3000);
UIManager.UIAll.BigMessage = "";
UIManager.UIAll.StatusMessage = "";
foreach(Player, Players) {
declare UI <=> UIManager.UI[Player];
UI.BigMessage = "";
UI.BigMessageAvatar = "";
UI.StatusMessage = "";
}
}
// ============ Play the rounds until the ending condition is reached.:
while( !MatchEndRequested ) {
Race_DoRound();
declare BestScore = 0;
if (Scores.count > 0 && Scores[0].Points >= PointsLimit)
break; // score limit reached.
}
// ============ Séquence de fin:
Players_UnspawnAll();
if (Scores.count > 0 && Scores[0].Points > 0 && !ServerShutdownRequested) {
UIManager.UIAll.ManialinkPage = GetFrameMapTimes();
UIManager.UIAll.UISequence = CGamePlaygroundUIConfig::EUISequence::Podium;
sleep(2500);
UIManager.UIAll.ManialinkPage = GetFrameMapTimes() ^
"""<frame posn="0 60">
<quad posn="0 3 -1" sizen="130 14" halign="center" style="Bgs1InRace"
substyle="BgTitle3" />
<label posn="0 0" halign="center" scale="2"
text="{{{Scores[0].PlayerInfo.Name}}}$z wins the match!" />
</frame>""";
sleep(3000);
UIManager.UIAll.ScoreTableVisibility = CGamePlaygroundUIConfig::EVisibility::ForcedVisible;
sleep(5000);
}
Synchro_DoBarrier();
UIManager.UIAll.ScoreTableVisibility = CGamePlaygroundUIConfig::EVisibility::Normal;
UIManager.UIAll.ManialinkPage = "";
Scores_Clear();
UnloadMap();
}
}
For me there are a lot of new variables inside this script.
You can use this script as it's own gamemode by starting a new server and choosing "Script" as gamemode.
I tried it out and it worked!
So as you can see it seems to be possible already to create you own gamemode.
The only problem is, that there isn't a documentation about all the variables and classes you can use in Game-Rule-scripts, I guess.
But maybe you know about such a documentation?
Greetings