diff --git a/src/Application/Common/Resources/Messages.Designer.cs b/src/Application/Common/Resources/Messages.Designer.cs
index c6d44a0c..d59393c4 100644
--- a/src/Application/Common/Resources/Messages.Designer.cs
+++ b/src/Application/Common/Resources/Messages.Designer.cs
@@ -87,6 +87,15 @@ internal static string BetaIsWinner {
}
}
+ ///
+ /// Looks up a localized string similar to ~n~~n~~n~~b~The blue flag is not at its base position.
+ ///
+ internal static string BlueFlagIsNotAtBasePosition {
+ get {
+ return ResourceManager.GetString("BlueFlagIsNotAtBasePosition", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to {PlayerName} has had {Kills} consecutive kills without dying.
///
@@ -330,6 +339,60 @@ internal static string NoPermissions {
}
}
+ ///
+ /// Looks up a localized string similar to ~n~~n~~n~{GameText}Defend this flag from enemy capture!.
+ ///
+ internal static string OnFlagAtBasePosition {
+ get {
+ return ResourceManager.GetString("OnFlagAtBasePosition", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {PlayerName} has captured the {TeamName} team's {ColorName} flag! Keep an eye on the score!.
+ ///
+ internal static string OnFlagCaptured {
+ get {
+ return ResourceManager.GetString("OnFlagCaptured", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {PlayerName} has dropped the {TeamName} team's {ColorName} flag! Retrieve it before the enemy does!.
+ ///
+ internal static string OnFlagDropped {
+ get {
+ return ResourceManager.GetString("OnFlagDropped", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {PlayerName} has returned the {TeamName} team's {ColorName} flag to its base! Keep up the defense!.
+ ///
+ internal static string OnFlagReturned {
+ get {
+ return ResourceManager.GetString("OnFlagReturned", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {PlayerName} has brought the {ColorName} flag to the {TeamName} team's base. Point scored!.
+ ///
+ internal static string OnFlagScore {
+ get {
+ return ResourceManager.GetString("OnFlagScore", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {PlayerName} has taken the {TeamName} team's {ColorName} flag! Keep an eye on the score!.
+ ///
+ internal static string OnFlagTaken {
+ get {
+ return ResourceManager.GetString("OnFlagTaken", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Password cannot be empty.
///
@@ -402,6 +465,15 @@ internal static string RedeemedPoints {
}
}
+ ///
+ /// Looks up a localized string similar to ~n~~n~~n~~r~The red flag is not at its base position.
+ ///
+ internal static string RedFlagIsNotAtBasePosition {
+ get {
+ return ResourceManager.GetString("RedFlagIsNotAtBasePosition", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to A spawn location can only be obtained for the alpha or beta team.
///
diff --git a/src/Application/Common/Resources/Messages.resx b/src/Application/Common/Resources/Messages.resx
index 2c8d567f..83359fe2 100644
--- a/src/Application/Common/Resources/Messages.resx
+++ b/src/Application/Common/Resources/Messages.resx
@@ -126,6 +126,9 @@
This round was won by the Beta team
+
+ ~n~~n~~n~~b~The blue flag is not at its base position
+
{PlayerName} has had {Kills} consecutive kills without dying
@@ -207,6 +210,24 @@
You do not have permissions to use this command
+
+ ~n~~n~~n~{GameText}Defend this flag from enemy capture!
+
+
+ {PlayerName} has captured the {TeamName} team's {ColorName} flag! Keep an eye on the score!
+
+
+ {PlayerName} has dropped the {TeamName} team's {ColorName} flag! Retrieve it before the enemy does!
+
+
+ {PlayerName} has returned the {TeamName} team's {ColorName} flag to its base! Keep up the defense!
+
+
+ {PlayerName} has brought the {ColorName} flag to the {TeamName} team's base. Point scored!
+
+
+ {PlayerName} has taken the {TeamName} team's {ColorName} flag! Keep an eye on the score!
+
Password cannot be empty
@@ -231,6 +252,9 @@
{PlayerName} redeemed their points for the combo: {ComboName}
+
+ ~n~~n~~n~~r~The red flag is not at its base position
+
A spawn location can only be obtained for the alpha or beta team
diff --git a/src/Application/Maps/Services/MapRotationService.cs b/src/Application/Maps/Services/MapRotationService.cs
index 32d5dd2a..f04d53ef 100644
--- a/src/Application/Maps/Services/MapRotationService.cs
+++ b/src/Application/Maps/Services/MapRotationService.cs
@@ -104,8 +104,12 @@ private void OnLoadingMap()
_worldService.SendClientMessage(Color.Orange, message);
IMap nextMap = currentMap.NextMap;
_mapInfoService.Load(nextMap);
+ Team.Alpha.Flag.RemoveCarrier();
+ Team.Beta.Flag.RemoveCarrier();
+ _teamPickupService.DestroyAllPickups();
_teamPickupService.CreateFlagFromBasePosition(Team.Alpha);
_teamPickupService.CreateFlagFromBasePosition(Team.Beta);
+ _teamIconService.DestroyAll();
_teamIconService.CreateFromBasePosition(Team.Alpha);
_teamIconService.CreateFromBasePosition(Team.Beta);
_serverService.SendRconCommand($"loadfs {nextMap.Name}");
diff --git a/src/Application/Teams/Flags/Events/OnFlagAtBasePosition.cs b/src/Application/Teams/Flags/Events/OnFlagAtBasePosition.cs
new file mode 100644
index 00000000..809b7f6f
--- /dev/null
+++ b/src/Application/Teams/Flags/Events/OnFlagAtBasePosition.cs
@@ -0,0 +1,15 @@
+namespace CTF.Application.Teams.Flags.Events;
+
+///
+/// This event occurs when a player attempts to pick up their own team's flag, which is currently at the base.
+///
+public class OnFlagAtBasePosition : IFlagEvent
+{
+ public FlagStatus FlagStatus => FlagStatus.InitialPosition;
+
+ public void Handle(Team team, Player player)
+ {
+ var text = Smart.Format(Messages.OnFlagAtBasePosition, team);
+ player.GameText(text, 5000, 3);
+ }
+}
diff --git a/src/Application/Teams/Flags/Events/OnFlagCaptured.cs b/src/Application/Teams/Flags/Events/OnFlagCaptured.cs
new file mode 100644
index 00000000..1ecf56fd
--- /dev/null
+++ b/src/Application/Teams/Flags/Events/OnFlagCaptured.cs
@@ -0,0 +1,35 @@
+namespace CTF.Application.Teams.Flags.Events;
+
+///
+/// This event occurs when a player has captured the opposing team's flag from their base.
+///
+public class OnFlagCaptured(
+ IPlayerRepository playerRepository,
+ IWorldService worldService,
+ TeamPickupService teamPickupService,
+ TeamSoundsService teamSoundsService,
+ PlayerStatsRenderer playerStatsRenderer) : IFlagEvent
+{
+ public FlagStatus FlagStatus => FlagStatus.Captured;
+
+ public void Handle(Team team, Player player)
+ {
+ teamPickupService.CreatePickupWithInfo(team);
+ teamPickupService.DestroyFlag(team);
+ teamSoundsService.PlayFlagTakenSound(team);
+ var message = Smart.Format(Messages.OnFlagCaptured, new
+ {
+ PlayerName = player.Name,
+ TeamName = team.Name,
+ ColorName = team.ColorName
+ });
+ worldService.SendClientMessage(team.ColorHex, message);
+ worldService.GameText($"~n~~n~~n~{team.GameText}{team.ColorName} flag captured!", 5000, 3);
+
+ PlayerInfo playerInfo = player.GetInfo();
+ playerInfo.StatsPerRound.AddPoints(5);
+ playerInfo.AddCapturedFlags();
+ playerRepository.UpdateCapturedFlags(playerInfo);
+ playerStatsRenderer.UpdateTextDraw(player);
+ }
+}
diff --git a/src/Application/Teams/Flags/Events/OnFlagDropped.cs b/src/Application/Teams/Flags/Events/OnFlagDropped.cs
new file mode 100644
index 00000000..be070e2d
--- /dev/null
+++ b/src/Application/Teams/Flags/Events/OnFlagDropped.cs
@@ -0,0 +1,32 @@
+namespace CTF.Application.Teams.Flags.Events;
+
+///
+/// This event occurs when a player has dropped the opposing team's flag.
+///
+public class OnFlagDropped(
+ IPlayerRepository playerRepository,
+ IWorldService worldService,
+ TeamPickupService teamPickupService,
+ TeamSoundsService teamSoundsService) : IFlagEvent
+{
+ public FlagStatus FlagStatus => FlagStatus.Dropped;
+
+ public void Handle(Team team, Player player)
+ {
+ teamPickupService.CreateFlagFromVector3(team, player.Position);
+ teamSoundsService.PlayFlagDroppedSound(team);
+ team.Flag.RemoveCarrier();
+ var message = Smart.Format(Messages.OnFlagDropped, new
+ {
+ PlayerName = player.Name,
+ TeamName = team.Name,
+ ColorName = team.ColorName
+ });
+ worldService.SendClientMessage(team.ColorHex, message);
+ worldService.GameText($"~n~~n~~n~{team.GameText}{team.ColorName} flag dropped!", 5000, 3);
+
+ PlayerInfo playerInfo = player.GetInfo();
+ playerInfo.AddDroppedFlags();
+ playerRepository.UpdateDroppedFlags(playerInfo);
+ }
+}
diff --git a/src/Application/Teams/Flags/Events/OnFlagReturned.cs b/src/Application/Teams/Flags/Events/OnFlagReturned.cs
new file mode 100644
index 00000000..5b95be71
--- /dev/null
+++ b/src/Application/Teams/Flags/Events/OnFlagReturned.cs
@@ -0,0 +1,35 @@
+namespace CTF.Application.Teams.Flags.Events;
+
+///
+/// This event occurs when a player has returned the flag to their team's base.
+///
+public class OnFlagReturned(
+ IPlayerRepository playerRepository,
+ IWorldService worldService,
+ TeamPickupService teamPickupService,
+ TeamSoundsService teamSoundsService,
+ PlayerStatsRenderer playerStatsRenderer) : IFlagEvent
+{
+ public FlagStatus FlagStatus => FlagStatus.Returned;
+
+ public void Handle(Team team, Player player)
+ {
+ teamPickupService.CreateFlagFromBasePosition(team);
+ teamPickupService.DestroyPickupWithInfo(team);
+ teamSoundsService.PlayFlagReturnedSound(team);
+ var message = Smart.Format(Messages.OnFlagReturned, new
+ {
+ PlayerName = player.Name,
+ TeamName = team.Name,
+ ColorName = team.ColorName
+ });
+ worldService.SendClientMessage(team.ColorHex, message);
+ worldService.GameText($"~n~~n~~n~{team.GameText}{team.ColorName} flag returned!", 5000, 3);
+
+ PlayerInfo playerInfo = player.GetInfo();
+ playerInfo.StatsPerRound.AddPoints(5);
+ playerInfo.AddReturnedFlags();
+ playerRepository.UpdateReturnedFlags(playerInfo);
+ playerStatsRenderer.UpdateTextDraw(player);
+ }
+}
diff --git a/src/Application/Teams/Flags/Events/OnFlagScore.cs b/src/Application/Teams/Flags/Events/OnFlagScore.cs
new file mode 100644
index 00000000..470e98f3
--- /dev/null
+++ b/src/Application/Teams/Flags/Events/OnFlagScore.cs
@@ -0,0 +1,50 @@
+namespace CTF.Application.Teams.Flags.Events;
+
+///
+/// This event occurs when a player has captured the opposing team's flag and brought it back to their own base.
+///
+public class OnFlagScore(
+ IPlayerRepository playerRepository,
+ IWorldService worldService,
+ TeamPickupService teamPickupService,
+ TeamSoundsService teamSoundsService,
+ TeamTextDrawRenderer teamTextDrawRenderer,
+ PlayerStatsRenderer playerStatsRenderer) : IFlagEvent
+{
+ public FlagStatus FlagStatus => FlagStatus.Brought;
+
+ public void Handle(Team team, Player player)
+ {
+ teamPickupService.CreateFlagFromBasePosition(team.RivalTeam);
+ teamPickupService.DestroyPickupWithInfo(team.RivalTeam);
+ teamSoundsService.PlayTeamScoresSound(team);
+ teamTextDrawRenderer.UpdateTeamScore(team);
+
+ var message = Smart.Format(Messages.OnFlagScore, new
+ {
+ PlayerName = player.Name,
+ TeamName = team.Name,
+ ColorName = team.RivalTeam.ColorName
+ });
+ worldService.SendClientMessage(team.ColorHex, message);
+ worldService.GameText($"~n~~n~~n~{team.GameText}{team.ColorName} team scores!", 5000, 3);
+
+ PlayerInfo playerInfo = player.GetInfo();
+ playerInfo.StatsPerRound.AddPoints(8);
+ playerInfo.AddBroughtFlags();
+ playerRepository.UpdateBroughtFlags(playerInfo);
+ GiveRewards(team);
+ }
+
+ private void GiveRewards(Team team)
+ {
+ TeamMembers teamMembers = team.Members;
+ foreach (Player player in teamMembers)
+ {
+ PlayerInfo playerInfo = player.GetInfo();
+ playerInfo.StatsPerRound.AddPoints(5);
+ player.AddHealth(10);
+ playerStatsRenderer.UpdateTextDraw(player);
+ }
+ }
+}
diff --git a/src/Application/Teams/Flags/Events/OnFlagTaken.cs b/src/Application/Teams/Flags/Events/OnFlagTaken.cs
new file mode 100644
index 00000000..fd4c4852
--- /dev/null
+++ b/src/Application/Teams/Flags/Events/OnFlagTaken.cs
@@ -0,0 +1,26 @@
+namespace CTF.Application.Teams.Flags.Events;
+
+///
+/// This event occurs when a player has taken the flag from a position other than the base.
+///
+public class OnFlagTaken(
+ IWorldService worldService,
+ TeamPickupService teamPickupService,
+ TeamSoundsService teamSoundsService) : IFlagEvent
+{
+ public FlagStatus FlagStatus => FlagStatus.Taken;
+
+ public void Handle(Team team, Player player)
+ {
+ teamPickupService.DestroyFlag(team);
+ teamSoundsService.PlayFlagTakenSound(team);
+ var message = Smart.Format(Messages.OnFlagTaken, new
+ {
+ PlayerName = player.Name,
+ TeamName = team.Name,
+ ColorName = team.ColorName
+ });
+ worldService.SendClientMessage(team.ColorHex, message);
+ worldService.GameText($"~n~~n~~n~{team.GameText}{team.ColorName} flag taken!", 5000, 3);
+ }
+}
diff --git a/src/Application/Teams/Flags/FlagSystem.cs b/src/Application/Teams/Flags/FlagSystem.cs
new file mode 100644
index 00000000..2092b28c
--- /dev/null
+++ b/src/Application/Teams/Flags/FlagSystem.cs
@@ -0,0 +1,63 @@
+namespace CTF.Application.Teams.Flags;
+
+public class FlagSystem(
+ IDictionary flagEvents,
+ PlayerStatsRenderer playerStatsRenderer,
+ OnFlagDropped onFlagDropped) : ISystem
+{
+ [Event]
+ public void OnPlayerDisconnect(Player player, DisconnectReason reason)
+ {
+ PlayerInfo playerInfo = player.GetInfo();
+ if (playerInfo.HasCapturedFlag())
+ {
+ Team currentTeam = playerInfo.Team;
+ onFlagDropped.Handle(currentTeam.RivalTeam, player);
+ }
+ }
+
+ [Event]
+ public void OnPlayerDeath(Player deadPlayer, Player killer, Weapon reason)
+ {
+ PlayerInfo deadPlayerInfo = deadPlayer.GetInfo();
+ if (deadPlayerInfo.HasCapturedFlag())
+ {
+ Team currentTeam = deadPlayerInfo.Team;
+ onFlagDropped.Handle(currentTeam.RivalTeam, deadPlayer);
+ if (killer.IsValidPlayer())
+ {
+ PlayerInfo killerInfo = killer.GetInfo();
+ killerInfo.StatsPerRound.AddPoints(4);
+ killer.AddHealth(10);
+ playerStatsRenderer.UpdateTextDraw(killer);
+ }
+ }
+ }
+
+ [Event]
+ public void OnPlayerPickUpPickup(Player player, Pickup pickup)
+ {
+ if (pickup.Model == (int)FlagModel.Red)
+ {
+ FlagStatus flagStatus = Team.Alpha.GetFlagStatus(flagPicker: player);
+ IFlagEvent flagEvent = flagEvents[flagStatus];
+ flagEvent.Handle(Team.Alpha, player);
+ }
+ else if (pickup.Model == (int)FlagModel.Blue)
+ {
+ FlagStatus flagStatus = Team.Beta.GetFlagStatus(flagPicker: player);
+ IFlagEvent flagEvent = flagEvents[flagStatus];
+ flagEvent.Handle(Team.Beta, player);
+ }
+ else if (pickup.Model == (int)PickupInfo.Red)
+ {
+ if (player.Team == (int)TeamId.Alpha)
+ player.GameText(Messages.RedFlagIsNotAtBasePosition, 5000, 3);
+ }
+ else if (pickup.Model == (int)PickupInfo.Blue)
+ {
+ if (player.Team == (int)TeamId.Beta)
+ player.GameText(Messages.BlueFlagIsNotAtBasePosition, 5000, 3);
+ }
+ }
+}
diff --git a/src/Application/Teams/Flags/IFlagEvent.cs b/src/Application/Teams/Flags/IFlagEvent.cs
new file mode 100644
index 00000000..42945a4e
--- /dev/null
+++ b/src/Application/Teams/Flags/IFlagEvent.cs
@@ -0,0 +1,19 @@
+namespace CTF.Application.Teams.Flags;
+
+///
+/// Represents an event related to the flag in the game.
+///
+public interface IFlagEvent
+{
+ ///
+ /// Gets the current status of the flag associated with the event.
+ ///
+ FlagStatus FlagStatus { get; }
+
+ ///
+ /// Handles the event when the flag is involved, updating the game state accordingly.
+ ///
+ /// The team associated with the event.
+ /// The player who triggered the event.
+ void Handle(Team team, Player player);
+}
diff --git a/src/Application/Teams/Flags/ServiceCollectionExtensions.cs b/src/Application/Teams/Flags/ServiceCollectionExtensions.cs
new file mode 100644
index 00000000..a8f72f60
--- /dev/null
+++ b/src/Application/Teams/Flags/ServiceCollectionExtensions.cs
@@ -0,0 +1,24 @@
+namespace CTF.Application.Teams.Flags;
+
+public static class FlagServicesExtensions
+{
+ public static IServiceCollection AddFlagServices(this IServiceCollection services)
+ {
+ services
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton();
+
+ services.AddSingleton();
+ services.AddSingleton>(serviceProvider =>
+ {
+ var flagEvents = serviceProvider.GetRequiredService>();
+ return flagEvents.ToDictionary(flagEvent => flagEvent.FlagStatus, flagEvent => flagEvent);
+ });
+
+ return services;
+ }
+}
diff --git a/src/Application/Teams/PickupInfo.cs b/src/Application/Teams/PickupInfo.cs
new file mode 100644
index 00000000..ac1f86b0
--- /dev/null
+++ b/src/Application/Teams/PickupInfo.cs
@@ -0,0 +1,10 @@
+namespace CTF.Application.Teams;
+
+///
+/// See
+///
+public enum PickupInfo
+{
+ Red = 19605,
+ Blue = 19607
+}
diff --git a/src/Application/Teams/ServiceCollectionExtensions.cs b/src/Application/Teams/ServiceCollectionExtensions.cs
index 4694f127..05e9b727 100644
--- a/src/Application/Teams/ServiceCollectionExtensions.cs
+++ b/src/Application/Teams/ServiceCollectionExtensions.cs
@@ -7,8 +7,10 @@ public static IServiceCollection AddTeamServices(this IServiceCollection service
services
.AddSingleton()
.AddSingleton()
+ .AddSingleton()
.AddSingleton()
- .AddSingleton();
+ .AddSingleton()
+ .AddFlagServices();
return services;
}
diff --git a/src/Application/Teams/Services/TeamPickupService.cs b/src/Application/Teams/Services/TeamPickupService.cs
index 8396d208..254a4c84 100644
--- a/src/Application/Teams/Services/TeamPickupService.cs
+++ b/src/Application/Teams/Services/TeamPickupService.cs
@@ -82,7 +82,7 @@ public void CreatePickupWithInfo(Team team)
if (team.Id == TeamId.Alpha)
{
_alphaPickupInfo = _worldService.CreatePickup(
- model: 1239,
+ model: (int)PickupInfo.Red,
type: PickupType.ScriptedActionsOnlyEveryFewSeconds,
position: currentMap.FlagLocations.Red
);
@@ -90,7 +90,7 @@ public void CreatePickupWithInfo(Team team)
else if(team.Id == TeamId.Beta)
{
_betaPickupInfo = _worldService.CreatePickup(
- model: 1239,
+ model: (int)PickupInfo.Blue,
type: PickupType.ScriptedActionsOnlyEveryFewSeconds,
position: currentMap.FlagLocations.Blue
);
diff --git a/src/Application/Teams/Services/TeamSoundsService.cs b/src/Application/Teams/Services/TeamSoundsService.cs
new file mode 100644
index 00000000..00a7e405
--- /dev/null
+++ b/src/Application/Teams/Services/TeamSoundsService.cs
@@ -0,0 +1,35 @@
+namespace CTF.Application.Teams.Services;
+
+public class TeamSoundsService
+{
+ ///
+ /// Plays the sound when the team's flag is taken.
+ ///
+ public void PlayFlagTakenSound(Team team)
+ => PlayAudioStreamToAll(team.Sounds.FlagTaken);
+
+ ///
+ /// Plays the sound when the team's flag is dropped.
+ ///
+ public void PlayFlagDroppedSound(Team team)
+ => PlayAudioStreamToAll(team.Sounds.FlagDropped);
+
+ ///
+ /// Plays the sound when the team's flag is returned.
+ ///
+ public void PlayFlagReturnedSound(Team team)
+ => PlayAudioStreamToAll(team.Sounds.FlagReturned);
+
+ ///
+ /// Plays the sound when the team scores.
+ ///
+ public void PlayTeamScoresSound(Team team)
+ => PlayAudioStreamToAll(team.Sounds.TeamScores);
+
+ private void PlayAudioStreamToAll(string url)
+ {
+ IEnumerable players = AlphaBetaTeamPlayers.GetAll();
+ foreach (Player player in players)
+ player.PlayAudioStream(url);
+ }
+}
diff --git a/src/Application/Teams/Team.cs b/src/Application/Teams/Team.cs
index dfe1e2b9..7b1ead88 100644
--- a/src/Application/Teams/Team.cs
+++ b/src/Application/Teams/Team.cs
@@ -13,7 +13,7 @@ static Team()
Id = TeamId.Alpha,
SkinId = SkinTeamId.Alpha,
Name = "Alpha",
- ColorName = "Red",
+ ColorName = "red",
GameText = "~r~",
ColorHex = new Color(255, 32, 64, 00),
Sounds = TeamSounds.Alpha,
@@ -31,7 +31,7 @@ static Team()
Id = TeamId.Beta,
SkinId = SkinTeamId.Beta,
Name = "Beta",
- ColorName = "Blue",
+ ColorName = "blue",
GameText = "~b~",
ColorHex = new Color(0, 136, 255, 00),
Sounds = TeamSounds.Beta,
@@ -51,7 +51,7 @@ static Team()
Id = TeamId.NoTeam,
SkinId = SkinTeamId.NoTeam,
Name = "NoTeam",
- ColorName = "White",
+ ColorName = "white",
GameText = "~w~",
ColorHex = new Color(255, 255, 255, 00),
Sounds = TeamSounds.None,
diff --git a/src/Application/Usings.cs b/src/Application/Usings.cs
index ea957b85..8e649a92 100644
--- a/src/Application/Usings.cs
+++ b/src/Application/Usings.cs
@@ -29,6 +29,7 @@
global using CTF.Application.Teams;
global using CTF.Application.Teams.ClassSelection;
global using CTF.Application.Teams.Flags;
+global using CTF.Application.Teams.Flags.Events;
global using CTF.Application.Teams.Services;
global using CTF.Application.Maps;
global using CTF.Application.Maps.Services;
\ No newline at end of file