Word replacement accent system (#15086)

This commit is contained in:
Kara
2023-04-03 19:50:37 -07:00
committed by GitHub
parent f8f318def8
commit 18df657fb6
11 changed files with 338 additions and 150 deletions

View File

@@ -1,9 +0,0 @@
namespace Content.Server.Speech.Components;
/// <summary>
/// Used for replacing words for dwarves (pseudo-scottish)
/// </summary>
[RegisterComponent]
public sealed class DwarfAccentComponent : Component
{
}

View File

@@ -10,12 +10,22 @@ namespace Content.Server.Speech.Components
[IdDataField]
public string ID { get; } = default!;
[DataField("words")]
public string[] Words = default!;
/// <summary>
/// If this array is non-null, the full text of anything said will be randomly replaced with one of these words.
/// </summary>
[DataField("fullReplacements")]
public string[]? FullReplacements;
/// <summary>
/// If this dictionary is non-null and <see cref="FullReplacements"/> is null, any keys surrounded by spaces
/// (words) will be replaced by the value, attempting to intelligently keep capitalization.
/// </summary>
[DataField("wordReplacements")]
public Dictionary<string, string>? WordReplacements;
}
/// <summary>
/// Replaces any spoken sentences with a random word.
/// Replaces full sentences or words within sentences with new strings.
/// </summary>
[RegisterComponent]
public sealed class ReplacementAccentComponent : Component

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -0,0 +1,109 @@
# 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
accent-dwarf-words-1 = girl
accent-dwarf-words-replace-1 = lassie
accent-dwarf-words-2 = boy
accent-dwarf-words-replace-2 = laddie
accent-dwarf-words-3 = man
accent-dwarf-words-replace-3 = lad
accent-dwarf-words-4 = woman
accent-dwarf-words-replace-4 = lass
accent-dwarf-words-5 = do
accent-dwarf-words-replace-5 = dae
accent-dwarf-words-6 = don't
accent-dwarf-words-replace-6 = dinnae
accent-dwarf-words-7 = dont
accent-dwarf-words-replace-7 = dinnae
accent-dwarf-words-8 = i'm
accent-dwarf-words-replace-8 = A'm
accent-dwarf-words-9 = im
accent-dwarf-words-replace-9 = am
accent-dwarf-words-10 = going
accent-dwarf-words-replace-10 = gaun
accent-dwarf-words-11 = know
accent-dwarf-words-replace-11 = ken
accent-dwarf-words-12 = i
accent-dwarf-words-replace-12 = Ah
accent-dwarf-words-13 = you're
accent-dwarf-words-replace-13 = ye're
accent-dwarf-words-14 = youre
accent-dwarf-words-replace-14 = yere
accent-dwarf-words-15 = you
accent-dwarf-words-replace-15 = ye
accent-dwarf-words-16 = i'll
accent-dwarf-words-replace-16 = A'll
accent-dwarf-words-17 = ill
accent-dwarf-words-replace-17 = all
accent-dwarf-words-18 = of
accent-dwarf-words-replace-18 = ae
accent-dwarf-words-19 = was
accent-dwarf-words-replace-19 = wis
accent-dwarf-words-20 = can't
accent-dwarf-words-replace-20 = cannae
accent-dwarf-words-21 = cant
accent-dwarf-words-replace-21 = cannae
accent-dwarf-words-22 = yourself
accent-dwarf-words-replace-22 = yersel
accent-dwarf-words-23 = where
accent-dwarf-words-replace-23 = whaur
accent-dwarf-words-24 = oh
accent-dwarf-words-replace-24 = ach
accent-dwarf-words-25 = little
accent-dwarf-words-replace-25 = wee
accent-dwarf-words-26 = small
accent-dwarf-words-replace-26 = wee
accent-dwarf-words-27 = shit
accent-dwarf-words-replace-27 = shite
accent-dwarf-words-28 = yeah
accent-dwarf-words-replace-28 = aye
accent-dwarf-words-29 = yea
accent-dwarf-words-replace-29 = aye
accent-dwarf-words-30 = yes
accent-dwarf-words-replace-30 = aye
accent-dwarf-words-31 = too
accent-dwarf-words-replace-31 = tae
accent-dwarf-words-32 = my
accent-dwarf-words-replace-32 = ma
accent-dwarf-words-33 = not
accent-dwarf-words-replace-33 = nae
accent-dwarf-words-34 = dad
accent-dwarf-words-replace-34 = da
accent-dwarf-words-35 = mom
accent-dwarf-words-replace-35 = maw

View File

