Files
wwdpublic/Content.Server/Traits/Assorted/ForeignerTraitSystem.cs
Mnemotechnican 82cbea2aef Language Refactor 3 (#937)
# Description
This significantly improves the quality of the language system by fixing
the mistakes I've made almost a year ago while developing it.

Mainly, this throws away the old half-broken way of networking in favor
of the component state system provided by RT. Language speaker comp is
now shared with SendOnlyToOwner = true, and its state is handled
manually.

In addition to that, this brings the following changes:
- UniversalLanguageSpeaker and LanguageKnowledge are now server-side
- DetermineLanguagesEvent and LanguagesUpdateEvent are now shared (so
that future systems can be built in shared, if needed)
- Everything now uses the ProtoId<LanguagePrototype> type instead of raw
strings (god, I hated those so much)
- The server-side language system now accepts Entity<T?> arguments
instead of EntityUid + T
- UniversalLanguageSpeaker is now based on DetermineEntityLanguagesEvent
and gets an Enabled field, which allows to turn it off. This may have
some use in the future.
- Some minor cleanup

<!--
TODO MEDIA

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

![Example Media Embed](https://example.com/thisimageisntreal.png)

</p>
</details>

-->

# Changelog
No cl

---------

Co-authored-by: VMSolidus <evilexecutive@gmail.com>
2024-10-19 13:41:35 +07:00

106 lines
4.3 KiB
C#

using System.Linq;
using Content.Server.Hands.Systems;
using Content.Server.Language;
using Content.Server.Storage.EntitySystems;
using Content.Shared.Clothing.Components;
using Content.Shared.Inventory;
using Content.Shared.Language;
using Content.Shared.Language.Components;
using Content.Shared.Language.Components.Translators;
using Content.Shared.Storage;
using Robust.Shared.Prototypes;
namespace Content.Server.Traits.Assorted;
public sealed partial class ForeignerTraitSystem : EntitySystem
{
[Dependency] private readonly EntityManager _entMan = default!;
[Dependency] private readonly HandsSystem _hands = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly LanguageSystem _languages = default!;
[Dependency] private readonly StorageSystem _storage = default!;
public override void Initialize()
{
SubscribeLocalEvent<ForeignerTraitComponent, ComponentInit>(OnSpawn); // TraitSystem adds it after PlayerSpawnCompleteEvent so it's fine
}
private void OnSpawn(Entity<ForeignerTraitComponent> entity, ref ComponentInit args)
{
if (entity.Comp.CantUnderstand && !entity.Comp.CantSpeak)
Log.Warning($"Allowing entity {entity.Owner} to speak a language but not understand it leads to undefined behavior.");
if (!TryComp<LanguageKnowledgeComponent>(entity, out var knowledge))
{
Log.Warning($"Entity {entity.Owner} does not have a LanguageKnowledge but has a ForeignerTrait!");
return;
}
var alternateLanguage = knowledge.SpokenLanguages.Find(it => it != entity.Comp.BaseLanguage);
if (alternateLanguage == default)
{
Log.Warning($"Entity {entity.Owner} does not have an alternative language to choose from (must have at least one non-GC for ForeignerTrait)!");
return;
}
if (TryGiveTranslator(entity.Owner, entity.Comp.BaseTranslator, entity.Comp.BaseLanguage, alternateLanguage, out var translator))
{
_languages.RemoveLanguage(entity.Owner, entity.Comp.BaseLanguage, entity.Comp.CantSpeak, entity.Comp.CantUnderstand);
}
}
/// <summary>
/// Tries to create and give the entity a translator that translates speech between the two specified languages.
/// </summary>
public bool TryGiveTranslator(
EntityUid uid,
string baseTranslatorPrototype,
ProtoId<LanguagePrototype> translatorLanguage,
ProtoId<LanguagePrototype> entityLanguage,
out EntityUid result)
{
result = EntityUid.Invalid;
if (translatorLanguage == entityLanguage)
return false;
var translator = _entMan.SpawnNextToOrDrop(baseTranslatorPrototype, uid);
result = translator;
if (!TryComp<HandheldTranslatorComponent>(translator, out var handheld))
{
handheld = AddComp<HandheldTranslatorComponent>(translator);
handheld.ToggleOnInteract = true;
handheld.SetLanguageOnInteract = true;
}
// Allows to speak the specified language and requires entities language.
handheld.SpokenLanguages = [translatorLanguage];
handheld.UnderstoodLanguages = [translatorLanguage];
handheld.RequiredLanguages = [entityLanguage];
// Try to put it in entities hand
if (_hands.TryPickupAnyHand(uid, translator, false, false, false))
return true;
// Try to find a valid clothing slot on the entity and equip the translator there
if (TryComp<ClothingComponent>(translator, out var clothing)
&& clothing.Slots != SlotFlags.NONE
&& _inventory.TryGetSlots(uid, out var slots)
&& slots.Any(it => _inventory.TryEquip(uid, translator, it.Name, true, false)))
return true;
// Try to put the translator into entities bag, if it has one
if (_inventory.TryGetSlotEntity(uid, "back", out var bag)
&& TryComp<StorageComponent>(bag, out var storage)
&& _storage.Insert(bag.Value, translator, out _, null, storage, false, false))
return true;
// If all of the above has failed, just drop it at the same location as the entity
// This should ideally never happen, but who knows.
Transform(translator).Coordinates = Transform(uid).Coordinates;
return true;
}
}