/**************************************** * Defusing Game Mode * * Author: steeffeen * * Contact: mail@steeffeen.com * ****************************************/ #Extends "Modes/ShootMania/ModeBase.Script.txt" /**************************************** INCLUDES ****************************************/ #Include "MathLib" as MathLib #Include "Libs/Nadeo/Victory.Script.txt" as Victory #Include "Libs/Nadeo/ShootMania/Score.Script.txt" as Score #Include "Libs/Nadeo/ShootMania/SM.Script.txt" as SM /**************************************** Settings ****************************************/ #Setting S_RoundsTimeLimit 2. as _("Time Limit to activate the Bomb (Minutes)") #Setting S_RoundsToWin 5 as _("Rounds to win a Map") #Setting S_RoundGapToWin 2 as _("Rounds Gap needed to win a Map") #Setting S_RoundsLimit 10 as _("Rounds Limit (First Team reaching it wins)") #Setting S_ActivationTime 5. as _("Time to activate the Bomb (Seconds)") #Setting S_DefusingTimeFactor 1.5 as _("Factor for Time to defuse the Bomb") #Setting S_BombExplosionTime 45. as _("Time until an activated Bomb explodes (Seconds)") /**************************************** CONSTANTS ****************************************/ #Const ScriptName "Defusing.Script.txt" #Const Version "0.05 (20.11.2013)" #Const CompatibleMapTypes "DefusingArena" /**************************************** GLOBALES ****************************************/ declare Ident[][Integer] G_ClanSpawnIds; // Ids of each clans spawn blocks declare Ident[] G_BombPoleIds; // Ids of the bomb spots declare Integer G_AttackingClan; // Clan number of the currently attacking team declare Ident G_BombCarrierId; // Id of the object carrying the bomb (player/pole) /**************************************** LABELS ****************************************/ ***LogVersion*** *** MB_LogVersion(ScriptName, Version); MB_LogVersion(Score::GetScriptName(), Score::GetScriptVersion()); MB_LogVersion(SM::GetScriptName(), SM::GetScriptVersion()); MB_LogVersion(Victory::GetScriptName(), Victory::GetScriptVersion()); *** ***StartServer*** *** // Enable rounds in modebase MB_UseSectionRound = True; // Set mode options UseClans = True; // Add bots Users_SetNbFakeUsers(2, 2); *** ***StartMap*** *** // Prepare map PrepareMap(); // Begin match ClanScores[1] = 0; ClanScores[2] = 0; UpdateClanScoresSummary(); Score::MatchBegin(); Victory::MatchBegin(); *** ***StartRound*** *** // Prepare next round PrepareRound(); // Prepare scores Score::RoundBegin(); Victory::RoundBegin(); // Set the begin of the round Mode::Synchro_DoBarrier(); StartTime = Now + 3500; EndTime = MathLib::NearestInteger(StartTime + S_RoundsTimeLimit * 60. * 1000.); UIManager.UIAll.UISequence = CUIConfig::EUISequence::Playing; *** ***PlayLoop*** *** // Manage players foreach (Player in Players) { switch (Player.SpawnStatus) { case CSmPlayer::ESpawnStatus::NotSpawned: { if (Player.RequestsSpectate) { // The player wants to spectate - Do nothing continue; } // Spawn the player MySpawnPlayer(Player); } case CSmPlayer::ESpawnStatus::Spawned: { if (Player.RequestsSpectate) { // The player wants to spectate UnspawnPlayer(Player); continue; } if (Player.RequestedClan != Player.CurrentClan) { // The player wants to change its team UnspawnPlayer(Player); continue; } } } } // Manage events foreach (Event in PendingEvents) { switch (Event.Type) { case CSmModeEvent::EType::OnHit: { if (Event.Shooter == Event.Victim) { // Self hit Discard(Event); continue; } if (Event.Shooter.CurrentClan == Event.Victim.CurrentClan) { // Team hit Discard(Event); continue; } // Opponent hit // Calculate points declare Points = Event.Damage / 100; // Grant points for the shooter Score::AddPoints(Event.Shooter, Points); // Set the amount of + displayed for the shooter Event.ShooterPoints = Points; // Pass on event PassOn(Event); } case CSmModeEvent::EType::OnArmorEmpty: { // Player has been eliminated PassOn(Event); } case CSmModeEvent::EType::OnCapture: { // Bomb has been planted G_BombCarrierId = Event.BlockPole.Id; EndTime = MathLib::NearestInteger(Now + S_BombExplosionTime * 1000.); PassOn(Event); } default: { // Not used events Discard(Event); } } } // Manage pole captures foreach (PoleId in G_BombPoleIds) { declare Pole <=> BlockPoles[PoleId]; if (BlockPoles.existskey(G_BombCarrierId)) { // Bomb has been activated if (PoleId != G_BombCarrierId) { // On another pole... Pole.Gauge.Speed = 0; Pole.Gauge.Value = 0; continue; } declare Defusing = False; // Loop through capturing players foreach (PlayerId in Pole.Sector.PlayersIds) { declare Player <=> Players[PlayerId]; if (Player.CurrentClan == G_AttackingClan) { // Attacker continue; } // Defender Defusing = True; break; } if (Defusing) { // Being defused! if (Pole.Gauge.ValueReal <= 0.) { // Completely defused! Victory::SetRoundWinnerIfNoWinner(3-G_AttackingClan); } Pole.Gauge.Speed = -MathLib::NearestInteger(Pole.Gauge.Max / (S_ActivationTime * S_DefusingTimeFactor * 1000.)); continue; } // Not defusing Pole.Gauge.Speed = 0; Pole.Gauge.Value = Pole.Gauge.Max; continue; } // Bomb is not yet activated declare Activating = False; // Loop through capturing players foreach (PlayerId in Pole.Sector.PlayersIds) { declare Player <=> Players[PlayerId]; if (Player.CurrentClan != G_AttackingClan) { // Defender continue; } // Attacker Activating = True; } if (Activating) { // Being activated! Pole.Gauge.Speed = MathLib::NearestInteger(Pole.Gauge.Max / (S_ActivationTime * 1000.)); continue; } // Not activating Pole.Gauge.Speed = 0; Pole.Gauge.Value = 0; } // Round end conditions if (Now >= EndTime) { if (BlockPoles.existskey(G_BombCarrierId)) { // Bomb exploded Victory::SetRoundWinnerIfNoWinner(G_AttackingClan); } else { // Attackers didn't set bomb Victory::SetRoundWinnerIfNoWinner(3-G_AttackingClan); } } if (Victory::NoRoundWinner()) continue; // Round is won MB_StopRound = True; *** ***EndRound*** *** // End round StartTime = -1; EndTime = -1; Score::RoundEnd(); Victory::SetRoundDrawIfNoWinner(); UIManager.UIAll.UISequence = CUIConfig::EUISequence::EndRound; // Validate round winner if (!Victory::IsRoundDraw()) { foreach (Index => Team in Teams) { if (!Victory::IsRoundWinner(Index+1)) continue; ClanScores[Index+1] += 1; break; } UpdateClanScoresSummary(); } Victory::RoundEnd(); // Match end conditions Victory::SetMatchWinnerFromScore(S_RoundsToWin, S_RoundGapToWin, S_RoundsLimit); if (Victory::NoMatchWinner()) continue; // Match is won MB_StopMap = True; *** ***EndMap*** *** // End match Score::MatchEnd(); Victory::MatchEnd(); *** /**************************************** FUNCTIONS ****************************************/ // Spawn the given player Void MySpawnPlayer(CSmPlayer _Player) { // Find a spawn block for the player declare SpawnIds = G_ClanSpawnIds[_Player.RequestedClan]; declare SpawnId = SpawnIds[MathLib::Rand(0, SpawnIds.count-1)]; declare Spawn <=> BlockSpawns[SpawnId]; // Spawn the player in its desired team SM::SpawnPlayer(_Player, _Player.RequestedClan, Spawn, StartTime); } // Load and prepare map for the match Void PrepareMap() { // Turn lights on foreach (Base in Bases) { Base.IsActive = True; Base.Clan = 0; } // Save team spawns G_ClanSpawnIds.clear(); foreach (Spawn in BlockSpawns) { if (Spawn.Order != 1 && Spawn.Order != 2) { // No team spawn continue; } if (!G_ClanSpawnIds.existskey(Spawn.Order)) { // Init spawns array G_ClanSpawnIds[Spawn.Order] = Ident[]; } // Save spawn id in array of its order G_ClanSpawnIds[Spawn.Order].add(Spawn.Id); } // Save bomb spots G_BombPoleIds.clear(); foreach (Pole in BlockPoles) { if (Pole.Tag != "A" && Pole.Tag != "B" && Pole.Tag != "C") { // No bomb spot continue; } // Init values Pole.Gauge.Max = 100000; // Add pole to bomb spots G_BombPoleIds.add(Pole.Id); } // Reset attacking team G_AttackingClan = -1; } // Prepare next round Void PrepareRound() { // Despawn all players SM::UnspawnAllPlayers(); // Switch sides if necessary declare SwitchSides = True; if (G_AttackingClan <= 0) { // Pick randomly which team starts attacking first G_AttackingClan = MathLib::Rand(1, 2); if (G_AttackingClan == 1) { // The spawns are fine like that - No need to switch sides SwitchSides = False; } } else { // Switch attacking team G_AttackingClan = 3 - G_AttackingClan; } if (SwitchSides) { // The teams are switching sides! - Switch spawn arrays declare Temp = G_ClanSpawnIds[1]; G_ClanSpawnIds[1] = G_ClanSpawnIds[2]; G_ClanSpawnIds[2] = Temp; } // Prepare spawns foreach (Order => SpawnIds in G_ClanSpawnIds) { foreach (SpawnId in SpawnIds) { declare Spawn <=> BlockSpawns[SpawnId]; // Set team color Spawn.Base.Clan = Order; } } // Prepare poles foreach (PoleId in G_BombPoleIds) { declare Pole <=> BlockPoles[PoleId]; // Set team color Pole.Base.Clan = G_AttackingClan; Pole.Gauge.Clan = G_AttackingClan; // Reset capture progress Pole.Gauge.Speed = 0; Pole.Gauge.Value = 0; } // Reset bomb carrier G_BombCarrierId = NullId; } // Updates clan scores at the top Void UpdateClanScoresSummary() { declare PlayerClan1Id = NullId; declare PlayerClan2Id = NullId; foreach (Player in Players) { if (Player.CurrentClan == 1) { if (PlayerClan1Id != NullId) continue; PlayerClan1Id = Player.Id; } if (Player.CurrentClan == 2) { if (PlayerClan2Id != NullId) continue; PlayerClan2Id = Player.Id; } if (PlayerClan1Id != NullId && PlayerClan2Id != NullId) break; } if (PlayerClan1Id == NullId || PlayerClan2Id == NullId) { UIManager.UIAll.OverlayScoreSummary = False; return; } UIManager.UIAll.OverlayScoreSummary = True; UIManager.UIAll.ScoreSummary_Player1 = PlayerClan1Id; UIManager.UIAll.ScoreSummary_Player2 = PlayerClan2Id; UIManager.UIAll.ScoreSummary_Points1 = ClanScores[1]; UIManager.UIAll.ScoreSummary_Points2 = ClanScores[2]; UIManager.UIAll.ScoreSummary_MatchPoints1 = -1; UIManager.UIAll.ScoreSummary_MatchPoints2 = -1; }