@@ -6,3 +6,54 @@ accent-mobster-suffix-boss-3 = , capiche?
accent-mobster-suffix-minion-1 = , yeah!
accent-mobster-suffix-minion-2 = , boss says!
accent-mobster-words-1 = let me
accent-mobster-words-replace-1 = lemme
accent-mobster-words-2 = should
accent-mobster-words-replace-2 = oughta
accent-mobster-words-3 = the
accent-mobster-words-replace-3 = da
accent-mobster-words-4 = them
accent-mobster-words-replace-4 = dem
accent-mobster-words-5 = attack
accent-mobster-words-replace-5 = whack
accent-mobster-words-6 = kill
accent-mobster-words-replace-6 = whack
accent-mobster-words-7 = murder
accent-mobster-words-replace-7 = whack
accent-mobster-words-8 = dead
accent-mobster-words-replace-8 = sleepin' with da fishies
accent-mobster-words-9 = hey
accent-mobster-words-replace-9 = ey'o
accent-mobster-words-10 = hi
accent-mobster-words-replace-10 = ey'o
accent-mobster-words-11 = hello
accent-mobster-words-replace-11 = ey'o
accent-mobster-words-12 = rules
accent-mobster-words-replace-12 = roolz
accent-mobster-words-13 = you
accent-mobster-words-replace-13 = yous
accent-mobster-words-14 = have to
accent-mobster-words-replace-14 = gotta
accent-mobster-words-15 = going to
accent-mobster-words-replace-15 = boutta
accent-mobster-words-16 = about to
accent-mobster-words-replace-16 = boutta
accent-mobster-words-17 = here
accent-mobster-words-replace-17 = 'ere

View File

@@ -1,6 +1,8 @@
# Accents that work off of full replacements rather than word replacements.
- type: accent
id: cat
words:
fullReplacements:
- accent-words-cat-1
- accent-words-cat-2
- accent-words-cat-3
@@ -9,7 +11,7 @@
- type: accent
id: dog
words:
fullReplacements:
- accent-words-dog-1
- accent-words-dog-2
- accent-words-dog-3
@@ -18,7 +20,7 @@
- type: accent
id: mouse
words:
fullReplacements:
- accent-words-mouse-1
- accent-words-mouse-2
- accent-words-mouse-3
@@ -26,41 +28,41 @@
- type: accent
id: mumble
words:
- accent-words-mumble-1
- accent-words-mumble-2
- accent-words-mumble-3
fullReplacements:
- accent-words-mumble-1
- accent-words-mumble-2
- accent-words-mumble-3
- type: accent
id: silicon
words:
- accent-words-silicon-1
- accent-words-silicon-2
- accent-words-silicon-3
- accent-words-silicon-4
fullReplacements:
- accent-words-silicon-1
- accent-words-silicon-2
- accent-words-silicon-3
- accent-words-silicon-4
- type: accent
id: xeno
words:
- accent-words-xeno-1
- accent-words-xeno-2
- accent-words-xeno-3
- accent-words-xeno-4
fullReplacements:
- accent-words-xeno-1
- accent-words-xeno-2
- accent-words-xeno-3
- accent-words-xeno-4
- type: accent
id: zombie
words:
- accent-words-zombie-1
- accent-words-zombie-2
- accent-words-zombie-3
- accent-words-zombie-4
- accent-words-zombie-5
- accent-words-zombie-6
- accent-words-zombie-7
fullReplacements:
- accent-words-zombie-1
- accent-words-zombie-2
- accent-words-zombie-3
- accent-words-zombie-4
- accent-words-zombie-5
- accent-words-zombie-6
- accent-words-zombie-7
- type: accent
id: genericAggressive
words:
fullReplacements:
- accent-words-generic-aggressive-1
- accent-words-generic-aggressive-2
- accent-words-generic-aggressive-3
@@ -68,7 +70,7 @@
- type: accent
id: duck
words:
fullReplacements:
- accent-words-duck-1
- accent-words-duck-2
- accent-words-duck-3
@@ -76,7 +78,7 @@
- type: accent
id: chicken
words:
fullReplacements:
- accent-words-chicken-1
- accent-words-chicken-2
- accent-words-chicken-3
@@ -84,7 +86,7 @@
- type: accent
id: pig
words:
fullReplacements:
- accent-words-pig-1
- accent-words-pig-2
- accent-words-pig-3

View File

