Files
wwdpublic/Content.Shared/Language/Systems/SharedLanguageSystem.cs
Mnemotechnican 9d5f3b65df Refactor the Language System (#459)
# Description
The language PR was merged early and OH GOD I ALREADY REGRET IT

This PR is intended to provide the missing refactors and address the
issues that were missed due to the early merge.

---

# TODO
- [X] Introduced a polymorphic obfuscation property to the
LanguagePrototype - now it supports more than just 2 hardcoded methods,
and each method can be configured per-language. Currently there are 3
obfuscation methods: replacement (same as replacement accent),
obuscation by syllables and obfuscation by phrases.
- [X] Refactored the existing obfuscation methods to not be a big
hardcoded mess.
- [X] Updated the existing languages accordingly: animalistic languages
are now less of an unreadable mess and include less syllables. Certain
languages like binary and snake seriously benefit from that.
- [X] Refactored the existing commands in response to the
never-addressed review (it got lost among hundreds of others)
- [X] Refactored the commands to be more user-friendly (you can now use
the number of the language in saylang and languageselect which can allow
using keybinds to switch between languages)
- [X] Moved a lot of obfuscation-related stuff from server to shared.
The actual obfuscation process, however, is still done on the server.
That may or may not be subject to change, too.
- [X] Refactored the entire process of resolution of entities'
languages. Instead of raising an event every time it's required to learn
what languages an entity knows, the lists of ALL languages available to
the entity (including via translators) is stored in
LanguageSpeakerComponent and only updated when necessary (e.g. when a
translator gets toggled). The list of languages the entity knows on its
own is now stored in LanguageKnowledgeComponent.
- [X] Made handheld translators automatically change your current
language when activated.
- [X] Rewrote the translator implanter system, now using the real
implants and implanters
- [ ] Rebalance science stuff (translators are incredibly expensive for
what they're worth)
- [ ] Uhhh stuff

---

<details><summary><h1>Media</h1></summary>
<p>

N/A for now

</p>
</details>

---

# Changelog

🆑
- tweak: Translator implants are now proper implants that can be
removed.
- tweak: Animalistic languages should now look less messy.
- fix: Hopefully fixed language menu desync and other issues.

---------

Signed-off-by: Mnemotechnican <69920617+Mnemotechnician@users.noreply.github.com>
2024-07-05 19:49:47 -04:00

68 lines
2.8 KiB
C#

using System.Text;
using Content.Shared.GameTicking;
using Robust.Shared.Prototypes;
namespace Content.Shared.Language.Systems;
public abstract class SharedLanguageSystem : EntitySystem
{
/// <summary>
/// The language used as a fallback in cases where an entity suddenly becomes a language speaker (e.g. the usage of make-sentient)
/// </summary>
[ValidatePrototypeId<LanguagePrototype>]
public static readonly string FallbackLanguagePrototype = "GalacticCommon";
/// <summary>
/// The language whose speakers are assumed to understand and speak every language. Should never be added directly.
/// </summary>
[ValidatePrototypeId<LanguagePrototype>]
public static readonly string UniversalPrototype = "Universal";
/// <summary>
/// A cached instance of <see cref="UniversalPrototype"/>
/// </summary>
public static LanguagePrototype Universal { get; private set; } = default!;
[Dependency] protected readonly IPrototypeManager _prototype = default!;
[Dependency] protected readonly SharedGameTicker _ticker = default!;
public override void Initialize()
{
Universal = _prototype.Index<LanguagePrototype>("Universal");
}
public LanguagePrototype? GetLanguagePrototype(string id)
{
_prototype.TryIndex<LanguagePrototype>(id, out var proto);
return proto;
}
/// <summary>
/// Obfuscate a message using the given language.
/// </summary>
public string ObfuscateSpeech(string message, LanguagePrototype language)
{
var builder = new StringBuilder();
var method = language.Obfuscation;
method.Obfuscate(builder, message, this);
return builder.ToString();
}
/// <summary>
/// Generates a stable pseudo-random number in the range (min, max) (inclusively) for the given seed.
/// One seed always corresponds to one number, however the resulting number also depends on the current round number.
/// This method is meant to be used in <see cref="ObfuscationMethod"/> to provide stable obfuscation.
/// </summary>
internal int PseudoRandomNumber(int seed, int min, int max)
{
// Using RobustRandom or System.Random here is a bad idea because this method can get called hundreds of times per message.
// Each call would require us to allocate a new instance of random, which would lead to lots of unnecessary calculations.
// Instead, we use a simple but effective algorithm derived from the C language.
// It does not produce a truly random number, but for the purpose of obfuscating messages in an RP-based game it's more than alright.
seed = seed ^ (_ticker.RoundId * 127);
var random = seed * 1103515245 + 12345;
return min + Math.Abs(random) % (max - min + 1);
}
}