Add CP Knockout V2
This commit is contained in:
		
							
								
								
									
										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 0 as _("Number of warm up") | ||||
| #Setting S_WarmUpDuration 0 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" as _("Nb of players above which one extra elim. /CP. Same setting of Knock") | ||||
|  | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| // Constants | ||||
| // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // | ||||
| #Const C_ModeName "CP Knockout" | ||||
| //L16N [Laps] Description of the mode rules | ||||
| #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/Trackmania/Modes/Laps.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; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user