diff --git a/Content.Server/Chat/ChatManager.cs b/Content.Server/Chat/ChatManager.cs index b2e13053d8..266758207a 100644 --- a/Content.Server/Chat/ChatManager.cs +++ b/Content.Server/Chat/ChatManager.cs @@ -1,11 +1,9 @@ -using System.Linq; -using Content.Server.GameObjects.Components.Observer; +using Content.Server.GameObjects.Components.Observer; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; using Content.Server.Interfaces.Chat; using Content.Shared.Chat; using Content.Shared.GameObjects.EntitySystems; -using NFluidsynth; using Robust.Server.Console; using Robust.Server.Interfaces.GameObjects; using Robust.Server.Interfaces.Player; @@ -13,6 +11,10 @@ using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Network; using Robust.Shared.IoC; using Robust.Shared.Localization; +using System; +using System.Collections.Generic; +using System.Linq; +using static Content.Server.Interfaces.Chat.IChatManager; namespace Content.Server.Chat { @@ -33,6 +35,9 @@ namespace Content.Server.Chat /// private const string MaxLengthExceededMessage = "Your message exceeded {0} character limit"; + //TODO: make prio based? + private List _chatTransformHandlers; + [Dependency] private readonly IEntitySystemManager _entitySystemManager = default!; [Dependency] private readonly IServerNetManager _netManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; @@ -49,6 +54,8 @@ namespace Content.Server.Chat var msg = _netManager.CreateNetMessage(); msg.MaxMessageLength = MaxMessageLength; _netManager.ServerSendToAll(msg); + + _chatTransformHandlers = new List(); } public void DispatchServerAnnouncement(string message) @@ -96,6 +103,12 @@ namespace Content.Server.Chat return; } + foreach (var handler in _chatTransformHandlers) + { + //TODO: rather return a bool and use a out var? + message = handler(source, message); + } + var pos = source.Transform.GridPosition; var clients = _playerManager.GetPlayersInRange(pos, VoiceRange).Select(p => p.ConnectedClient); @@ -215,5 +228,10 @@ namespace Content.Server.Chat response.MaxMessageLength = MaxMessageLength; _netManager.ServerSendMessage(response, msg.MsgChannel); } + + public void RegisterChatTransform(TransformChat handler) + { + _chatTransformHandlers.Add(handler); + } } } diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs index 120b364352..140fcb424b 100644 --- a/Content.Server/EntryPoint.cs +++ b/Content.Server/EntryPoint.cs @@ -15,6 +15,7 @@ using Robust.Shared.Interfaces.Log; using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Timing; +using Content.Server.GameObjects.Components.Mobs.Speech; namespace Content.Server { @@ -64,6 +65,7 @@ namespace Content.Server IoCManager.Resolve().StartInit(); IoCManager.Resolve().Initialize(); IoCManager.Resolve().Initialize(); + IoCManager.Resolve().Initialize(); } public override void PostInit() diff --git a/Content.Server/GameObjects/Components/Mobs/Speech/AccentManager.cs b/Content.Server/GameObjects/Components/Mobs/Speech/AccentManager.cs new file mode 100644 index 0000000000..c3482fe61e --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/Speech/AccentManager.cs @@ -0,0 +1,41 @@ +using Content.Server.Interfaces.Chat; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Content.Server.GameObjects.Components.Mobs.Speech +{ + public interface IAccentManager + { + public void Initialize(); + } + + public class AccentManager : IAccentManager + { + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly IComponentManager _componentManager = default!; + + public static readonly Regex SentenceRegex = new Regex(@"(?<=[\.!\?])"); + + public void Initialize() + { + IoCManager.InjectDependencies(this); + + _chatManager.RegisterChatTransform(AccentHandler); + } + + public string AccentHandler(IEntity player, string message) + { + //TODO: give accents a prio? + var accents = _componentManager.GetComponents(player.Uid); + foreach (var accent in accents) + { + message = accent.Accentuate(message); + } + return message; + } + } +} diff --git a/Content.Server/GameObjects/Components/Mobs/Speech/BackwardsAccentComponent.cs b/Content.Server/GameObjects/Components/Mobs/Speech/BackwardsAccentComponent.cs new file mode 100644 index 0000000000..af97bba4f8 --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/Speech/BackwardsAccentComponent.cs @@ -0,0 +1,18 @@ +using Robust.Shared.GameObjects; +using System; + +namespace Content.Server.GameObjects.Components.Mobs.Speech +{ + [RegisterComponent] + public class BackwardsAccentComponent : Component, IAccentComponent + { + public override string Name => "BackwardsAccent"; + + public string Accentuate(string message) + { + var arr = message.ToCharArray(); + Array.Reverse(arr); + return new string(arr); + } + } +} diff --git a/Content.Server/GameObjects/Components/Mobs/Speech/OwOAccentComponent.cs b/Content.Server/GameObjects/Components/Mobs/Speech/OwOAccentComponent.cs new file mode 100644 index 0000000000..819e7c81cd --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/Speech/OwOAccentComponent.cs @@ -0,0 +1,36 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Random; +using System.Collections.Generic; + +namespace Content.Server.GameObjects.Components.Mobs.Speech +{ + [RegisterComponent] + public class OwOAccentComponent : Component, IAccentComponent + { + public override string Name => "OwOAccent"; + + private static readonly IReadOnlyList Faces = new List{ + " (・`ω´・)", " ;;w;;", " owo", " UwU", " >w<", " ^w^" + }.AsReadOnly(); + private string RandomFace => IoCManager.Resolve().Pick(Faces); + + private static readonly Dictionary SpecialWords = new Dictionary + { + { "you", "wu" }, + }; + + public string Accentuate(string message) + { + foreach ((var word,var repl) in SpecialWords) + { + message = message.Replace(word, repl); + } + + return message.Replace("!", RandomFace) + .Replace("r", "w").Replace("R", "W") + .Replace("l", "w").Replace("L", "W"); + } + } +} diff --git a/Content.Server/GameObjects/Components/Mobs/Speech/SpanishAccentComponent.cs b/Content.Server/GameObjects/Components/Mobs/Speech/SpanishAccentComponent.cs new file mode 100644 index 0000000000..4ae5cb4019 --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/Speech/SpanishAccentComponent.cs @@ -0,0 +1,64 @@ +using Microsoft.CodeAnalysis.Operations; +using Microsoft.EntityFrameworkCore.Internal; +using Robust.Shared.GameObjects; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; + +namespace Content.Server.GameObjects.Components.Mobs.Speech +{ + [RegisterComponent] + public class SpanishAccentComponent : Component, IAccentComponent + { + public override string Name => "SpanishAccent"; + + public string Accentuate(string message) + { + // Insert E before every S + message = InsertS(message); + // If a sentence ends with ?, insert a reverse ? at the beginning of the sentence + message = ReplaceQuestionMark(message); + return message; + } + + private string InsertS(string message) + { + // Replace every new Word that starts with s/S + var msg = message.Replace(" s", " es").Replace(" S", "Es"); + + // Still need to check if the beginning of the message starts + if (msg.StartsWith("s")) + { + return msg.Remove(0, 1).Insert(0, "es"); + } + else if (msg.StartsWith("S")) + { + return msg.Remove(0, 1).Insert(0, "Es"); + } + + return msg; + } + + private string ReplaceQuestionMark(string message) + { + var sentences = AccentManager.SentenceRegex.Split(message); + var msg = ""; + foreach (var s in sentences) + { + if (s.EndsWith("?")) // We've got a question => add ¿ to the beginning + { + // Because we don't split by whitespace, we may have some spaces in front of the sentence. + // So we add the symbol before the first non space char + msg += s.Insert(s.Length - s.TrimStart().Length, "¿"); + } + else + { + msg += s; + } + } + return msg; + } + } +} diff --git a/Content.Server/GameObjects/Components/Mobs/Speech/SpeechComponent.cs b/Content.Server/GameObjects/Components/Mobs/Speech/SpeechComponent.cs new file mode 100644 index 0000000000..b9b8a40670 --- /dev/null +++ b/Content.Server/GameObjects/Components/Mobs/Speech/SpeechComponent.cs @@ -0,0 +1,94 @@ +using Robust.Server.Interfaces.Console; +using Robust.Server.Interfaces.Player; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using System; +using System.Linq; + +namespace Content.Server.GameObjects.Components.Mobs.Speech +{ + internal interface IAccentComponent + { + /// + /// Transforms a message with the given Accent + /// + /// The spoken message + /// The message after the transformation + public string Accentuate(string message); + } + + public class AddAccent : IClientCommand + { + public string Command => "addaccent"; + + public string Description => "Add a speech component to the current player"; + + public string Help => $"{Command} /?"; + + public void Execute(IConsoleShell shell, IPlayerSession player, string[] args) + { + if (args.Length == 0) + { + shell.SendText(player, Help); + return; + } + + if (player.AttachedEntity == null) + { + shell.SendText(player, "You don't have a player!"); + return; + } + + var compFactory = IoCManager.Resolve(); + + if (args[0] == "?") + { + // Get all components that implement the ISpeechComponent except + var speeches = compFactory.GetAllRefTypes() + .Where(c => typeof(IAccentComponent).IsAssignableFrom(c) && c.IsClass); + var msg = ""; + foreach(var s in speeches) + { + msg += $"{compFactory.GetRegistration(s).Name}\n"; + } + shell.SendText(player, msg); + } + else + { + var name = args[0]; + // Try to get the Component + Type type; + try + { + var comp = compFactory.GetComponent(name); + type = comp.GetType(); + } + catch (Exception) + { + shell.SendText(player, $"Accent {name} not found. Try {Command} ? to get a list of all appliable accents."); + return; + } + + // Check if that already exists + try + { + var comp = player.AttachedEntity.GetComponent(type); + shell.SendText(player, "You already have this accent!"); + return; + } + catch (Exception) + { + // Accent not found + } + + // Generic fuckery + var ensure = typeof(IEntity).GetMethod("AddComponent"); + if (ensure == null) + return; + var method = ensure.MakeGenericMethod(type); + method.Invoke(player.AttachedEntity, null); + } + } + } +} diff --git a/Content.Server/Interfaces/Chat/IChatManager.cs b/Content.Server/Interfaces/Chat/IChatManager.cs index e32c9bea8d..eb06e54d78 100644 --- a/Content.Server/Interfaces/Chat/IChatManager.cs +++ b/Content.Server/Interfaces/Chat/IChatManager.cs @@ -1,5 +1,6 @@ -using Robust.Server.Interfaces.Player; +using Robust.Server.Interfaces.Player; using Robust.Shared.Interfaces.GameObjects; +using System; namespace Content.Server.Interfaces.Chat { @@ -28,5 +29,8 @@ namespace Content.Server.Interfaces.Chat void SendDeadChat(IPlayerSession player, string message); void SendHookOOC(string sender, string message); + + delegate string TransformChat(IEntity speaker, string message); + void RegisterChatTransform(TransformChat handler); } } diff --git a/Content.Server/ServerContentIoC.cs b/Content.Server/ServerContentIoC.cs index 0083b4c8bd..1e757d4232 100644 --- a/Content.Server/ServerContentIoC.cs +++ b/Content.Server/ServerContentIoC.cs @@ -1,8 +1,9 @@ -using Content.Server.AI.Utility.Considerations; +using Content.Server.AI.Utility.Considerations; using Content.Server.AI.WorldState; using Content.Server.Body.Network; using Content.Server.Cargo; using Content.Server.Chat; +using Content.Server.GameObjects.Components.Mobs.Speech; using Content.Server.GameObjects.Components.NodeContainer.NodeGroups; using Content.Server.GameObjects.Components.Power.PowerNetComponents; using Content.Server.GameTicking; @@ -41,6 +42,7 @@ namespace Content.Server IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); } } } diff --git a/Resources/Groups/groups.yml b/Resources/Groups/groups.yml index 71787fbcaa..75fe17e4bf 100644 --- a/Resources/Groups/groups.yml +++ b/Resources/Groups/groups.yml @@ -97,6 +97,7 @@ - tilewalls - events - destroymechanism + - addaccent - readyall - factions CanViewVar: true @@ -189,6 +190,7 @@ - tilewalls - events - destroymechanism + - addaccent - readyall - factions CanViewVar: true