diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index e15cf39c..636085c6 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -27,7 +27,7 @@ jobs:
- uses: actions/upload-artifact@v4.0.0
with:
- name: jailbreak-nightly-${{ github.run_id }}
+ name: jailbreak-nightly
path: build
# If build didn't put any artifacts in the build folder, consider it an error
if-no-files-found: error
diff --git a/Jailbreak.sln b/Jailbreak.sln
index 7dfd86e9..9570efb8 100644
--- a/Jailbreak.sln
+++ b/Jailbreak.sln
@@ -16,6 +16,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jailbreak.Generic", "src\Ja
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jailbreak.Teams", "mod\Jailbreak.Teams\Jailbreak.Teams.csproj", "{28EE05E4-8FE3-4CC6-AA03-0C533EFBFBF2}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jailbreak.Formatting", "public\Jailbreak.Formatting\Jailbreak.Formatting.csproj", "{446E0B6F-E4FE-45E6-BD9B-BD943698327A}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lang", "lang", "{CDCDE44E-01D2-4B76-99DA-A57E1E956038}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jailbreak.English", "lang\Jailbreak.English\Jailbreak.English.csproj", "{FC2D6F50-BCFF-41E6-A965-6C73CC01C3BF}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -42,6 +48,14 @@ Global
{28EE05E4-8FE3-4CC6-AA03-0C533EFBFBF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28EE05E4-8FE3-4CC6-AA03-0C533EFBFBF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28EE05E4-8FE3-4CC6-AA03-0C533EFBFBF2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {446E0B6F-E4FE-45E6-BD9B-BD943698327A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {446E0B6F-E4FE-45E6-BD9B-BD943698327A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {446E0B6F-E4FE-45E6-BD9B-BD943698327A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {446E0B6F-E4FE-45E6-BD9B-BD943698327A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {FC2D6F50-BCFF-41E6-A965-6C73CC01C3BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {FC2D6F50-BCFF-41E6-A965-6C73CC01C3BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {FC2D6F50-BCFF-41E6-A965-6C73CC01C3BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {FC2D6F50-BCFF-41E6-A965-6C73CC01C3BF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{9135CCC9-66C5-4A9C-AE3C-91475B5F0437} = {177DA48D-8306-4102-918D-992569878581}
@@ -49,5 +63,7 @@ Global
{E73417E7-D397-415B-B96F-BF43DAC511B8} = {36BA84C0-291C-4930-A7C6-97CDF8F7F0D7}
{1D9CC1AC-4ABC-41A3-8CC7-6FB17E864C3F} = {177DA48D-8306-4102-918D-992569878581}
{28EE05E4-8FE3-4CC6-AA03-0C533EFBFBF2} = {36BA84C0-291C-4930-A7C6-97CDF8F7F0D7}
+ {446E0B6F-E4FE-45E6-BD9B-BD943698327A} = {59311734-3648-43C2-B43C-385718B0D103}
+ {FC2D6F50-BCFF-41E6-A965-6C73CC01C3BF} = {CDCDE44E-01D2-4B76-99DA-A57E1E956038}
EndGlobalSection
EndGlobal
diff --git a/lang/Jailbreak.English/Jailbreak.English.csproj b/lang/Jailbreak.English/Jailbreak.English.csproj
new file mode 100644
index 00000000..883695e7
--- /dev/null
+++ b/lang/Jailbreak.English/Jailbreak.English.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/lang/Jailbreak.English/Teams/RatioNotifications.cs b/lang/Jailbreak.English/Teams/RatioNotifications.cs
new file mode 100644
index 00000000..6ed87799
--- /dev/null
+++ b/lang/Jailbreak.English/Teams/RatioNotifications.cs
@@ -0,0 +1,46 @@
+using CounterStrikeSharp.API.Modules.Utils;
+
+using Jailbreak.Formatting.Base;
+using Jailbreak.Formatting.Core;
+using Jailbreak.Formatting.Logistics;
+using Jailbreak.Formatting.Objects;
+using Jailbreak.Formatting.Views;
+
+namespace Jailbreak.English.Teams;
+
+public class RatioNotifications : IRatioNotifications, ILanguage
+{
+ public static FormatObject PREFIX = new HiddenFormatObject( $" {ChatColors.LightRed}[{ChatColors.Red}JB{ChatColors.LightRed}]" )
+ {
+ // Hide in panorama and center text
+ Plain = false,
+ Panorama = false,
+ Chat = true,
+ };
+
+ public IView NOT_ENOUGH_GUARDS => new SimpleView(writer =>
+ writer
+ .Line(PREFIX, "There's not enough guards in the queue!"));
+
+ public IView JOIN_GUARD_QUEUE => new SimpleView(writer =>
+ writer
+ .Line(PREFIX, "Type !guard to become a guard!"));
+
+ public IView YOU_WERE_AUTOBALANCED_PRISONER => new SimpleView(writer =>
+ writer
+ .Line(PREFIX, "You were autobalanced to the prisoner team!"));
+
+ public IView ATTEMPT_TO_JOIN_FROM_TEAM_MENU => new SimpleView(writer =>
+ writer
+ .Line(PREFIX, "You were swapped back to the prisoner team!")
+ .Line(PREFIX, "Please use !guard to join the guard team."));
+
+ public IView LEFT_GUARD => new SimpleView(writer =>
+ writer
+ .Line(PREFIX, "You are no longer a guard.")
+ .Line(PREFIX, "Please use !guard if you want to re-join the guard team."));
+
+ public IView YOU_WERE_AUTOBALANCED_GUARD => new SimpleView(writer =>
+ writer
+ .Line(PREFIX, "You are now a guard!"));
+}
diff --git a/lang/Jailbreak.English/Warden/WardenNotifications.cs b/lang/Jailbreak.English/Warden/WardenNotifications.cs
new file mode 100644
index 00000000..332120b5
--- /dev/null
+++ b/lang/Jailbreak.English/Warden/WardenNotifications.cs
@@ -0,0 +1,67 @@
+using CounterStrikeSharp.API.Core;
+using CounterStrikeSharp.API.Modules.Utils;
+
+using Jailbreak.Formatting.Base;
+using Jailbreak.Formatting.Core;
+using Jailbreak.Formatting.Logistics;
+using Jailbreak.Formatting.Objects;
+using Jailbreak.Formatting.Views;
+
+namespace Jailbreak.English.Warden;
+
+public class WardenNotifications : IWardenNotifications, ILanguage
+{
+ public static FormatObject PREFIX = new HiddenFormatObject( $" {ChatColors.Lime}[{ChatColors.Green}WARDEN{ChatColors.Lime}]" )
+ {
+ // Hide in panorama and center text
+ Plain = false,
+ Panorama = false,
+ Chat = true,
+ };
+
+ public IView PICKING_SHORTLY => new SimpleView(writer =>
+ writer
+ .Line(PREFIX, "Picking a warden shortly")
+ .Line(PREFIX, "To enter the warden queue, type !warden in chat."));
+
+ public IView NO_WARDENS => new SimpleView(writer =>
+ writer
+ .Line(PREFIX, "No wardens in queue! The next player to run !warden will become a warden."));
+
+ public IView WARDEN_LEFT => new SimpleView(writer =>
+ writer.Line(PREFIX, "The warden has left the game!"));
+
+ public IView WARDEN_DIED => new SimpleView(writer =>
+ writer.Line(PREFIX, "The warden has died!"));
+
+ public IView BECOME_NEXT_WARDEN => new SimpleView(writer =>
+ writer.Line(PREFIX, "Type !warden to become the next warden"));
+
+ public IView JOIN_RAFFLE => new SimpleView(writer =>
+ writer.Line(PREFIX, "You've joined the warden raffle!"));
+
+ public IView LEAVE_RAFFLE => new SimpleView(writer =>
+ writer.Line(PREFIX, "You've left the warden raffle!"));
+
+ public IView PASS_WARDEN(CCSPlayerController player)
+ {
+ return new SimpleView(writer =>
+ writer.Line(PREFIX, player, "has resigned from being warden!"));
+ }
+
+ public IView NEW_WARDEN(CCSPlayerController player)
+ {
+ return new SimpleView(writer =>
+ writer.Line(PREFIX, player, "is now the warden!"));
+ }
+
+ public IView CURRENT_WARDEN(CCSPlayerController? player)
+ {
+ if (player is not null)
+ return new SimpleView(writer =>
+ writer.Line(PREFIX, "The current warden is", player));
+ else
+ return new SimpleView(writer =>
+ writer.Line(PREFIX, "There is currently no warden!"));
+ }
+}
diff --git a/mod/Jailbreak.Teams/Jailbreak.Teams.csproj b/mod/Jailbreak.Teams/Jailbreak.Teams.csproj
index 29f7e134..7f2b8a0c 100644
--- a/mod/Jailbreak.Teams/Jailbreak.Teams.csproj
+++ b/mod/Jailbreak.Teams/Jailbreak.Teams.csproj
@@ -7,7 +7,8 @@
-
+
+
-
+
diff --git a/mod/Jailbreak.Teams/Queue/QueueBehavior.cs b/mod/Jailbreak.Teams/Queue/QueueBehavior.cs
index 6acdbeea..c5b55372 100644
--- a/mod/Jailbreak.Teams/Queue/QueueBehavior.cs
+++ b/mod/Jailbreak.Teams/Queue/QueueBehavior.cs
@@ -1,8 +1,11 @@
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration;
+using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Utils;
+using Jailbreak.Formatting.Extensions;
+using Jailbreak.Formatting.Views;
using Jailbreak.Public.Behaviors;
using Jailbreak.Public.Extensions;
using Jailbreak.Public.Generic;
@@ -18,9 +21,12 @@ public class QueueBehavior : IGuardQueue, IPluginBehavior
private readonly ILogger _logger;
private readonly IPlayerState _state;
- public QueueBehavior(IPlayerStateFactory factory, ILogger logger)
+ private IRatioNotifications _notifications;
+
+ public QueueBehavior(IPlayerStateFactory factory, ILogger logger, IRatioNotifications notifications)
{
- _logger = logger;
+ _logger = logger;
+ _notifications = notifications;
_counter = 0;
_state = factory.Global();
}
@@ -48,14 +54,12 @@ public bool TryExitQueue(CCSPlayerController player)
public bool TryPop(int count)
{
- Server.PrintToChatAll($"[Jail] Autobalancing is adding {count} guards.");
var queue = Queue.ToList();
if (queue.Count <= count)
{
- Server.PrintToChatAll("[Jail] Not enough guards are in the queue!");
- Server.PrintToChatAll("[Jail] Type !guard in chat to join the queue");
- ServerExtensions.PrintToCenterAll("Not enough players in guard queue!\nType !guard to become a guard.");
+ _notifications.NOT_ENOUGH_GUARDS.ToAllChat();
+ _notifications.JOIN_GUARD_QUEUE.ToAllChat().ToAllCenter();
}
_logger.LogInformation("[Queue] Pop requested {@Count} out of {@InQueue}", count, queue.Count);
@@ -72,8 +76,6 @@ public bool TryPop(int count)
public bool TryPush(int count)
{
- Server.PrintToChatAll($"[Jail] Autobalancing is removing {count} guards.");
-
var players = Utilities.GetPlayers()
.Where(player => player.GetTeam() == CsTeam.CounterTerrorist)
.Shuffle(Random.Shared)
@@ -91,7 +93,7 @@ public bool TryPush(int count)
TryEnterQueue(toSwap);
- toSwap.PrintToCenter("You were autobalanced to the prisoner team!");
+ _notifications.YOU_WERE_AUTOBALANCED_PRISONER.ToPlayerCenter(toSwap);
}
return true;
@@ -102,7 +104,10 @@ public void ForceGuard(CCSPlayerController player)
// Set IsGuard so they won't be swapped back.
_state.Get(player).IsGuard = true;
- player.PrintToCenter("You are now a guard!");
+ _notifications.YOU_WERE_AUTOBALANCED_GUARD
+ .ToPlayerChat(player)
+ .ToPlayerCenter(player);
+
player.ChangeTeam(CsTeam.CounterTerrorist);
}
@@ -129,16 +134,72 @@ public HookResult OnPlayerTeam(EventPlayerTeam ev, GameEventInfo info)
if (ev.Team == (int)CsTeam.CounterTerrorist && !state.IsGuard)
{
- player.SwitchTeam(CsTeam.Terrorist);
- player.PrintToCenter("You were swapped to T!\nUse !guard to join the queue.");
-
return HookResult.Handled;
}
if (player.GetTeam() == CsTeam.Terrorist && state.IsGuard)
- if (TryExitQueue(player))
- player.PrintToCenter("You were removed from the guard queue for switching to T.\nUse !guard to rejoin the queue!");
+ {
+ if (this.TryExitQueue(player))
+ _notifications.LEFT_GUARD
+ .ToPlayerCenter(player)
+ .ToPlayerChat(player);
+ }
return HookResult.Continue;
}
+
+ private void HandleQueueRequest(CCSPlayerController player)
+ {
+ if (TryEnterQueue(player))
+ _notifications.JOIN_GUARD_QUEUE
+ .ToPlayerCenter(player)
+ .ToPlayerChat(player);
+ else
+ player.PrintToCenter("An error occured adding you to the queue.");
+
+ }
+
+ private void HandleLeaveRequest(CCSPlayerController player)
+ {
+ if (TryExitQueue(player))
+ _notifications.LEFT_GUARD
+ .ToPlayerCenter(player)
+ .ToPlayerChat(player);
+ else
+ player.PrintToCenter("An error occured removing you from the queue.");
+ }
+
+ public int GetQueuePosition(CCSPlayerController player)
+ {
+ return Queue.ToList()
+ .FindIndex(controller => controller.Slot == player.Slot);
+ }
+
+ [ConsoleCommand("css_guard", "Joins the guard queue")]
+ [ConsoleCommand("css_g", "Joins the guard queue")]
+ [CommandHelper(0, "", CommandUsage.CLIENT_ONLY)]
+ public void Command_Guard(CCSPlayerController? player, CommandInfo command)
+ {
+ if (player == null)
+ return;
+ HandleQueueRequest(player);
+ }
+
+ [ConsoleCommand("css_leave", "Leaves the guard queue")]
+ [CommandHelper(0, "", CommandUsage.CLIENT_ONLY)]
+ public void Command_Leave(CCSPlayerController? player, CommandInfo command)
+ {
+ if (player == null)
+ return;
+ HandleLeaveRequest(player);
+ }
+
+
+ public IEnumerable Queue
+ => Utilities.GetPlayers()
+ .Select(player => (Player: player, State: _state.Get(player)))
+ .Where(tuple => tuple.State.InQueue) // Exclude not in queue
+ .Where(tuple => !tuple.State.IsGuard) // Exclude current guards
+ .OrderBy(tuple => tuple.State.Position) // Order by counter value when joined queue
+ .Select(tuple => tuple.Player);
}
\ No newline at end of file
diff --git a/mod/Jailbreak.Warden/Commands/WardenCommandsBehavior.cs b/mod/Jailbreak.Warden/Commands/WardenCommandsBehavior.cs
index 2967a5f2..b88f4b1b 100644
--- a/mod/Jailbreak.Warden/Commands/WardenCommandsBehavior.cs
+++ b/mod/Jailbreak.Warden/Commands/WardenCommandsBehavior.cs
@@ -3,6 +3,8 @@
using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Utils;
+using Jailbreak.Formatting.Extensions;
+using Jailbreak.Formatting.Views;
using Jailbreak.Public.Behaviors;
using Jailbreak.Public.Extensions;
using Jailbreak.Public.Mod.Warden;
@@ -11,13 +13,15 @@ namespace Jailbreak.Warden.Commands;
public class WardenCommandsBehavior : IPluginBehavior
{
- private readonly IWardenSelectionService _queue;
- private readonly IWardenService _warden;
+ private IWardenSelectionService _queue;
+ private IWardenService _warden;
+ private IWardenNotifications _notifications;
- public WardenCommandsBehavior(IWardenSelectionService queue, IWardenService warden)
+ public WardenCommandsBehavior(IWardenSelectionService queue, IWardenService warden, IWardenNotifications notifications)
{
_queue = queue;
_warden = warden;
+ _notifications = notifications;
}
public void Dispose()
@@ -33,11 +37,11 @@ public HookResult HandleWarden(CCSPlayerController sender)
{
if (_queue.InQueue(sender))
if (_queue.TryEnter(sender))
- sender.PrintToChat("[Warden] You've joined the queue!");
+ _notifications.JOIN_RAFFLE.ToPlayerChat(sender);
if (!_queue.InQueue(sender))
if (_queue.TryExit(sender))
- sender.PrintToChat("[Warden] You've left the queue!");
+ _notifications.LEAVE_RAFFLE.ToPlayerChat(sender);
return HookResult.Handled;
}
@@ -46,11 +50,7 @@ public HookResult HandleWarden(CCSPlayerController sender)
if (isCt && !_warden.HasWarden)
_warden.TrySetWarden(sender);
- // Respond to all other requests
- if (_warden.HasWarden)
- sender.PrintToChat($"[Warden] The current warden is {_warden.Warden.PlayerName}");
- else
- sender.PrintToChat("[Warden] There is currently no warden!");
+ _notifications.CURRENT_WARDEN(_warden.Warden).ToPlayerChat(sender);
return HookResult.Handled;
}
@@ -63,7 +63,12 @@ public HookResult HandlePass(CCSPlayerController sender)
if (isWarden)
{
// Handle warden pass
- Server.PrintToChatAll("[Warden] The warden has passed!");
+ _notifications.PASS_WARDEN(sender)
+ .ToAllChat()
+ .ToAllCenter();
+
+ _notifications.BECOME_NEXT_WARDEN.ToAllChat();
+
if (!_warden.TryRemoveWarden())
Server.PrintToChatAll("[BUG] Couldn't remove warden :^(");
diff --git a/mod/Jailbreak.Warden/Extensions/WardenFormatWriterExtensions.cs b/mod/Jailbreak.Warden/Extensions/WardenFormatWriterExtensions.cs
new file mode 100644
index 00000000..2e6a4e31
--- /dev/null
+++ b/mod/Jailbreak.Warden/Extensions/WardenFormatWriterExtensions.cs
@@ -0,0 +1,6 @@
+namespace Jailbreak.Warden.Extensions;
+
+public class WardenFormatWriterExtensions
+{
+
+}
\ No newline at end of file
diff --git a/mod/Jailbreak.Warden/Global/WardenBehavior.cs b/mod/Jailbreak.Warden/Global/WardenBehavior.cs
index 3034a719..9fc02573 100644
--- a/mod/Jailbreak.Warden/Global/WardenBehavior.cs
+++ b/mod/Jailbreak.Warden/Global/WardenBehavior.cs
@@ -6,6 +6,9 @@
using Jailbreak.Public.Behaviors;
using Jailbreak.Public.Extensions;
using Jailbreak.Public.Mod.Warden;
+using Jailbreak.Formatting.Core;
+using Jailbreak.Formatting.Extensions;
+using Jailbreak.Formatting.Views;
using Microsoft.Extensions.Logging;
@@ -20,6 +23,17 @@ public WardenBehavior(ILogger logger)
_logger = logger;
}
+
+ private IWardenNotifications _notifications;
+
+ private bool _hasWarden;
+ private CCSPlayerController? _warden;
+
+ public WardenBehavior(IWardenNotifications notifications)
+ {
+ _notifications = notifications;
+ }
+
///
/// Get the current warden, if there is one.
///
@@ -42,9 +56,11 @@ public bool TrySetWarden(CCSPlayerController controller)
HasWarden = true;
Warden = controller;
- Server.PrintToChatAll($"[Warden] {Warden.PlayerName.Sanitize()} is now the warden!");
- ServerExtensions.PrintToCenterAll($"{Warden.PlayerName.Sanitize()} is now the warden!");
- Warden.ClanName = "[WARDEN]";
+ _notifications.NEW_WARDEN(_warden)
+ .ToAllChat()
+ .ToAllCenter();
+
+ _warden.ClanName = "[WARDEN]";
return true;
}
@@ -75,9 +91,11 @@ public HookResult OnDeath(EventPlayerDeath ev, GameEventInfo info)
_logger.LogWarning("[Warden] BUG: Problem removing current warden :^(");
// Warden died!
- Server.PrintToChatAll("[Warden] The current warden has died!");
- Server.PrintToChatAll("[Warden] Type !warden to become the next warden");
- ServerExtensions.PrintToCenterAll("The warden has died!");
+ _notifications.WARDEN_DIED
+ .ToAllChat()
+ .ToAllCenter();
+
+ _notifications.BECOME_NEXT_WARDEN.ToAllChat();
}
return HookResult.Continue;
@@ -102,10 +120,12 @@ public HookResult OnPlayerDisconnect(EventPlayerDisconnect ev, GameEventInfo inf
if (!TryRemoveWarden())
_logger.LogWarning("[Warden] BUG: Problem removing current warden :^(");
- // Warden died!
- Server.PrintToChatAll("[Warden] The current warden has left the game!");
- Server.PrintToChatAll("[Warden] Type !warden to become the next warden");
- ServerExtensions.PrintToCenterAll("The warden has left!");
+
+ _notifications.WARDEN_LEFT
+ .ToAllChat()
+ .ToAllCenter();
+
+ _notifications.BECOME_NEXT_WARDEN.ToAllChat();
}
return HookResult.Continue;
diff --git a/mod/Jailbreak.Warden/Jailbreak.Warden.csproj b/mod/Jailbreak.Warden/Jailbreak.Warden.csproj
index 29f7e134..e549f3ef 100644
--- a/mod/Jailbreak.Warden/Jailbreak.Warden.csproj
+++ b/mod/Jailbreak.Warden/Jailbreak.Warden.csproj
@@ -7,7 +7,8 @@
-
+
+
diff --git a/mod/Jailbreak.Warden/Selection/WardenSelectionBehavior.cs b/mod/Jailbreak.Warden/Selection/WardenSelectionBehavior.cs
index 3a2dbdf0..04d40921 100644
--- a/mod/Jailbreak.Warden/Selection/WardenSelectionBehavior.cs
+++ b/mod/Jailbreak.Warden/Selection/WardenSelectionBehavior.cs
@@ -1,4 +1,6 @@
-using System.Runtime.CompilerServices;
+using System;
+using System.Linq;
+using System.Runtime.CompilerServices;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
@@ -6,6 +8,8 @@
using CounterStrikeSharp.API.Modules.Timers;
using CounterStrikeSharp.API.Modules.Utils;
+using Jailbreak.Formatting.Extensions;
+using Jailbreak.Formatting.Views;
using Jailbreak.Public.Behaviors;
using Jailbreak.Public.Extensions;
using Jailbreak.Public.Generic;
@@ -39,10 +43,13 @@ public class WardenSelectionBehavior : IPluginBehavior, IWardenSelectionService
private readonly IWardenService _warden;
- public WardenSelectionBehavior(IPlayerStateFactory factory, IWardenService warden, ILogger logger)
+ private IWardenNotifications _notifications;
+
+ public WardenSelectionBehavior(IPlayerStateFactory factory, IWardenService warden, IWardenNotifications notifications, ILogger logger)
{
+ _logger = logger;
_warden = warden;
- _logger = logger;
+ _notifications = notifications;
_queue = factory.Round();
_favor = factory.Global();
@@ -88,8 +95,7 @@ public HookResult OnRoundStart(EventRoundStart ev, GameEventInfo info)
// Enable the warden queue
_queueInactive = false;
- Server.PrintToChatAll("[Warden] Picking a warden shortly.");
- Server.PrintToChatAll("[Warden] To enter the warden queue, type !WARDEN in chat.");
+ _notifications.PICKING_SHORTLY.ToAllChat();
// Start a timer to pick the warden in 7 seconds
ScheduleChooseWarden(7.0f);
@@ -118,7 +124,7 @@ protected void OnChooseWarden()
if (eligible.Count == 0)
{
- Server.PrintToChatAll("[Warden] No Wardens in queue!");
+ _notifications.NO_WARDENS.ToAllChat();
_queueInactive = true;
return;
diff --git a/public/Jailbreak.Formatting/Base/IView.cs b/public/Jailbreak.Formatting/Base/IView.cs
new file mode 100644
index 00000000..9568bafe
--- /dev/null
+++ b/public/Jailbreak.Formatting/Base/IView.cs
@@ -0,0 +1,10 @@
+using Jailbreak.Formatting.Core;
+
+namespace Jailbreak.Formatting.Base;
+
+public interface IView
+{
+
+ void Render(FormatWriter writer);
+
+}
diff --git a/public/Jailbreak.Formatting/Base/SimpleView.cs b/public/Jailbreak.Formatting/Base/SimpleView.cs
new file mode 100644
index 00000000..6cf13644
--- /dev/null
+++ b/public/Jailbreak.Formatting/Base/SimpleView.cs
@@ -0,0 +1,30 @@
+using Jailbreak.Formatting.Core;
+
+namespace Jailbreak.Formatting.Base;
+
+public class SimpleView : IView
+{
+
+ private Action _handler;
+
+ public SimpleView(Action handler)
+ {
+ _handler = handler;
+ }
+
+ public SimpleView(params string[] lines)
+ {
+ _handler = (writer) =>
+ {
+ foreach (string line in lines)
+ {
+ writer.Line(line);
+ }
+ };
+ }
+
+ public void Render(FormatWriter writer)
+ {
+ _handler(writer);
+ }
+}
diff --git a/public/Jailbreak.Formatting/Core/FormatObject.cs b/public/Jailbreak.Formatting/Core/FormatObject.cs
new file mode 100644
index 00000000..49f31096
--- /dev/null
+++ b/public/Jailbreak.Formatting/Core/FormatObject.cs
@@ -0,0 +1,39 @@
+using CounterStrikeSharp.API.Core;
+
+using Jailbreak.Formatting.Objects;
+using Jailbreak.Public.Extensions;
+
+namespace Jailbreak.Formatting.Core;
+
+public abstract class FormatObject
+{
+
+ ///
+ /// Output this format object compatible with CS2 chat formatting.
+ ///
+ ///
+ public virtual string ToChat()
+ => ToPlain();
+
+ ///
+ /// Output this format object in a panorama-compatible format.
+ ///
+ ///
+ public virtual string ToPanorama()
+ => ToPlain().Sanitize();
+
+ ///
+ /// Output plaintext
+ ///
+ ///
+ public abstract string ToPlain();
+
+
+ public static implicit operator FormatObject(string value)
+ => new StringFormatObject(value);
+
+ public static implicit operator FormatObject(CCSPlayerController value)
+ => new PlayerFormatObject(value);
+
+ public static FormatObject FromObject(object value) => new StringFormatObject(value.ToString());
+}
diff --git a/public/Jailbreak.Formatting/Core/FormatWriter.cs b/public/Jailbreak.Formatting/Core/FormatWriter.cs
new file mode 100644
index 00000000..feccfc30
--- /dev/null
+++ b/public/Jailbreak.Formatting/Core/FormatWriter.cs
@@ -0,0 +1,28 @@
+namespace Jailbreak.Formatting.Core;
+
+public class FormatWriter
+{
+
+ private List< FormatObject[] > _lines = new();
+
+ public IEnumerable Lines => _lines;
+
+ public IEnumerable Chat
+ => Lines.Select(array => string.Join(' ', array.Select(obj => obj.ToChat()) ));
+
+
+ public IEnumerable Panorama
+ => Lines.Select(array => string.Join(' ', array.Select(obj => obj.ToPanorama()) ));
+
+ public IEnumerable Plain
+ => Lines.Select(array => string.Join(' ', array.Select(obj => obj.ToPlain()) ));
+
+
+ public FormatWriter Line( params FormatObject[] args )
+ {
+ _lines.Add( args );
+
+ return this;
+ }
+
+}
diff --git a/public/Jailbreak.Formatting/Extensions/ViewExtensions.cs b/public/Jailbreak.Formatting/Extensions/ViewExtensions.cs
new file mode 100644
index 00000000..981f483a
--- /dev/null
+++ b/public/Jailbreak.Formatting/Extensions/ViewExtensions.cs
@@ -0,0 +1,85 @@
+using CounterStrikeSharp.API;
+using CounterStrikeSharp.API.Core;
+
+using Jailbreak.Formatting.Base;
+using Jailbreak.Formatting.Core;
+
+namespace Jailbreak.Formatting.Extensions;
+
+public static class ViewExtensions
+{
+
+ public static FormatWriter ToWriter(this IView view)
+ {
+ var writer = new FormatWriter();
+
+ view.Render(writer);
+
+ return writer;
+ }
+
+ #region Individual
+
+ public static IView ToPlayerConsole(this IView view, CCSPlayerController player)
+ {
+ var writer = view.ToWriter();
+
+ foreach (string writerLine in writer.Plain)
+ player.PrintToConsole(writerLine);
+
+ return view;
+ }
+
+ public static IView ToPlayerChat(this IView view, CCSPlayerController player)
+ {
+ var writer = view.ToWriter();
+
+ foreach (string writerLine in writer.Chat)
+ player.PrintToChat(writerLine);
+
+ return view;
+ }
+
+ public static IView ToPlayerCenter(this IView view, CCSPlayerController player)
+ {
+ var writer = view.ToWriter();
+ var merged = string.Join('\n', writer.Plain);
+
+ player.PrintToCenter(merged);
+
+ return view;
+ }
+
+ public static IView ToPlayerCenterHtml(this IView view, CCSPlayerController player)
+ {
+ var writer = view.ToWriter();
+ var merged = string.Join('\n', writer.Panorama);
+
+ player.PrintToCenterHtml(merged);
+
+ return view;
+ }
+
+ #endregion
+
+ public static IView ToAllConsole(this IView view)
+ {
+ Utilities.GetPlayers().ForEach(player => view.ToPlayerConsole(player));
+
+ return view;
+ }
+
+ public static IView ToAllChat(this IView view)
+ {
+ Utilities.GetPlayers().ForEach(player => view.ToPlayerChat(player));
+
+ return view;
+ }
+
+ public static IView ToAllCenter(this IView view)
+ {
+ Utilities.GetPlayers().ForEach(player => view.ToPlayerCenter(player));
+
+ return view;
+ }
+}
diff --git a/public/Jailbreak.Formatting/Jailbreak.Formatting.csproj b/public/Jailbreak.Formatting/Jailbreak.Formatting.csproj
new file mode 100644
index 00000000..dfa47427
--- /dev/null
+++ b/public/Jailbreak.Formatting/Jailbreak.Formatting.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/public/Jailbreak.Formatting/Languages/English.cs b/public/Jailbreak.Formatting/Languages/English.cs
new file mode 100644
index 00000000..0fa55e13
--- /dev/null
+++ b/public/Jailbreak.Formatting/Languages/English.cs
@@ -0,0 +1,8 @@
+using Jailbreak.Formatting.Logistics;
+
+namespace Jailbreak.Formatting.Languages;
+
+public class English : IDialect
+{
+
+}
diff --git a/public/Jailbreak.Formatting/Logistics/IDialect.cs b/public/Jailbreak.Formatting/Logistics/IDialect.cs
new file mode 100644
index 00000000..a10560c4
--- /dev/null
+++ b/public/Jailbreak.Formatting/Logistics/IDialect.cs
@@ -0,0 +1,10 @@
+namespace Jailbreak.Formatting.Logistics;
+
+///
+/// A specific language, such as "English" or "French",
+/// should inherit this interface.
+///
+public interface IDialect
+{
+
+}
diff --git a/public/Jailbreak.Formatting/Logistics/ILanguage.cs b/public/Jailbreak.Formatting/Logistics/ILanguage.cs
new file mode 100644
index 00000000..a6483688
--- /dev/null
+++ b/public/Jailbreak.Formatting/Logistics/ILanguage.cs
@@ -0,0 +1,12 @@
+namespace Jailbreak.Formatting.Logistics;
+
+///
+/// Specifies that this class is written in a specific language
+/// Eg, ILanguage<English> or ILanguage<French>
+///
+///
+public interface ILanguage
+ where TDialect: IDialect
+{
+
+}
diff --git a/public/Jailbreak.Formatting/Logistics/LanguageConfig.cs b/public/Jailbreak.Formatting/Logistics/LanguageConfig.cs
new file mode 100644
index 00000000..47cc15be
--- /dev/null
+++ b/public/Jailbreak.Formatting/Logistics/LanguageConfig.cs
@@ -0,0 +1,25 @@
+using Jailbreak.Formatting.Views;
+
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Jailbreak.Formatting.Logistics;
+
+public class LanguageConfig
+ where TDialect: IDialect
+{
+
+ private IServiceCollection _collection;
+
+ public LanguageConfig(IServiceCollection collection)
+ {
+ _collection = collection;
+ }
+
+ public void WithRatio()
+ where TRatio : class, ILanguage, IRatioNotifications
+ => _collection.AddSingleton();
+
+ public void WithWarden()
+ where TWarden : class, ILanguage, IWardenNotifications
+ => _collection.AddSingleton();
+}
diff --git a/public/Jailbreak.Formatting/Logistics/RegisterLanguageExtensions.cs b/public/Jailbreak.Formatting/Logistics/RegisterLanguageExtensions.cs
new file mode 100644
index 00000000..b2174095
--- /dev/null
+++ b/public/Jailbreak.Formatting/Logistics/RegisterLanguageExtensions.cs
@@ -0,0 +1,16 @@
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Jailbreak.Formatting.Logistics;
+
+public static class RegisterLanguageExtensions
+{
+ public static void AddLanguage(
+ this IServiceCollection collection,
+ Action> factory)
+ where TDialect: IDialect
+ {
+ var config = new LanguageConfig(collection);
+
+ factory(config);
+ }
+}
diff --git a/public/Jailbreak.Formatting/Objects/HiddenFormatObject.cs b/public/Jailbreak.Formatting/Objects/HiddenFormatObject.cs
new file mode 100644
index 00000000..a9cac122
--- /dev/null
+++ b/public/Jailbreak.Formatting/Objects/HiddenFormatObject.cs
@@ -0,0 +1,40 @@
+using Jailbreak.Formatting.Core;
+
+namespace Jailbreak.Formatting.Objects;
+
+public class HiddenFormatObject : FormatObject
+{
+
+ public FormatObject _value;
+
+ public HiddenFormatObject(FormatObject value)
+ {
+ _value = value;
+ }
+
+ public bool Plain { get; set; } = true;
+
+ public bool Panorama { get; set; } = true;
+
+ public bool Chat { get; set; } = true;
+
+ public override string ToChat()
+ {
+ if (Chat)
+ return _value.ToChat();
+ return String.Empty;
+ }
+
+ public override string ToPanorama()
+ {
+ if (Panorama)
+ return _value.ToPanorama();
+ return String.Empty; }
+
+ public override string ToPlain()
+ {
+ if (Plain)
+ return _value.ToPlain();
+ return String.Empty;
+ }
+}
diff --git a/public/Jailbreak.Formatting/Objects/PlayerFormatObject.cs b/public/Jailbreak.Formatting/Objects/PlayerFormatObject.cs
new file mode 100644
index 00000000..3dadab23
--- /dev/null
+++ b/public/Jailbreak.Formatting/Objects/PlayerFormatObject.cs
@@ -0,0 +1,26 @@
+using CounterStrikeSharp.API.Core;
+using CounterStrikeSharp.API.Modules.Utils;
+
+using Jailbreak.Formatting.Core;
+using Jailbreak.Public.Extensions;
+
+namespace Jailbreak.Formatting.Objects;
+
+public class PlayerFormatObject : FormatObject
+{
+ private CCSPlayerController _player;
+
+ public PlayerFormatObject(CCSPlayerController player)
+ {
+ _player = player;
+ }
+
+ public override string ToChat()
+ => $"{ChatColors.Yellow}{_player.PlayerName}";
+
+ public override string ToPanorama()
+ => _player.PlayerName.Sanitize();
+
+ public override string ToPlain()
+ => _player.PlayerName;
+}
diff --git a/public/Jailbreak.Formatting/Objects/StringFormatObject.cs b/public/Jailbreak.Formatting/Objects/StringFormatObject.cs
new file mode 100644
index 00000000..64900bba
--- /dev/null
+++ b/public/Jailbreak.Formatting/Objects/StringFormatObject.cs
@@ -0,0 +1,28 @@
+using CounterStrikeSharp.API.Modules.Utils;
+
+using Jailbreak.Formatting.Core;
+
+namespace Jailbreak.Formatting.Objects;
+
+public class StringFormatObject : FormatObject
+{
+ private string _value;
+ private char _chatColor;
+
+ public StringFormatObject(string value, char chatColor = '\x01')
+ {
+ _value = value;
+ _chatColor = chatColor;
+ }
+
+ public string Value => _value;
+
+ public override string ToChat()
+ => $"{_chatColor}{Value}";
+
+ public override string ToPanorama()
+ => Value;
+
+ public override string ToPlain()
+ => Value;
+}
diff --git a/public/Jailbreak.Formatting/Views/ITeamsNotifications.cs b/public/Jailbreak.Formatting/Views/ITeamsNotifications.cs
new file mode 100644
index 00000000..15e2ce80
--- /dev/null
+++ b/public/Jailbreak.Formatting/Views/ITeamsNotifications.cs
@@ -0,0 +1,19 @@
+using Jailbreak.Formatting.Base;
+
+namespace Jailbreak.Formatting.Views;
+
+public interface IRatioNotifications
+{
+ public IView NOT_ENOUGH_GUARDS { get; }
+
+ public IView JOIN_GUARD_QUEUE { get; }
+
+ public IView YOU_WERE_AUTOBALANCED_PRISONER { get; }
+
+ public IView YOU_WERE_AUTOBALANCED_GUARD { get; }
+
+ public IView ATTEMPT_TO_JOIN_FROM_TEAM_MENU { get; }
+
+ public IView LEFT_GUARD { get; }
+
+}
diff --git a/public/Jailbreak.Formatting/Views/IWardenNotifications.cs b/public/Jailbreak.Formatting/Views/IWardenNotifications.cs
new file mode 100644
index 00000000..189b41be
--- /dev/null
+++ b/public/Jailbreak.Formatting/Views/IWardenNotifications.cs
@@ -0,0 +1,38 @@
+using CounterStrikeSharp.API.Core;
+
+using Jailbreak.Formatting.Base;
+
+namespace Jailbreak.Formatting.Views;
+
+public interface IWardenNotifications
+{
+ public IView PICKING_SHORTLY { get; }
+ public IView NO_WARDENS { get; }
+ public IView WARDEN_LEFT { get; }
+ public IView WARDEN_DIED { get; }
+ public IView BECOME_NEXT_WARDEN { get; }
+ public IView JOIN_RAFFLE { get; }
+ public IView LEAVE_RAFFLE { get; }
+
+ ///
+ /// Create a view for when the specified player passes warden
+ ///
+ ///
+ ///
+ public IView PASS_WARDEN(CCSPlayerController player);
+
+ ///
+ /// Create a view for when this player becomes a new warden
+ ///
+ ///
+ ///
+ public IView NEW_WARDEN(CCSPlayerController player);
+
+ ///
+ /// Format a response to a request about the current warden.
+ /// When player is null, instead respond stating that there is no warden.
+ ///
+ ///
+ ///
+ public IView CURRENT_WARDEN(CCSPlayerController? player);
+}
diff --git a/src/Jailbreak.Generic/GenericServiceExtension.cs b/src/Jailbreak.Generic/GenericServiceExtension.cs
index 850207e3..c55984ff 100644
--- a/src/Jailbreak.Generic/GenericServiceExtension.cs
+++ b/src/Jailbreak.Generic/GenericServiceExtension.cs
@@ -1,4 +1,6 @@
-using Jailbreak.Generic.PlayerState;
+using CounterStrikeSharp.API.Core;
+
+using Jailbreak.Generic.PlayerState;
using Jailbreak.Generic.PlayerState.Behaviors;
using Jailbreak.Public.Extensions;
using Jailbreak.Public.Generic;
diff --git a/src/Jailbreak.Generic/PlayerState/Behaviors/RoundStateTracker.cs b/src/Jailbreak.Generic/PlayerState/Behaviors/RoundStateTracker.cs
index 1c4aa376..66566489 100644
--- a/src/Jailbreak.Generic/PlayerState/Behaviors/RoundStateTracker.cs
+++ b/src/Jailbreak.Generic/PlayerState/Behaviors/RoundStateTracker.cs
@@ -3,6 +3,7 @@
using Jailbreak.Public.Behaviors;
+
namespace Jailbreak.Generic.PlayerState.Behaviors;
public class RoundStateTracker : BaseStateTracker, IPluginBehavior
diff --git a/src/Jailbreak/Jailbreak.csproj b/src/Jailbreak/Jailbreak.csproj
index 50d6651b..0a0c71f4 100644
--- a/src/Jailbreak/Jailbreak.csproj
+++ b/src/Jailbreak/Jailbreak.csproj
@@ -32,10 +32,11 @@
-
-
-
-
+
+
+
+
+
diff --git a/src/Jailbreak/JailbreakServiceCollection.cs b/src/Jailbreak/JailbreakServiceCollection.cs
index 7bd7b26a..d5504c14 100644
--- a/src/Jailbreak/JailbreakServiceCollection.cs
+++ b/src/Jailbreak/JailbreakServiceCollection.cs
@@ -1,6 +1,10 @@
using CounterStrikeSharp.API.Core;
using Jailbreak.Config;
+using Jailbreak.English.Teams;
+using Jailbreak.English.Warden;
+using Jailbreak.Formatting.Languages;
+using Jailbreak.Formatting.Logistics;
using Jailbreak.Generic;
using Jailbreak.Public.Configuration;
using Jailbreak.Teams;
@@ -25,5 +29,12 @@ public void ConfigureServices(IServiceCollection serviceCollection)
serviceCollection.AddJailbreakGeneric();
serviceCollection.AddJailbreakWarden();
serviceCollection.AddJailbreakTeams();
+
+ // Add in english localization
+ serviceCollection.AddLanguage(config =>
+ {
+ config.WithRatio();
+ config.WithWarden();
+ });
}
}
\ No newline at end of file