@@ -0,0 +1,63 @@
# Accents that work off of word replacements.
# this is kind of dumb but localization demands it.
# i guess you could just specify the prefix ('mobster') and count and let the system fill it
- type: accent
id: mobster
wordReplacements:
accent-mobster-words-1: accent-mobster-words-replace-1
accent-mobster-words-2: accent-mobster-words-replace-2
accent-mobster-words-3: accent-mobster-words-replace-3
accent-mobster-words-4: accent-mobster-words-replace-4
accent-mobster-words-5: accent-mobster-words-replace-5
accent-mobster-words-6: accent-mobster-words-replace-6
accent-mobster-words-7: accent-mobster-words-replace-7
accent-mobster-words-8: accent-mobster-words-replace-8
accent-mobster-words-9: accent-mobster-words-replace-9
accent-mobster-words-10: accent-mobster-words-replace-10
accent-mobster-words-11: accent-mobster-words-replace-11
accent-mobster-words-12: accent-mobster-words-replace-12
accent-mobster-words-13: accent-mobster-words-replace-13
accent-mobster-words-14: accent-mobster-words-replace-14
accent-mobster-words-15: accent-mobster-words-replace-15
accent-mobster-words-16: accent-mobster-words-replace-16
accent-mobster-words-17: accent-mobster-words-replace-17
- type: accent
id: dwarf
wordReplacements:
accent-dwarf-words-1: accent-dwarf-words-replace-1
accent-dwarf-words-2: accent-dwarf-words-replace-2
accent-dwarf-words-3: accent-dwarf-words-replace-3
accent-dwarf-words-4: accent-dwarf-words-replace-4
accent-dwarf-words-5: accent-dwarf-words-replace-5
accent-dwarf-words-6: accent-dwarf-words-replace-6
accent-dwarf-words-7: accent-dwarf-words-replace-7
accent-dwarf-words-8: accent-dwarf-words-replace-8
accent-dwarf-words-9: accent-dwarf-words-replace-9
accent-dwarf-words-10: accent-dwarf-words-replace-10
accent-dwarf-words-11: accent-dwarf-words-replace-11
accent-dwarf-words-12: accent-dwarf-words-replace-12
accent-dwarf-words-13: accent-dwarf-words-replace-13
accent-dwarf-words-14: accent-dwarf-words-replace-14
accent-dwarf-words-15: accent-dwarf-words-replace-15
accent-dwarf-words-16: accent-dwarf-words-replace-16
accent-dwarf-words-17: accent-dwarf-words-replace-17
accent-dwarf-words-18: accent-dwarf-words-replace-18
accent-dwarf-words-19: accent-dwarf-words-replace-19
accent-dwarf-words-20: accent-dwarf-words-replace-20
accent-dwarf-words-21: accent-dwarf-words-replace-21
accent-dwarf-words-22: accent-dwarf-words-replace-22
accent-dwarf-words-23: accent-dwarf-words-replace-23
accent-dwarf-words-24: accent-dwarf-words-replace-24
accent-dwarf-words-25: accent-dwarf-words-replace-25
accent-dwarf-words-26: accent-dwarf-words-replace-26
accent-dwarf-words-27: accent-dwarf-words-replace-27
accent-dwarf-words-28: accent-dwarf-words-replace-28
accent-dwarf-words-29: accent-dwarf-words-replace-29
accent-dwarf-words-30: accent-dwarf-words-replace-30
accent-dwarf-words-31: accent-dwarf-words-replace-31
accent-dwarf-words-32: accent-dwarf-words-replace-32
accent-dwarf-words-33: accent-dwarf-words-replace-33
accent-dwarf-words-34: accent-dwarf-words-replace-34
accent-dwarf-words-35: accent-dwarf-words-replace-35

View File

@@ -42,7 +42,8 @@
Male: UnisexDwarf
Female: UnisexDwarf
Unsexed: UnisexDwarf
- type: DwarfAccent
- type: ReplacementAccent
accent: dwarf
- type: Speech
speechSounds: Bass

View File

@@ -311,22 +311,6 @@ binds:
type: State
key: BackSpace
canRepeat: true
- function: TextDelete
type: State
key: Delete
canRepeat: true
- function: TextWordBackspace
type: State
key: BackSpace
mod1: Control
canRepeat: true
allowSubCombs: true
- function: TextWordDelete
type: State
key: Delete
mod1: Control
canRepeat: true
allowSubCombs: true
- function: TextNewline
type: State
key: Return
@@ -384,6 +368,10 @@ binds:
- function: TextScrollToBottom
type: State
key: PageDown
- function: TextDelete
type: State
key: Delete
canRepeat: true
- function: TextTabComplete
type: State
key: Tab