Word replacement accent system (#15086)
This commit is contained in:
@@ -1,83 +0,0 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Server.Speech.Components;
|
||||
|
||||
namespace Content.Server.Speech.EntitySystems;
|
||||
|
||||
public sealed class DwarfAccentSystem : EntitySystem
|
||||
{
|
||||
// TODO:
|
||||
// these are pretty bad to have as static dicts in systems, ideally these all get moved to prototypes
|
||||
// these can honestly stay unlocalized in prototypes? -- most of these word-replacers make zero sense to localize into other languages
|
||||
// since they're so english-specific
|
||||
// all of the 'word-replacers' should also probably respect capitalization when transformed, so all caps -> all caps
|
||||
// and first letter capitalized -> first letter capitalized, at the very least
|
||||
|
||||
// these specifically mostly come from examples of specific scottish-english (not necessarily scots) verbiage
|
||||
// https://en.wikipedia.org/wiki/Scotticism
|
||||
// https://en.wikipedia.org/wiki/Scottish_English
|
||||
// https://www.cs.stir.ac.uk/~kjt/general/scots.html
|
||||
private static readonly Dictionary<string, string> DirectReplacements = new()
|
||||
{
|
||||
{ "girl", "lassie" },
|
||||
{ "boy", "laddie" },
|
||||
{ "man", "lad" },
|
||||
{ "woman", "lass" },
|
||||
{ "do", "dae" },
|
||||
{ "don't", "dinnae" },
|
||||
{ "dont", "dinnae" },
|
||||
{ "i'm", "A'm" },
|
||||
{ "im", "am"},
|
||||
{ "going", "gaun" },
|
||||
{ "know", "ken"},
|
||||
{ "i", "Ah" },
|
||||
{ "you're", "ye're"},
|
||||
{ "youre", "yere"},
|
||||
{ "you", "ye" },
|
||||
{ "i'll", "A'll" },
|
||||
{ "ill", "all"},
|
||||
{ "of", "ae" },
|
||||
{ "was", "wis" },
|
||||
{ "can't", "cannae" },
|
||||
{ "cant", "cannae" },
|
||||
{ "yourself", "yersel" },
|
||||
{ "where", "whaur" },
|
||||
{ "oh", "ach" },
|
||||
{ "little", "wee" },
|
||||
{ "small", "wee" },
|
||||
{ "shit", "shite" },
|
||||
{ "yeah", "aye" },
|
||||
{ "yea", "aye"},
|
||||
{ "yes", "aye" },
|
||||
{ "too", "tae" },
|
||||
{ "my", "ma" },
|
||||
{ "not", "nae" },
|
||||
{ "dad", "da" },
|
||||
{ "mom", "maw" },
|
||||
};
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DwarfAccentComponent, AccentGetEvent>(OnAccentGet);
|
||||
}
|
||||
|
||||
public string Accentuate(string message)
|
||||
{
|
||||
// this is just word replacements right now,
|
||||
// but leaving it open to more intelligent phonotactic manipulations at some point which are probably possible
|
||||
var msg = message;
|
||||
|
||||
foreach (var (first, replace) in DirectReplacements)
|
||||
{
|
||||
msg = Regex.Replace(msg, $@"(?<!\w){first}(?!\w)", replace, RegexOptions.IgnoreCase);
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
private void OnAccentGet(EntityUid uid, DwarfAccentComponent component, AccentGetEvent args)
|
||||
{
|
||||
args.Message = Accentuate(args.Message);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ namespace Content.Server.Speech.EntitySystems;
|
||||
public sealed class MobsterAccentSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ReplacementAccentSystem _replacement = default!;
|
||||
|
||||
private static readonly Dictionary<string, string> DirectReplacements = new()
|
||||
{
|
||||
@@ -43,12 +44,8 @@ public sealed class MobsterAccentSystem : EntitySystem
|
||||
// Do text manipulations first
|
||||
// Then prefix/suffix funnyies
|
||||
|
||||
var msg = message;
|
||||
|
||||
foreach (var (first, replace) in DirectReplacements)
|
||||
{
|
||||
msg = Regex.Replace(msg, $@"(?<!\w){first}(?!\w)", replace, RegexOptions.IgnoreCase);
|
||||
}
|
||||
// direct word replacements
|
||||
var msg = _replacement.ApplyReplacements(message, "mobster");
|
||||
|
||||
// thinking -> thinkin'
|
||||
// king -> king
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Server.Speech.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
@@ -6,12 +9,13 @@ namespace Content.Server.Speech.EntitySystems
|
||||
{
|
||||
// TODO: Code in-game languages and make this a language
|
||||
/// <summary>
|
||||
/// Replaces any spoken sentences with a random word.
|
||||
/// Replaces text in messages, either with full replacements or word replacements.
|
||||
/// </summary>
|
||||
public sealed class ReplacementAccentSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ILocalizationManager _loc = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -20,9 +24,64 @@ namespace Content.Server.Speech.EntitySystems
|
||||
|
||||
private void OnAccent(EntityUid uid, ReplacementAccentComponent component, AccentGetEvent args)
|
||||
{
|
||||
var words = _proto.Index<ReplacementAccentPrototype>(component.Accent).Words;
|
||||
args.Message = ApplyReplacements(args.Message, component.Accent);
|
||||
}
|
||||
|
||||
args.Message = words.Length != 0 ? Loc.GetString(_random.Pick(words)) : "";
|
||||
/// <summary>
|
||||
/// Attempts to apply a given replacement accent prototype to a message.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string ApplyReplacements(string message, string accent)
|
||||
{
|
||||
if (!_proto.TryIndex<ReplacementAccentPrototype>(accent, out var prototype))
|
||||
return message;
|
||||
|
||||
// Prioritize fully replacing if that exists--
|
||||
// ideally both aren't used at the same time (but we don't have a way to enforce that in serialization yet)
|
||||
if (prototype.FullReplacements != null)
|
||||
{
|
||||
return prototype.FullReplacements.Length != 0 ? Loc.GetString(_random.Pick(prototype.FullReplacements)) : "";
|
||||
}
|
||||
|
||||
if (prototype.WordReplacements == null)
|
||||
return message;
|
||||
|
||||
foreach (var (first, replace) in prototype.WordReplacements)
|
||||
{
|
||||
var f = _loc.GetString(first);
|
||||
var r = _loc.GetString(replace);
|
||||
// this is kind of slow but its not that bad
|
||||
// essentially: go over all matches, try to match capitalization where possible, then replace
|
||||
// rather than using regex.replace
|
||||
foreach (Match match in Regex.Matches(message, $@"(?<!\w){f}(?!\w)", RegexOptions.IgnoreCase))
|
||||
{
|
||||
var replacement = r;
|
||||
|
||||
// Intelligently replace capitalization
|
||||
// two cases where we will do so:
|
||||
// - the string is all upper case (just uppercase the replacement too)
|
||||
// - the first letter of the word is capitalized (common, just uppercase the first letter too)
|
||||
// any other cases are not really useful or not viable, since the match & replacement can be different
|
||||
// lengths
|
||||
|
||||
// second expression here is weird--its specifically for single-word capitalization for I or A
|
||||
// dwarf expands I -> Ah, without that it would transform I -> AH
|
||||
// so that second case will only fully-uppercase if the replacement length is also 1
|
||||
if (!match.Value.Any(char.IsLower) && (match.Length > 1 || replacement.Length == 1))
|
||||
{
|
||||
replacement = replacement.ToUpperInvariant();
|
||||
}
|
||||
else if (match.Length >= 1 && replacement.Length >= 1 && char.IsUpper(match.Value[0]))
|
||||
{
|
||||
replacement = replacement[0].ToString().ToUpper() + replacement[1..];
|
||||
}
|
||||
|
||||
// In-place replace the match with the transformed capitalization replacement
|
||||
message = message.Remove(match.Index, match.Length).Insert(match.Index, replacement);
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user