diff --git a/ChatCommands/CustomGamemode.cs b/ChatCommands/CustomGamemode.cs index a7e6294..ac90ac5 100644 --- a/ChatCommands/CustomGamemode.cs +++ b/ChatCommands/CustomGamemode.cs @@ -47,7 +47,7 @@ public CustomGamemode() : base( } var customGamemodeToAdd = string.Join(" ", args).ToUpperInvariant(); - if (CustomGamemodeHelper.isValidCustomGamemode(customGamemodeToAdd)) + if (CustomGamemodeHelper.IsValidCustomGamemode(customGamemodeToAdd)) { CustomGamemodeHelper.SetCustomGameMode(customGamemodeToAdd, Server); diff --git a/Data/CustomGameModeEvents.cs b/Data/CustomGameModeEvents.cs index 86f0f73..920ef97 100644 --- a/Data/CustomGameModeEvents.cs +++ b/Data/CustomGameModeEvents.cs @@ -14,7 +14,8 @@ public static class CustomGameModeEvents public static readonly List VipEvents = new() { - new VipGamemode() + new VipGamemode(), + new RegionManager() }; public static readonly List HideAndSeekEvents = new() diff --git a/Data/RegionList.cs b/Data/RegionList.cs index 70b0926..3c19876 100644 --- a/Data/RegionList.cs +++ b/Data/RegionList.cs @@ -15,6 +15,11 @@ public class RegionList LonovoRegions.TeamASafeZone, LonovoRegions.TeamBSafeZone }, + "WineParadise" => new List + { + WineparadiseRegions.TeamASafeZone, + WineparadiseRegions.TeamBSafeZone + }, _ => new List() }; } diff --git a/Data/Regions/WineparadiseRegions.cs b/Data/Regions/WineparadiseRegions.cs new file mode 100644 index 0000000..b5beff8 --- /dev/null +++ b/Data/Regions/WineparadiseRegions.cs @@ -0,0 +1,42 @@ +using System.Numerics; +using BattleBitMinigames.Handlers; + +namespace BattleBitMinigames.Data.Regions; + +public class WineparadiseRegions +{ + // US safe zone + public static readonly RegionHelper.Region TeamASafeZone = new ( + "US Safe Zone", + "You've entered the US Safe Zone. Please leave immediately.", + new List + { + new (-92, -271), + new (-197, -142), + new (-338, -18), + new (-387, 190), + new (-605, -27), + new (-660, -275), + new (-510, -372), + new (-297, -352) + } + ); + + // RU safe zone region + public static readonly RegionHelper.Region? TeamBSafeZone = new ( + "RU Safe Zone", + "You've entered the RU Safe Zone. Please leave immediately.", + new List + { + new (-121, 397), + new (0, 346), + new (140, 182), + new (313, 90), + new (353, 124), + new (380, 289), + new (332, 455), + new (160, 526), + new (-54, 455) + } + ); +} \ No newline at end of file diff --git a/Events/RegionManager.cs b/Events/RegionManager.cs index a21c6b3..3939550 100644 --- a/Events/RegionManager.cs +++ b/Events/RegionManager.cs @@ -1,39 +1,123 @@ -using System.Numerics; +using System.Globalization; +using System.Numerics; +using BattleBitAPI.Common; using BattleBitMinigames.Api; using BattleBitMinigames.Data; using BattleBitMinigames.Handlers; -using BattleBitMinigames.Helpers; +using BattleBitMinigames.Interfaces; namespace BattleBitMinigames.Events; - public class RegionManager : Event { - bool Enabled = true; + private CancellationTokenSource? _cancellationTokenSource; private void StartRegionManager() { + Program.Logger.Info("Started RegionManager!"); + _cancellationTokenSource = new CancellationTokenSource(); + var cancellationToken = _cancellationTokenSource.Token; + Task.Run(async () => { - while (Server.IsConnected) + while (Server.IsConnected && !cancellationToken.IsCancellationRequested) { foreach (var player in Server.AllPlayers.Where(player => player.IsAlive && player.Position != Vector3.Zero)) { var region = RegionHelper.GetIsPlayerInRegion(RegionList.GetMapRegions(Server.Map), player); if (region != null) { - // TODO: Remove in prod - player.Message($"You are in {RichTextHelper.Bold(true)}{region.Name}{RichTextHelper.Bold(false)}!", 999); - // TODO: Spawn task to kill player after X amount of time, make message prettier. - } + var now = DateTime.UtcNow; + var spawnedInSpawn = player.GetPlayerProperty(PlayerProperties.IVipPlayerProperties.SpawnedInSpawn); + var spawnedInSpawnTimeStr = player.GetPlayerProperty(PlayerProperties.IVipPlayerProperties.SpawnedInSpawnTime); + var enteredSpawnTimeStr = player.GetPlayerProperty(PlayerProperties.IVipPlayerProperties.EnteredSpawnTime); + + if (spawnedInSpawn == "true") + { + if (DateTime.TryParse(spawnedInSpawnTimeStr, out var spawnedInSpawnTime)) + { + if ((now - spawnedInSpawnTime).TotalSeconds >= 90) + { + player.Kill(); + player.Message($"You spawned in the {region.Name} and stayed for too long!", 15); + } + else + { + player.Message($"You spawned in the {region.Name}, please leave within {(int)(90 - (now - spawnedInSpawnTime).TotalSeconds)} seconds!"); + } + } + } + else + { + if (enteredSpawnTimeStr == string.Empty) + { + player.SetPlayerProperty(PlayerProperties.IVipPlayerProperties.EnteredSpawnTime, now.ToUniversalTime().ToString()); + } + else if (DateTime.TryParse(enteredSpawnTimeStr, out var enteredSpawnTime)) + { + if ((now - enteredSpawnTime).TotalSeconds >= 20) + { + player.Kill(); + player.Message($"You were in the {region.Name} for too long!", 15); + } + else + { + player.Message($"You entered the {region.Name}, please leave within {(int)(20 - (now - enteredSpawnTime).TotalSeconds)} seconds!"); + } + } + } + } else { - player.Message("You are not in a region!", 1); + // Reset properties when a player leaves the spawn region + player.RemovePlayerProperty(PlayerProperties.IVipPlayerProperties.SpawnedInSpawn); + player.RemovePlayerProperty(PlayerProperties.IVipPlayerProperties.EnteredSpawnTime); + player.RemovePlayerProperty(PlayerProperties.IVipPlayerProperties.SpawnedInSpawnTime); } } - await Task.Delay(1000); + await Task.Delay(1000, cancellationToken); + } + + Program.Logger.Info("RegionManager stopped."); + }, cancellationToken); + } + + private void StopRegionManager() + { + if (_cancellationTokenSource is { IsCancellationRequested: false }) + { + _cancellationTokenSource.Cancel(); + Program.Logger.Info("Stopping RegionManager..."); + } + } + + public override Task OnPlayerTypedMessage(BattleBitPlayer player, ChatChannel channel, string msg) + { + if (player.GetHighestRole() == Enums.PlayerRoles.Admin) + { + if (msg.Contains("?rgm start")) + { + StartRegionManager(); + } + else if (msg.Contains("?rgm stop")) + { + StopRegionManager(); } - }); + } + + return base.OnPlayerTypedMessage(player, channel, msg); + } + + public override Task OnPlayerSpawned(BattleBitPlayer player) + { + var region = RegionHelper.GetIsPlayerInRegion(RegionList.GetMapRegions(Server.Map), player); + if (region != null) + { + player.SetPlayerProperty(PlayerProperties.IVipPlayerProperties.SpawnedInSpawn, "true"); + player.SetPlayerProperty(PlayerProperties.IVipPlayerProperties.SpawnedInSpawnTime, DateTime.UtcNow.ToString()); + } + + return base.OnPlayerSpawned(player); } public override Task OnConnected() @@ -41,4 +125,4 @@ public override Task OnConnected() StartRegionManager(); return Task.CompletedTask; } -} \ No newline at end of file +} diff --git a/Events/VipGamemode.cs b/Events/VipGamemode.cs index 670c7c9..1fbd091 100644 --- a/Events/VipGamemode.cs +++ b/Events/VipGamemode.cs @@ -8,23 +8,28 @@ namespace BattleBitMinigames.Events; public class VipGamemode : Event { private Random _random = new(); - private BattleBitPlayer? TeamAVip; - private BattleBitPlayer? TeamBVip; + private BattleBitPlayer? _teamAVip; + private BattleBitPlayer? _teamBVip; private bool IsPlayerVip(BattleBitPlayer player) { - return player == TeamAVip || player == TeamBVip; + return player == _teamAVip || player == _teamBVip; } private void SetVipSettings(BattleBitPlayer player) { - if (player.Team == Team.TeamA) - { - TeamAVip = player; - } - else if (player.Team == Team.TeamB) + switch (player.Team) { - TeamBVip = player; + case Team.TeamA: + _teamAVip = player; + break; + case Team.TeamB: + _teamBVip = player; + break; + case Team.None: + break; + default: + throw new ArgumentOutOfRangeException(nameof(player)); } player.Message($"{RichTextHelper.FromColorName("Gold")}You are the VIP!{RichTextHelper.NewLine()}{RichTextHelper.FromColorName("Orange")}You have reduced damage and fall damage, and you are exposed on the map. {RichTextHelper.FromColorName("Red")}Don't die{RichTextHelper.Color()}!", 10); @@ -37,7 +42,7 @@ private void SetVipSettings(BattleBitPlayer player) player.JoinSquad(Squads.King); } - private void SetNonVipSettings(BattleBitPlayer player) + private static void SetNonVipSettings(BattleBitPlayer player) { player.Modifications.IsExposedOnMap = false; player.Modifications.FallDamageMultiplier = 1f; @@ -47,21 +52,21 @@ private void SetNonVipSettings(BattleBitPlayer player) // TODO: Implement function to increment player point contribution using player properties - public override async Task OnPlayerSpawning(BattleBitPlayer player, + public override Task OnPlayerSpawning(BattleBitPlayer player, OnPlayerSpawnArguments request) { if (request.Loadout.HeavyGadget.Name.ToLower().Contains("rpg") || request.Loadout.LightGadget.Name.ToLower().Contains("c4") || request.Loadout.LightGadget.Name.ToLower().Contains("claymore") || request.Loadout.Throwable.Name.ToLower().Contains("impact") || request.Loadout.Throwable.Name.ToLower().Contains("frag")) { player.SayToChat($"{RichTextHelper.FromColorName("Orange")}You can't use explosives in this gamemode."); player.Message($"{RichTextHelper.FromColorName("Orange")}You can't use explosives in this gamemode.", 10); - return null; + return Task.FromResult(null); } switch (player.Team) { case Team.TeamA: { - if (TeamAVip == null) + if (_teamAVip == null) { SetVipSettings(player); request.Wearings = PlayerOutfits.BlueTeam; @@ -86,7 +91,7 @@ private void SetNonVipSettings(BattleBitPlayer player) } case Team.TeamB: { - if (TeamBVip == null) + if (_teamBVip == null) { SetVipSettings(player); request.Wearings = PlayerOutfits.RedTeam; @@ -114,7 +119,7 @@ private void SetNonVipSettings(BattleBitPlayer player) break; } - return request; + return Task.FromResult(request); } public override Task OnGameStateChanged(GameState oldState, GameState newState) @@ -141,20 +146,25 @@ public override Task OnGameStateChanged(GameState oldState, GameState newState) public override Task OnPlayerDied(BattleBitPlayer player) { - if (IsPlayerVip(player)) + if (!IsPlayerVip(player)) return Task.CompletedTask; + switch (player.Team) { - if (player.Team == Team.TeamA) - { + case Team.TeamA: Server.SayToAllChat($"The VIP {RichTextHelper.FromColorName("Gold")}{player.Name}{RichTextHelper.Color()} took the easy way out (suicided)! {RichTextHelper.FromColorName("RoyalBlue")}US{RichTextHelper.Color()} has lost {RichTextHelper.FromColorName("Crimson")}20 tickets{RichTextHelper.Color()}."); - TeamAVip = null; - } - else if (player.Team == Team.TeamB) - { + Server.RoundSettings.TeamATickets -= 20; + _teamAVip = null; + break; + case Team.TeamB: Server.SayToAllChat($"The VIP {RichTextHelper.FromColorName("Gold")}{player.Name}{RichTextHelper.Color()} took the easy way out (suicided)! {RichTextHelper.FromColorName("Red")}RU{RichTextHelper.Color()} has lost {RichTextHelper.FromColorName("Crimson")}20 tickets{RichTextHelper.Color()}."); - TeamBVip = null; - } + Server.RoundSettings.TeamBTickets -= 20; + _teamBVip = null; + break; + case Team.None: + break; + default: + throw new ArgumentOutOfRangeException(nameof(player)); } - + return Task.CompletedTask; } @@ -166,47 +176,44 @@ public override Task OnAPlayerDownedAnotherPlayer(OnPlayerKillArguments Admins = new() { 76561198395073327, - 76561198035784951 + 76561198035784951, + 76561198855127416 }; public static List Moderators = new() diff --git a/Interfaces/PlayerProperties.cs b/Interfaces/PlayerProperties.cs index 5182ff1..9254800 100644 --- a/Interfaces/PlayerProperties.cs +++ b/Interfaces/PlayerProperties.cs @@ -33,4 +33,14 @@ public interface IInfectedPlayerProperties public const string KillsAsInfected = "infected_kills_as_infected"; public const string KillsAsHuman = "infected_kills_as_human"; } + + /// + /// VIP player properties + /// + public interface IVipPlayerProperties + { + public const string SpawnedInSpawn = "spawned_in_spawn"; + public const string SpawnedInSpawnTime = "spawned_in_spawn_time"; + public const string EnteredSpawnTime = "entered_spawn_time"; + } } \ No newline at end of file diff --git a/Program.cs b/Program.cs index 73b4e03..dd0906e 100644 --- a/Program.cs +++ b/Program.cs @@ -33,17 +33,18 @@ private static void Main() AllowTrailingCommas = true }; - private async void StartApi() + private void StartApi() { try { - SetupLogger(); + Logger = SetupLogger(); LoadConfiguration(); ValidateConfiguration(); StartServerListener(); } catch (Exception ex) { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract if (Logger == null) { Console.WriteLine("Failed to initialize logger" + Environment.NewLine + ex); @@ -67,12 +68,12 @@ private async void StartApi() } } - private void SetupLogger() + private static ILog SetupLogger() { - string log4netConfig = "log4net.config"; - if (!File.Exists(log4netConfig)) + const string log4NetConfig = "log4net.config"; + if (!File.Exists(log4NetConfig)) { - File.WriteAllText(log4netConfig, @" + File.WriteAllText(log4NetConfig, @" @@ -105,7 +106,7 @@ private void SetupLogger() try { Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - XmlConfigurator.Configure(new FileInfo(log4netConfig)); + XmlConfigurator.Configure(new FileInfo(log4NetConfig)); } catch (Exception ex) { @@ -115,8 +116,7 @@ private void SetupLogger() try { - Logger = LogManager.GetLogger("API"); - Logger.Info("Logger initialized."); + return LogManager.GetLogger("API"); } catch (Exception ex) { @@ -125,7 +125,7 @@ private void SetupLogger() } } - private void LoadConfiguration() + private static void LoadConfiguration() { if (!File.Exists("appsettings.json")) { @@ -146,14 +146,14 @@ public static void SaveConfiguration(Configuration.ServerConfiguration serverCon public static void ReloadConfiguration() { - foreach (var Map in Server.MapRotation.GetMapRotation()) + foreach (var map in Server.MapRotation.GetMapRotation()) { - Server.MapRotation.RemoveFromRotation(Map); + Server.MapRotation.RemoveFromRotation(map); } - foreach (var Gamemode in Server.GamemodeRotation.GetGamemodeRotation()) + foreach (var gamemode in Server.GamemodeRotation.GetGamemodeRotation()) { - Server.GamemodeRotation.RemoveFromRotation(Gamemode); + Server.GamemodeRotation.RemoveFromRotation(gamemode); } if (!ServerConfiguration.MapRotation.Any()) @@ -168,14 +168,14 @@ public static void ReloadConfiguration() SaveConfiguration(ServerConfiguration); } - foreach (var Map in ServerConfiguration.MapRotation) + foreach (var map in ServerConfiguration.MapRotation) { - Server.MapRotation.AddToRotation(Map); + Server.MapRotation.AddToRotation(map); } - foreach (var Gamemode in ServerConfiguration.GamemodeRotation) + foreach (var gamemode in ServerConfiguration.GamemodeRotation) { - Server.GamemodeRotation.AddToRotation(Gamemode); + Server.GamemodeRotation.AddToRotation(gamemode); } new ConfigurationBuilder() @@ -185,13 +185,13 @@ public static void ReloadConfiguration() .Bind(ServerConfiguration); } - private void ValidateConfiguration() + private static void ValidateConfiguration() { List validationResults = new(); IPAddress? ipAddress = null; - bool isValid = Validator.TryValidateObject(ServerConfiguration, new ValidationContext(ServerConfiguration), validationResults, true) - && IPAddress.TryParse(ServerConfiguration.IP, out ipAddress); + var isValid = Validator.TryValidateObject(ServerConfiguration, new ValidationContext(ServerConfiguration), validationResults, true) + && IPAddress.TryParse(ServerConfiguration.IP, out ipAddress); if (ServerConfiguration.Password == "") { @@ -206,7 +206,7 @@ private void ValidateConfiguration() errorMessages = errorMessages.Append($"Invalid IP address: {ServerConfiguration.IP}"); } - string errorString = $"Invalid configuration:{Environment.NewLine}{string.Join(Environment.NewLine, errorMessages)}"; + var errorString = $"Invalid configuration:{Environment.NewLine}{string.Join(Environment.NewLine, errorMessages)}"; throw new ValidationException(errorString); } @@ -264,7 +264,7 @@ private static async Task OnGameServerConnected(GameServer serv if (ServerConfiguration.Password != string.Empty) Server.ExecuteCommand("setpass " + ServerConfiguration.Password); - if (ServerConfiguration.LaunchCustomGamemode != string.Empty) + if (ServerConfiguration.LaunchCustomGamemode != string.Empty && Server.RoundSettings.State != GameState.EndingGame) { CustomGamemodeHelper.SetCustomGameMode(ServerConfiguration.LaunchCustomGamemode, Server); }