-
Notifications
You must be signed in to change notification settings - Fork 147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Foreigner Traits #525
Foreigner Traits #525
Changes from all commits
f5e7901
3857823
0eedda5
0c82df8
9852374
8dc91c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using Content.Shared.Language; | ||
using Content.Shared.Language.Systems; | ||
using Robust.Shared.Prototypes; | ||
|
||
namespace Content.Server.Traits.Assorted; | ||
|
||
/// <summary> | ||
/// When applied to a not-yet-spawned player entity, removes <see cref="BaseLanguage"/> from the lists of their languages | ||
/// and gives them a translator instead. | ||
/// </summary> | ||
[RegisterComponent] | ||
public sealed partial class ForeignerTraitComponent : Component | ||
{ | ||
/// <summary> | ||
/// The "base" language that is to be removed and substituted with a translator. | ||
/// By default, equals to the fallback language, which is GalacticCommon. | ||
/// </summary> | ||
[DataField] | ||
public ProtoId<LanguagePrototype> BaseLanguage = SharedLanguageSystem.FallbackLanguagePrototype; | ||
|
||
/// <summary> | ||
/// Whether this trait prevents the entity from understanding the base language. | ||
/// </summary> | ||
public bool CantUnderstand = true; | ||
|
||
/// <summary> | ||
/// Whether this trait prevents the entity from speaking the base language. | ||
/// </summary> | ||
public bool CantSpeak = true; | ||
|
||
/// <summary> | ||
/// The base translator prototype to use when creating a translator for the entity. | ||
/// </summary> | ||
[DataField(required: true)] | ||
public ProtoId<EntityPrototype> BaseTranslator = default!; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
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 == null) | ||
{ | ||
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, entity.Comp.BaseLanguage, entity.Comp.CantSpeak, entity.Comp.CantUnderstand, knowledge); | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Tries to create and give the entity a translator to 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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,13 @@ | ||
translator-component-shutoff = The {$translator} shuts off. | ||
translator-component-turnon = The {$translator} turns on. | ||
translator-enabled = It appears to be active. | ||
translator-disabled = It appears to be disabled. | ||
translator-implanter-refuse = The {$implanter} has no effect on {$target}. | ||
translator-implanter-success = The {$implanter} successfully injected {$target}. | ||
translator-implanter-ready = This implanter appears to be ready to use. | ||
translator-implanter-used = This implanter seems empty. | ||
|
||
translator-examined-langs-understood = It can translate from: [color=green]{$languages}[/color]. | ||
translator-examined-langs-spoken = It can translate to: [color=green]{$languages}[/color]. | ||
translator-examined-requires-any = It requires you to know at least one of these languages: [color=yellow]{$languages}[/color]. | ||
translator-examined-requires-all = It requires you to know all of these languages: [color=yellow]{$languages}[/color]. | ||
translator-examined-enabled = It appears to be [color=green]active[/color]. | ||
translator-examined-disabled = It appears to be [color=red]turned off[/color]. |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -42,3 +42,13 @@ trait-name-Thieving = Thieving | |||||
trait-description-Thieving = | ||||||
You are deft with your hands, and talented at convincing people of their belongings. | ||||||
You can identify pocketed items, steal them quieter, and steal ~33% faster. | ||||||
|
||||||
trait-name-ForeignerLight = Foreigner (light) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Don't really like the Light appended to the end; this should differentiate them enough I guess, makes some sense too? Shrug There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally this would also change the Loc string but I'm on mobile and don't want to go looking for every instance manually.... |
||||||
trait-description-ForeignerLight = | ||||||
You struggle to learn this station's primary language, and as such, cannot speak it. You can, however, comprehend what others say in that language. | ||||||
To help you overcome this obstacle, you are equipped with a translator that helps you speak in this station's primary language. | ||||||
|
||||||
trait-name-Foreigner = Foreigner | ||||||
trait-description-Foreigner = | ||||||
For one reason or another you do not speak this station's primary language. | ||||||
Instead, you have a translator issued to you that only you can use. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be worth discussing, but I just thought that its an option to make it so that this item can be bought in your loadouts if you have the trait. But that may also fuck over players who aren't aware that they don't spawn with the item.