Sorry, wenn ich hier mal auf deutsch schreibe.kapsubm wrote:the script so far (works perfectly in splitscreen, single and multiplayermode)
(issues: Scoretable on end of round)
the Difference to your script is, that u can respan, the time limit is taken in account,
in this time all players can do stunts as long as the time limit is not null
(scoring 1 minute due to nadeo restriction)
if we can sort out the table sort and maybe ladderranks, it should be ready for release.
Code: Select all
// *********************** ** ** * * // * Multiplayer Stunt competition // ******************* ** ** * * #RequireContext CTmMode #Include "TextLib" as TextLib #Include "MathLib" as MathLib #Include "Libs/Nadeo/Mode.Script.txt" #Include "Libs/Nadeo/TrackMania/TM.Script.txt" as TM // ******** Match Mode Settings ********* #Setting MatchMode 0 //0 = Match mode off; 1 = Points mode; 2 = Nr of rounds mode; 3 = Map playlist mode #Setting PointsLimit 30 //Nr of Points needed to win match. (Only used when MatchMode is set to 1) #Setting NrOfRoundsLimit 5 //Nr of rounds to play before match ends. (Only used when MatchMode is set to 2) #Setting CanMatchEndInTie False //True = The match can end in a tie; False = Extra round(s) are played if winning score is tied at end of match //********* GameMode Settings ******** #Setting GameMode 2 // 0 = Rounds (Not working) 1 = TimeAttack (Not working see separate script); 2 = StuntMode; 3 = Platform(Not working) // ******** TimeAttack Settings ********** #Setting TimeLimitSeconds 300 //Time limit for round in seconds #Setting AlwaysRespawnToStart False //True = Respawn to start; False = Respawn to latest checkpoint, but respawn twice within 1 sec to respawn to start. // ******** Various Settings ****** #Setting RandomMapOrder False //True = Random Map Order; False = Map Order of PlayList #Setting ShowGoldTime False //True = Show the map's gold time in upper left corner; False = Don't show gold time #Setting ForceStuntFigrueText False //True = Show StuntFigureText regardless of game type; False = Show StuntFigureText depending on game type. //***** Constants ****** #Const RoundPoints [10,6,4,3,2,1] #Const GAME_MODE_ROUNDS 0 #Const GAME_MODE_TIME_ATTACK 1 #Const GAME_MODE_STUNTS 2 #Const GAME_MODE_PLATFORM 3 #Const MATCH_MODE_OFF 0 //In this mode there is no match, just plain old time attack. #Const MATCH_MODE_POINTS 1 //In this mode the match ends when a player reaches the point limit. #Const MATCH_MODE_NR_OF_ROUNDS 2 //In this mode the match ends after a certain number of rounds have been played. #Const MATCH_MODE_PLAYLIST 3 //In this mode the match ends after all maps in playlist have been played. #Const DOUBLE_CLICK_THRESHOLD 1000 // #Const STUNT_TEXT_TIMEOUT 1000 #Const STUNT_MODE_COUNT_DOWN 60000 // Not configurable/working in ManiaPlanet 1.2e beta so setting it as a constant //My StuntUIStates #Const REGULAR_SCORE 0 //Makes UI display CurRace.StuntsScore #Const COUNT_DOWN_SCORE -1 //Makes UI display CurRace.StuntsScore - countdown penalty #Const SKIP_REDRAW -2 //Used to optimize drawing of ui somewhat #Const HAS_FINISHED -3 //Makes UI display CurRace.StuntsScore at end of race //StuntUIStates > 0 = time when to redraw regular score. (Last time a stunt was performed + STUNT_TEXT_TIMEOUT //#Const SORT_ASCENDING = 0 //#Const SORT_DECENDING = 0 declare Integer[Ident] RoundArrivalOrder; declare Integer[Ident] StuntUIState; declare Integer[] MapOrder; declare Boolean IsSkipThisRound; declare Text[CTmModeEvent::EStuntFigure] StuntFigureDictionary; declare CTmResult::ETmRaceResultCriteria ResultCriteria; declare Boolean ShowStuntFigureText; Void InitializeGlobalVars() { switch(GameMode) { case GAME_MODE_ROUNDS: { ShowStuntFigureText = ForceStuntFigrueText; ResultCriteria = CTmResult::ETmRaceResultCriteria::Stunts; } case GAME_MODE_TIME_ATTACK: { ShowStuntFigureText = ForceStuntFigrueText; ResultCriteria = CTmResult::ETmRaceResultCriteria::Stunts; } case GAME_MODE_STUNTS: { ShowStuntFigureText = True; //ShowStuntUI = True; ResultCriteria = CTmResult::ETmRaceResultCriteria::Stunts; } case GAME_MODE_PLATFORM: { ShowStuntFigureText = ForceStuntFigrueText; ResultCriteria = CTmResult::ETmRaceResultCriteria::Stunts; } } } // === Scores === Integer GetResultScore(CTmResult _Result, CTmResult::ETmRaceResultCriteria _ResultCriteria) { declare Integer Score; switch (_ResultCriteria) { case CTmResult::ETmRaceResultCriteria::Time: Score = _Result.StuntsScore; case CTmResult::ETmRaceResultCriteria::Stunts: Score = _Result.StuntsScore; case CTmResult::ETmRaceResultCriteria::NbRespawns: Score = _Result.StuntsScore; default: assert(False); //Shouldn't be here! FIx it! } return Score; } //Doesn't work with negative Results! Boolean IsResultBetter(CTmResult _Result1, CTmResult _Result2, CTmResult::ETmRaceResultCriteria _ResultCriteria) { if (_Result1 != Null && _Result2 != Null) log("IsResultBetter: " ^GetResultScore(_Result1,_ResultCriteria)^" "^GetResultScore(_Result2,_ResultCriteria)); declare Boolean IsBetter; if ( _Result1 == Null ) { log("Result1 was null"); IsBetter = False; } else if ( _Result2 == Null) { log("Result2 was null"); IsBetter = True; } else { declare Comp = _Result1.Compare(_Result2, _ResultCriteria); IsBetter = Comp > 0; } return IsBetter; } Integer Result_Compare(Integer _Result1, Integer _Result2, CTmResult::ETmRaceResultCriteria ResultCriteria) { declare Integer Result; switch(ResultCriteria) { case CTmResult::ETmRaceResultCriteria::Time: Result = _Result2 - _Result1; case CTmResult::ETmRaceResultCriteria::Stunts: Result = _Result1 - _Result2; case CTmResult::ETmRaceResultCriteria::NbRespawns: Result = _Result2 - _Result1; default: assert(False); //Shouldn't reach this point. } return Result; } Boolean IsResultBetter(Integer _Result1, Integer _Result2, CTmResult::ETmRaceResultCriteria ResultCriteria) { return Result_Compare(_Result1, _Result2, ResultCriteria) > 0; } Integer[Text] SortResultArray(Integer[Text] _ResultArray, CTmResult::ETmRaceResultCriteria ResultCriteria) { declare Integer[Text] SortedArray; switch(ResultCriteria) { case CTmResult::ETmRaceResultCriteria::Time: SortedArray = _ResultArray.sort(); case CTmResult::ETmRaceResultCriteria::Stunts: { foreach (Name => Score in _ResultArray) { SortedArray[Name] = -Score; } SortedArray = SortedArray.sort(); foreach (Name => Score in SortedArray){ SortedArray[Name] = MathLib::Abs(Score); } } case CTmResult::ETmRaceResultCriteria::NbRespawns: SortedArray = _ResultArray.sort(); default: assert(False); //Shouldn't reach this point. } return SortedArray; } Integer[Ident] SortRoundArrivalOrder(Integer[Ident] _RoundArrivalArray, CTmResult::ETmRaceResultCriteria ResultCriteria) { declare Integer[Ident] SortedArray; switch(ResultCriteria) { case CTmResult::ETmRaceResultCriteria::Time: SortedArray = _RoundArrivalArray.sort(); case CTmResult::ETmRaceResultCriteria::Stunts: { foreach (Id => Score in _RoundArrivalArray) { SortedArray[Id] = -Score; } SortedArray = SortedArray.sort(); foreach (Id => Score in SortedArray){ SortedArray[Id] = MathLib::Abs(Score); } } case CTmResult::ETmRaceResultCriteria::NbRespawns: SortedArray = _RoundArrivalArray.sort(); default: assert(False); //Shouldn't reach this point. } return SortedArray; } Integer GetEventScore(CTmModeEvent Event, CTmResult::ETmRaceResultCriteria ResultCriteria) { declare Integer Result; switch (ResultCriteria) { case CTmResult::ETmRaceResultCriteria::Time: Result = Event.RaceTime; case CTmResult::ETmRaceResultCriteria::Stunts: Result = Event.StuntsScore; case CTmResult::ETmRaceResultCriteria::NbRespawns: Result = Event.NbRespawns; default: assert(False); //Shouldn't be here! FIx it! } return Result; } Void ClearScoreTimes() { foreach (Score in Scores) { Score.BestRace.Time = -1; Score.BestLap.Time = -1; Score.PrevRace.Time = -1; Score.TempResult.Time = -1; Score.BestRace.StuntsScore = 0; Score.BestLap.StuntsScore = 0; Score.PrevRace.StuntsScore = 0; Score.TempResult.StuntsScore = 0; Score.BestRace.NbRespawns = -1; Score.BestLap.NbRespawns = -1; Score.PrevRace.NbRespawns = -1; Score.TempResult.NbRespawns = -1; } } Boolean IsPointsTied() { if (Scores.count > 1) return Scores[0].Points == Scores[1].Points; return False; } Integer CalcStuntScoreAtCountDown(CTmPlayer Player, Integer Time) { declare StuntScore = Player.CurRace.StuntsScore; if (Player.RaceStartTime != 0 && Time > STUNT_MODE_COUNT_DOWN) { StuntScore = StuntScore - (Time - STUNT_MODE_COUNT_DOWN) / 200; if (StuntScore < 0) StuntScore = 0; } return StuntScore; } Boolean IsEndOfMatchReached(Integer MatchRoundNr) { if (MatchMode == MATCH_MODE_POINTS && Scores.count > 0 && Scores[0].Points >= PointsLimit) return True; else if (MatchMode == MATCH_MODE_NR_OF_ROUNDS && MatchRoundNr >= NrOfRoundsLimit) return True; else if (MatchMode == MATCH_MODE_PLAYLIST && MatchRoundNr >= MapList.count) return True; return False; } Void UnSpawn(CTmPlayer Player) { TM::EndRaceSequence_Remove(Player); Player.RaceStartTime = 0; } Integer[] CreateMapOrder(Boolean shouldRandomize) { declare Integer[] newMapOrder; declare Integer maxMapNr = MapList.count-1; for(i,0,maxMapNr) { newMapOrder.add(i); } if (shouldRandomize) { declare Integer tmp; declare Integer rand; for (i, 0, maxMapNr) { rand = MathLib::Rand(0, maxMapNr); tmp = newMapOrder[i]; newMapOrder[i] = newMapOrder[rand]; newMapOrder[rand] = tmp; } } return newMapOrder; } Text GetRaceResultText(Integer Result, CTmResult::ETmRaceResultCriteria ResultCriteria) { declare Text RaceResultText; switch (ResultCriteria) { case CTmResult::ETmRaceResultCriteria::Time: { RaceResultText ^= TextLib::TimeToText(Result, True); } case CTmResult::ETmRaceResultCriteria::Stunts: { RaceResultText ^= Result ^" Points"; } default: assert(False); //Should not be here } return RaceResultText; } Text GetRaceResultText(CTmResult Result, CTmResult::ETmRaceResultCriteria ResultCriteria) { declare Text RaceResultText; RaceResultText = GetRaceResultText(GetResultScore(Result, ResultCriteria), ResultCriteria); return RaceResultText; } Text GetManiaLinkPage(Text text, Integer length, Integer height) { return """<frame posn="0 60"> <quad posn="0 3 -1" sizen="{{{length}}} {{{height}}}" halign="center" style="Bgs1InRace" substyle="BgTitle3" /> <label posn="0 0" halign="center" scale="1" text="{{{text}}}" /> </frame>"""; } Text GetManiaLinkPage(Text text) { return GetManiaLinkPage(text, 15, 2); } Void InitStuntFigureDictionary() { StuntFigureDictionary.clear(); StuntFigureDictionary[CTmModeEvent::EStuntFigure::None] = "None???"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::StraightJump] = "Jump"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::Flip] = "Flip"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::BackFlip] = "Back Flip"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::Spin] = "Spin"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::Aerial] = "Aerial"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::AlleyOop] = "Alley Ooop"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::Roll] = "Roll"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::Corkscrew] = "Corkscrew"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::SpinOff] = "Spin Off"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::Rodeo] = "Rodeo"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::FlipFlap] = "Flip Flap"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::Twister] = "Twister"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::FreeStyle] = "Free Style"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::SpinningMix] = "Spinning Mix"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::FlippingChaos] = "Flipping Chaos"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::RollingMadness] = "Rolling Madness"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckNone] = "Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckStraightJump] = "Jump Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckFlip] = "Flip Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckBackFlip] = "Back Flip Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckSpin] = "Spin Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckAerial] = "Aerial Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckAlleyOop] = "Alley Ooop Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckRoll] = "Roll Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckCorkscrew] = "Corkscrew Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckSpinOff] = "Spin Off Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckRodeo] = "Rodeo Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckFlipFlap] = "Flip Flap Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckTwister] = "Twister Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckFreeStyle] = "Free Style Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckSpinningMix] = "Spinning Mix Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckFlippingChaos] = "Flipping Chaos Wreck"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::WreckRollingMadness] = "Rolling Madness Wreck"; /// StuntFigureDictionary[CTmModeEvent::EStuntFigure::TimePenalty] = "Time Penalty"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::Reset] = "Reset"; StuntFigureDictionary[CTmModeEvent::EStuntFigure::RespawnPenalty] = "Respawn Penalty"; /// StuntFigureDictionary[CTmModeEvent::EStuntFigure::Grind] = "Grind"; } Text GetStuntText(CTmModeEvent Event) { declare Text StuntText = ""; /* (Chained)(Master) (Straight) (Basic) xxx (Angle)(!!!) + 34 50 Points */ if (Event.Combo > 0) { if (Event.Combo > 1) StuntText ^= """{{{Event.Combo}}}X"""; StuntText ^= " Chained"; } if (Event.IsReverse) StuntText ^= " Reverse"; if (Event.IsMasterJump) StuntText ^= " Master"; if (Event.IsStraight) StuntText ^= " Straight"; if (Event.Angle == 0) StuntText ^= " Basic"; StuntText ^=""" {{{StuntFigureDictionary[Event.StuntFigure]}}}"""; if (Event.Angle != 0) { StuntText ^= """ {{{Event.Angle}}}"""; for (i, 1, Event.Angle/180) StuntText ^= "!"; } return StuntText; } Void UpdateStuntScoreUI(CTmPlayer Player, Text StuntFigureText, Integer StuntPoint, Integer Score) { declare UI <=> UIManager.GetUI(Player); if (UI != Null) { declare Text PointIncrText; declare Text plusminus; plusminus="-"; if (StuntPoint > 0) { if (StuntFigureText != " Basic Respawn Penalty") { plusminus="+"; } PointIncrText = plusminus^"""{{{StuntPoint}}}"""; } UI.StatusMessage = """{{{StuntFigureText}}} {{{PointIncrText}}} {{{Score}}} Points"""; } } Void UpdateStuntScoreUI(CTmPlayer Player, Text StuntFigureText, Integer StuntPoint) { UpdateStuntScoreUI(Player, StuntFigureText, StuntPoint, Player.CurRace.StuntsScore); } /* Not used. (Need to rewrite it in order to improve stunt mode ui.) Void doUI(CPlayer _Player) { declare UI <=> UIManager.GetUI(_Player); declare CUILayer UILayerTmp; if ( UI != Null ) { UI.UILayers.add(UILayerTmp); //frame pos 80 -45 = lower right quadrant UILayerTmp.ManialinkPage = """<frame posn="0 0" > <!-- <quad posn="0 0 -1" valign="center" halign="center" sizen="160 90" style="Bgs1InRace" substyle="BgTitle3" /> --> <label posn="0 0" valign="center" halign="center" scale="2" text="Stunt: " /> </frame>"""; } } */ Void logUI(CUIConfig UI, Text _text) { log(_text ^" UIStatus: " ^UI.UIStatus); log("Number of layers: "^UI.UILayers.count); //log("IsHidden: "^UI.OverlayHideNotices ^ UI.OverlayHideMapInfo^UI.OverlayHideOpponentsInfo^UI.OverlayHideChat^UI.OverlayHideCheckPointList^UI.OverlayHideRoundScores^UI.OverlayHideAll^UI.OverlayHideScoresOnAltMenu^UI.NoticesFilter_HidePlayerInfo^UI.NoticesFilter_HidePlayerWarning^UI.NoticesFilter_HidePlayerInfoIfNotMe^UI.NoticesFilter_HidePlayerWarningIfNotMe^UI.NoticesFilter_HideMapInfo^UI.NoticesFilter_HideMapWarning^UI.NoticesFilter_HideMatchInfo^UI.NoticesFilter_HideMatchWarning); log("ScoreTableVisibility: "^UI.ScoreTableVisibility^" SmallScoreTableVisibility: "^UI.SmallScoreTableVisibility^" AlliesLabelsVisibility: "^UI.AlliesLabelsVisibility^" EnemiesLabelsVisibility: "^UI.EnemiesLabelsVisibility^" EnemiesLabelsShowGauges: "^UI.EnemiesLabelsShowGauges); log("ManiaLinkPage :"^ UI.ManialinkPage); } //Best times shown in upper left corner Text GetFrameMapResults(CTmResult::ETmRaceResultCriteria ResultCriteria) { declare Integer[Text] Results; if (ShowGoldTime && ResultCriteria == CTmResult::ETmRaceResultCriteria::Time) { Results["Gold"] = Map.TMObjective_GoldTime; } foreach (Score in Scores) { switch (ResultCriteria) { case CTmResult::ETmRaceResultCriteria::Time: { if (Score.BestRace.Time != -1) Results[Score.User.Name] = Score.BestRace.Time; } case CTmResult::ETmRaceResultCriteria::Stunts: { if (Score.BestRace.StuntsScore != -1) Results[Score.User.Name] = Score.BestRace.StuntsScore; } default: assert(False); //Should not reach this point } } Results = SortResultArray(Results, ResultCriteria); declare Text Legend; switch (ResultCriteria) { case CTmResult::ETmRaceResultCriteria::Time: Legend = ("$oBest times:"); case CTmResult::ETmRaceResultCriteria::Stunts: Legend = ("$oBest scores:"); default: assert(False); //Shouldn't reach this point } declare Frame = """ <frame posn="-160 76"> <label posn="1 3" style="TextRaceStaticSmall" sizen="50 2" halign="left" text="{{{Legend}}}" />"""; declare Line = 0; foreach (Name => Result in Results) { Frame ^= """<label posn="2 {{{-3*Line}}}" style="TextPlayerCardName" sizen="25 3" halign="left" text="{{{Name}}}" /> <label posn="30 {{{-3*Line}}}" style="TextRaceStaticSmall" sizen="25 3" halign="left" text="$0F0{{{GetRaceResultText(Result, ResultCriteria)}}}" />"""; Line += 1; } Frame ^= """</frame>""" ; return Frame; } declare CUILayer UILayerScores; declare CUILayer UILayerInfo; // ============ Intro Sequence: Void PlayIntroSequence(Text InfoText){ sleep(0); // flush loading lag. UILayerScores.ManialinkPage = GetFrameMapResults(ResultCriteria); UIManager.UIAll.UILayers.clear(); UIManager.UIAll.UILayers.add(UILayerScores); UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible; UIManager.UIAll.UISequence = CUIConfig::EUISequence::Intro; //sleep(4000); // HACK; should be "wait(sequences over)" wait(UIManager.UIAll.UISequenceIsCompleted); if (InfoText == "") UILayerInfo.ManialinkPage = ""; else { UILayerInfo.ManialinkPage = GetManiaLinkPage(InfoText); } UIManager.UIAll.UILayers.add(UILayerInfo); UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal; UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing; foreach(Player, Players) { declare UI <=> UIManager.UI[Player]; Player.RaceStartTime = Now + 100000; // spawn the cars. } sleep(3000); //Remove UILayerInfo UIManager.UIAll.UILayers.clear(); UIManager.UIAll.UILayers.add(UILayerScores); UIManager.UIAll.BigMessage = ""; UIManager.UIAll.StatusMessage = ""; foreach(Player, Players) { declare UI <=> UIManager.UI[Player]; UI.BigMessage = ""; UI.BigMessageAvatarLogin = ""; UI.StatusMessage = ""; } } // === Race rounds == Void DoTimeAttackRace() { UiRounds = False; if (GameMode == GAME_MODE_STUNTS) { //Should be stuntmodeui= true UiStuntsMode = True; UiRaceChrono = CTmMode::ETmRaceChronoBehaviour::CountDown; UiDisplayStuntsNames = True; //Nadeo hasn't got it working yet. Extracted own stunt name own StuntUI Layer. ResultCriteria = CTmResult::ETmRaceResultCriteria::Stunts; } else { //StuntMode Off ResultCriteria = CTmResult::ETmRaceResultCriteria::Time; } UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing; TM::Players_SpawnAll(Now + 2000); foreach(Score, Scores) { Score.PrevRaceDeltaPoints = 0; } sleep(500); Synchro_DoBarrier(); RoundArrivalOrder = Integer[Ident]; declare RoundArrivalOrderDirty = True; declare LastRespawnTime = Integer[Ident]; declare CheckpointCount = Integer[Ident]; foreach (Player, Players) { LastRespawnTime[Player.Id] = Now; } declare Integer MatchBestResult for Map = -1; foreach(Player, Players) { declare Integer StuntUIState for Player; Player.CurRace.StuntsScore = 0; // StuntsScore[Player.Id] = 0; StuntUIState = REGULAR_SCORE; //Is this one needed? } UILayerScores.ManialinkPage = GetFrameMapResults(ResultCriteria); CutOffTimeLimit = Now + TimeLimitSeconds * 1000 +2500; //=== Loop until all players finished racing declare NbContestants = Players.count; while( Now < CutOffTimeLimit ) { TM::RunGame(); TM::EndRaceSequence_Update(); if (MatchEndRequested) return; // Process events queue foreach(Event, PendingEvents) { PassOn(Event); declare Text stunttext = ""; declare Player <=> Event.Player; declare Integer StuntUIState for Player; stunttext=GetStuntText(Event); if (Event.Type == CTmModeEvent::EType::Stunt) { XmlRpc.SendCallback("playerStunt", "playerLogin:"^Player.User.Login^";stuntScore:"^Player.CurRace.StuntsScore^";eventScore:"^Event.Points^";Stunt:"^stunttext^":"); if ( stunttext == " Basic Respawn Penalty" ) { Player.CurRace.StuntsScore -= Event.Points; } else { Player.CurRace.StuntsScore += Event.Points; } UpdateStuntScoreUI(Event.Player, GetStuntText(Event), Event.Points); StuntUIState = Now + STUNT_TEXT_TIMEOUT; } else if (Event.Type == CTmModeEvent::EType::StartLine) { CheckpointCount[Player.Id] = 0; StuntUIState = REGULAR_SCORE; //Displays stuntui when respawned Player.CurRace.StuntsScore = 0; } else if (Event.Type == CTmModeEvent::EType::WayPoint) { CheckpointCount[Player.Id] +=1; //xmlrpc declare Text Stuntdata= "playerLogin:"^Player.User.Login^";stuntScore:"^Player.CurRace.StuntsScore; XmlRpc.SendCallback("playerCheckpoint", Stuntdata); if (Event.IsEndRace) { assert(Player.CurRace.Time == Event.RaceTime); Player.CurRace.StuntsScore = CalcStuntScoreAtCountDown(Player, Event.RaceTime); StuntUIState = HAS_FINISHED; //Stops the countdown in StuntUI and displays the final score. Player.Score.PrevRace = Player.CurRace; declare Text ResultText = "$00F" ^ GetRaceResultText(Player.CurRace, ResultCriteria); declare NbRespawns = Player.CurRace.NbRespawns; //xmlrpc XmlRpc.SendCallback("playerFinish", "playerLogin:"^Player.User.Login^";stuntScore:"^Player.CurRace.StuntsScore^";CPCount:"^NbRespawns); if (IsResultBetter(Player.CurRace, Player.Score.BestRace, ResultCriteria)) { Player.Score.BestRace = Player.CurRace; if (IsResultBetter(GetResultScore(Player.CurRace, ResultCriteria), MatchBestResult, ResultCriteria)) { MatchBestResult = GetResultScore(Player.CurRace, ResultCriteria); TM::EndRaceSequence_Add(Player, TextLib::Compose(_("New match record! %1"), ResultText)); } else { TM::EndRaceSequence_Add(Player, TextLib::Compose(_("Personal record! %1"), ResultText)); } RoundArrivalOrder[Player.Id] = GetEventScore(Event, ResultCriteria); RoundArrivalOrderDirty = True; } else { if (GameMode != GAME_MODE_STUNTS) ResultText = ""; TM::EndRaceSequence_Add(Player, ResultText); } } } } //Not fully optimized, could have a proper Dirty flag... foreach(Player, Players) { declare Integer StuntUIState for Player; //assert (Player.Id!= prevPlayerId); if ((StuntUIState > 0 && Now > StuntUIState) || StuntUIState == REGULAR_SCORE) { UpdateStuntScoreUI(Player, "", 0); StuntUIState = SKIP_REDRAW; } else if (StuntUIState == HAS_FINISHED) { //UpdateStuntScoreUI(Player, "", 0); } declare CurRaceTime = Now - Player.RaceStartTime; if (Player.RaceStartTime > 0 && CurRaceTime > STUNT_MODE_COUNT_DOWN && StuntUIState != HAS_FINISHED) { UpdateStuntScoreUI(Player, "$f00Finish Now!", 0, CalcStuntScoreAtCountDown(Player, CurRaceTime)); } //prevPlayerId = Player.Id; } // Refresh score table. if (RoundArrivalOrderDirty) { RoundArrivalOrderDirty = False; RoundArrivalOrder = SortRoundArrivalOrder(RoundArrivalOrder, ResultCriteria); UILayerScores.ManialinkPage = GetFrameMapResults(ResultCriteria); // refresh scores... } // Respawn unspawned players if (PlayersWaiting.count > 0) { TM::Players_SpawnWaiting(0); } } TM::Players_UnspawnAll(); Synchro_DoBarrier(); // endround sequence sleep(1000); } Void PlayEndOfRoundSequence(Text InfoText, Integer MatchRoundNr) { UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound; UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible; sleep(6500); //Showing race times End of round if (MatchMode != MATCH_MODE_OFF) { //Display match point sequence UiRounds = False; UiStuntsMode = True; if (InfoText == "") UILayerInfo.ManialinkPage = ""; else { UILayerInfo.ManialinkPage = GetManiaLinkPage(InfoText); } UIManager.UIAll.UILayers.add(UILayerInfo); declare Index = 0; declare Text Table; switch(ResultCriteria) { case CTmResult::ETmRaceResultCriteria::Time: Table = """<scoretable type="time">"""; case CTmResult::ETmRaceResultCriteria::Stunts: Table = """<scoretable type="stunts">"""; case CTmResult::ETmRaceResultCriteria::NbRespawns: assert(False); //Not implemented yet default: assert(False); //Shouldn't reach this point. } declare Ident PrevPlayerId = NullId; declare ScoreInc = 0; foreach (PlayerId => Score in RoundArrivalOrder) { declare Player <=> Players[PlayerId]; // faut le PlayerInfo //Only update score of previous player if the times aren't identical declare Boolean IsSameTimeAsPrevPlayer = (PrevPlayerId != NullId && Score == RoundArrivalOrder[PrevPlayerId]); if (!IsSameTimeAsPrevPlayer) { if (Index < RoundPoints.count) ScoreInc = RoundPoints[Index]; else ScoreInc = 1; if (Index == Players.count -1) // last players gets no points. ScoreInc = 0; } Index += 1; Table ^= """<score login="{{{Player.Login}}}" value="{{{Score}}}" inc="{{{ScoreInc}}}"/>"""; Player.Score.PrevRaceDeltaPoints = ScoreInc; PrevPlayerId = PlayerId; } Table ^= """</scoretable>"""; UIManager.UIAll.SmallScoreTable = Table; sleep(12000); //Showing DeltaPoints foreach(Score, Scores) { Score.Points += Score.PrevRaceDeltaPoints; Score.PrevRaceDeltaPoints = 0; } if (IsPointsTied() && IsEndOfMatchReached(MatchRoundNr)) UILayerInfo.ManialinkPage = GetManiaLinkPage("Points are tied! Next round will be a tiebreaker!", 210, 14); sleep(2500); //Showing TotalPoints UILayerInfo.ManialinkPage = ""; } UIManager.UIAll.SmallScoreTable = ""; UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal; sleep(500); Synchro_DoBarrier(); } //==== EndOfMatchSequence ==== Void EndOfMatchSequence() { TM::Players_UnspawnAll(); UiRounds = True; if (Scores.count > 0 && Scores[0].Points > 0 && !ServerShutdownRequested) { UILayerScores.ManialinkPage = GetFrameMapResults(ResultCriteria); UIManager.UIAll.UISequence = CUIConfig::EUISequence::Podium; sleep(12500); declare Text WinnerNames = Scores[0].User.Name; declare TextBoxWidth = 130; for (i, 1, Scores.count -1) { if ( Scores[i].Points == Scores[0].Points) { WinnerNames ^= TextLib::MLEncode(" & ") ^Scores[i].User.Name; TextBoxWidth = 320; } else break; } declare VictoryText = TextLib::Compose(_("$<%1$> wins the match!"), WinnerNames); UILayerScores.ManialinkPage = GetFrameMapResults(ResultCriteria); UILayerInfo.ManialinkPage = GetManiaLinkPage(VictoryText, TextBoxWidth, 14); UIManager.UIAll.UILayers.clear(); UIManager.UIAll.UILayers.add(UILayerScores); UIManager.UIAll.UILayers.add(UILayerInfo); sleep(3000); UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible; sleep(5000); } Synchro_DoBarrier(); UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::Normal; UIManager.UIAll.UILayers.clear(); Scores_Clear(); MatchEndRequested = False; // taken into account. } // === Main === main() { //Init UIManager.ResetAll(); log("restart..."); InitializeGlobalVars(); IndependantLaps = True; RespawnBehaviour = ::ETMRespawnBehaviour::Normal; UILayerScores <=> UIManager.UILayerCreate(); UILayerInfo <=> UIManager.UILayerCreate(); InitStuntFigureDictionary(); MapOrder = CreateMapOrder(RandomMapOrder); declare MapOrderIndex = 0; //Since the script needs to be pasted instead of loaded there is already a loaded map. //Need to unload it to enforce map order. //if (MapLoaded) // UnloadMap(); // loop until server interrupted while( !ServerShutdownRequested ) { declare MatchRoundNr = 1; declare IsTieBreakRound = False; declare IntroText = ""; declare EndOfRoundText = ""; //make sure new match start with first map in maplist in playlist mode. (Last match could've ended in tiebreak.) if (MatchMode == MATCH_MODE_PLAYLIST) { MapOrderIndex = 0; } // ============ Play the Time Attack races until the end of match condition is reached: while( !MatchEndRequested ) { IsSkipThisRound = False; //Display scoreTable before match in Rounds mode if a Match Mode is set if (MatchMode!= MATCH_MODE_OFF) UiRounds = True; UIManager.UIAll.ScoreTableVisibility = CUIConfig::EVisibility::ForcedVisible; UIManager.UIAll.UISequence = CUIConfig::EUISequence::Intro; NextMapIndex = MapOrder[MapOrderIndex]; LoadMap(); //ClearScoreTimes(); //Don't call "Scores_Clear" because we want to preserve the match points Scores_Clear(); TM::Players_UnspawnAll(); //Set texts according to settings and state if (IsTieBreakRound) { IntroText = "Tiebreaker round!"; EndOfRoundText = "Player with most points wins!"; } else if (MatchMode == MATCH_MODE_POINTS) { if (MatchRoundNr == 1) IntroText = "New match!"; else IntroText = ""; EndOfRoundText = """{{{PointsLimit}}} points to win!"""; } else if (MatchMode == MATCH_MODE_NR_OF_ROUNDS) { IntroText = """Round {{{MatchRoundNr}}} of {{{NrOfRoundsLimit}}}"""; EndOfRoundText = """Round {{{MatchRoundNr}}} of {{{NrOfRoundsLimit}}}"""; } else if (MatchMode == MATCH_MODE_PLAYLIST) { IntroText = """Round {{{MatchRoundNr}}} of {{{MapList.count}}}"""; EndOfRoundText = """Round {{{MatchRoundNr}}} of {{{MapList.count}}}"""; } PlayIntroSequence(IntroText); DoTimeAttackRace(); if (!IsSkipThisRound) PlayEndOfRoundSequence(EndOfRoundText, MatchRoundNr); if (IsEndOfMatchReached(MatchRoundNr)) { if (!CanMatchEndInTie && IsPointsTied()) IsTieBreakRound = True; else { IsTieBreakRound = False; break; // score limit reached. } } if (!IsSkipThisRound || MatchMode == MATCH_MODE_PLAYLIST) MatchRoundNr += 1; MapOrderIndex += 1; if (MapOrderIndex >= MapList.count) { MapOrderIndex = 0; } UnloadMap(); } // ============ End of Match Sequence: if (MatchMode != MATCH_MODE_OFF) EndOfMatchSequence(); UnloadMap(); } // ====== Cleanup log("Cleanup! (Is this bit of code ever called???)"); UIManager.UILayerDestroy(UILayerScores); UIManager.UILayerDestroy(UILayerInfo); }
Wollten gestern mal einen Stuntserver zum laufen bringen,
hat leider noch nicht geklappt.
Verstehe das mit dem splitscreen auch nicht so ganz.
Kann man den mode auch normal laufen lassen?