Skip to content

Commit

Permalink
Admeme Language Commands (Attempt 2) (#565)
Browse files Browse the repository at this point in the history
# Description
#562 but using commands instead of tricks.

Less user-friendly but makes you look like a hacker when you use them
:trollface:


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


![image](https://github.com/user-attachments/assets/818b6226-aea8-4f9a-b30d-5df39b8041e6)

![image](https://github.com/user-attachments/assets/f3c40feb-f84d-4083-bc9a-44f7b1dfb30d)

![image](https://github.com/user-attachments/assets/31827207-4903-4df1-9074-eb060d0cf63c)

(ignore the missing locale, I fixes)

</p></details>

# Changelog

:cl:
- add: Admin tooling: added several admin commands to help manipulate
entities' languages.

---------

Signed-off-by: Mnemotechnican <69920617+Mnemotechnician@users.noreply.github.com>
Co-authored-by: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com>
  • Loading branch information
Mnemotechnician and DEATHB4DEFEAT authored Aug 6, 2024
1 parent b6b21da commit 81ad859
Show file tree
Hide file tree
Showing 3 changed files with 248 additions and 0 deletions.
75 changes: 75 additions & 0 deletions Content.Server/Language/Commands/AdminLanguageCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using Content.Server.Administration;
using Content.Shared.Administration;
using Content.Shared.Language;
using Content.Shared.Language.Components;
using Content.Shared.Language.Systems;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;

namespace Content.Server.Language.Commands;

[ToolshedCommand(Name = "language"), AdminCommand(AdminFlags.Admin)]
public sealed class AdminLanguageCommand : ToolshedCommand
{
private LanguageSystem? _languagesField;
private LanguageSystem Languages => _languagesField ??= GetSys<LanguageSystem>();

[CommandImplementation("add")]
public EntityUid AddLanguage(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<string, Prototype<LanguagePrototype>> @ref,
[CommandArgument] bool canSpeak = true,
[CommandArgument] bool canUnderstand = true
)
{
var language = @ref.Evaluate(ctx)!;

if (language == SharedLanguageSystem.UniversalPrototype)
{
EnsureComp<UniversalLanguageSpeakerComponent>(input);
Languages.UpdateEntityLanguages(input);
}
else
{
EnsureComp<LanguageSpeakerComponent>(input);
Languages.AddLanguage(input, language, canSpeak, canUnderstand);
}

return input;
}

[CommandImplementation("rm")]
public EntityUid RemoveLanguage(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<string, Prototype<LanguagePrototype>> @ref,
[CommandArgument] bool removeSpeak = true,
[CommandArgument] bool removeUnderstand = true
)
{
var language = @ref.Evaluate(ctx)!;
if (language == SharedLanguageSystem.UniversalPrototype && HasComp<UniversalLanguageSpeakerComponent>(input))
{
RemComp<UniversalLanguageSpeakerComponent>(input);
EnsureComp<LanguageSpeakerComponent>(input);
}
// We execute this branch even in case of universal so that it gets removed if it was added manually to the LanguageKnowledge
Languages.RemoveLanguage(input, language, removeSpeak, removeUnderstand);

return input;
}

[CommandImplementation("lsspoken")]
public IEnumerable<string> ListSpoken([PipedArgument] EntityUid input)
{
return Languages.GetSpokenLanguages(input);
}

[CommandImplementation("lsunderstood")]
public IEnumerable<string> ListUnderstood([PipedArgument] EntityUid input)
{
return Languages.GetUnderstoodLanguages(input);
}
}
155 changes: 155 additions & 0 deletions Content.Server/Language/Commands/AdminTranslatorCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Administration;
using Content.Shared.Administration;
using Content.Shared.Language;
using Content.Shared.Language.Components;
using Content.Shared.Language.Components.Translators;
using Content.Shared.Language.Systems;
using Robust.Server.Containers;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;

namespace Content.Server.Language.Commands;

[ToolshedCommand(Name = "translator"), AdminCommand(AdminFlags.Admin)]
public sealed class AdminTranslatorCommand : ToolshedCommand
{
private LanguageSystem? _languagesField;
private ContainerSystem? _containersField;

private ContainerSystem Containers => _containersField ??= GetSys<ContainerSystem>();
private LanguageSystem Languages => _languagesField ??= GetSys<LanguageSystem>();

[CommandImplementation("addlang")]
public EntityUid AddLanguage(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<string, Prototype<LanguagePrototype>> @ref,
[CommandArgument] bool addSpeak = true,
[CommandArgument] bool addUnderstand = true
)
{
var language = @ref.Evaluate(ctx)!;
// noob trap - needs a universallanguagespeakercomponent
if (language == SharedLanguageSystem.UniversalPrototype)
throw new ArgumentException(Loc.GetString("command-language-error-this-will-not-work"));

if (!TryGetTranslatorComp(input, out var translator))
throw new ArgumentException(Loc.GetString("command-language-error-not-a-translator", ("entity", input)));

if (addSpeak && !translator.SpokenLanguages.Contains(language))
translator.SpokenLanguages.Add(language);
if (addUnderstand && !translator.UnderstoodLanguages.Contains(language))
translator.UnderstoodLanguages.Add(language);

UpdateTranslatorHolder(input);

return input;
}

[CommandImplementation("rmlang")]
public EntityUid RemoveLanguage(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<string, Prototype<LanguagePrototype>> @ref,
[CommandArgument] bool removeSpeak = true,
[CommandArgument] bool removeUnderstand = true
)
{
var language = @ref.Evaluate(ctx)!;
if (!TryGetTranslatorComp(input, out var translator))
throw new ArgumentException(Loc.GetString("command-language-error-not-a-translator", ("entity", input)));

if (removeSpeak)
translator.SpokenLanguages.Remove(language);
if (removeUnderstand)
translator.UnderstoodLanguages.Remove(language);

UpdateTranslatorHolder(input);

return input;
}

[CommandImplementation("addrequired")]
public EntityUid AddRequiredLanguage(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<string, Prototype<LanguagePrototype>> @ref)
{
var language = @ref.Evaluate(ctx)!;
if (!TryGetTranslatorComp(input, out var translator))
throw new ArgumentException(Loc.GetString("command-language-error-not-a-translator", ("entity", input)));

if (!translator.RequiredLanguages.Contains(language))
{
translator.RequiredLanguages.Add(language);
UpdateTranslatorHolder(input);
}

return input;
}

[CommandImplementation("rmrequired")]
public EntityUid RemoveRequiredLanguage(
[CommandInvocationContext] IInvocationContext ctx,
[PipedArgument] EntityUid input,
[CommandArgument] ValueRef<string, Prototype<LanguagePrototype>> @ref)
{
var language = @ref.Evaluate(ctx)!;
if (!TryGetTranslatorComp(input, out var translator))
throw new ArgumentException(Loc.GetString("command-language-error-not-a-translator", ("entity", input)));

if (translator.RequiredLanguages.Remove(language))
UpdateTranslatorHolder(input);

return input;
}

[CommandImplementation("lsspoken")]
public IEnumerable<string> ListSpoken([PipedArgument] EntityUid input)
{
if (!TryGetTranslatorComp(input, out var translator))
return [];
return translator.SpokenLanguages;
}

[CommandImplementation("lsunderstood")]
public IEnumerable<string> ListUnderstood([PipedArgument] EntityUid input)
{
if (!TryGetTranslatorComp(input, out var translator))
return [];
return translator.UnderstoodLanguages;
}

[CommandImplementation("lsrequired")]
public IEnumerable<string> ListRequired([PipedArgument] EntityUid input)
{
if (!TryGetTranslatorComp(input, out var translator))
return [];
return translator.RequiredLanguages;
}

private bool TryGetTranslatorComp(EntityUid uid, [NotNullWhen(true)] out BaseTranslatorComponent? translator)
{
if (TryComp<HandheldTranslatorComponent>(uid, out var handheld))
translator = handheld;
else if (TryComp<TranslatorImplantComponent>(uid, out var implant))
translator = implant;
else if (TryComp<IntrinsicTranslatorComponent>(uid, out var intrinsic))
translator = intrinsic;
else
translator = null;

return translator != null;
}

private void UpdateTranslatorHolder(EntityUid translator)
{
if (!Containers.TryGetContainingContainer(translator, out var cont)
|| cont.Owner is not { Valid: true } holder)
return;

Languages.UpdateEntityLanguages(holder);
}
}
18 changes: 18 additions & 0 deletions Resources/Locale/en-US/language/commands.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,21 @@ command-language-entry = {$id}. {$language} - {$name}
command-language-invalid-number = The language number must be between 0 and {$total}. Alternatively, use the language name.
command-language-invalid-language = The language {$id} does not exist or you cannot speak it.
# toolshed

command-description-language-add = Adds a new language to the piped entity. The two last arguments indicate whether it should be spoken/understood. Example: 'self language:add "Canilunzt" true true'
command-description-language-rm = Removes a language from the piped entity. Works similarly to language:add. Example: 'self language:rm "GalacticCommon" true true'.
command-description-language-lsspoken = Lists all languages the entity can speak. Example: 'self language:lsspoken'
command-description-language-lsunderstood = Lists all languages the entity can understand. Example: 'self language:lssunderstood'
command-description-translator-addlang = Adds a new target language to the piped translator entity. See language:add for details.
command-description-translator-rmlang = Removes a target language from the piped translator entity. See language:rm for details.
command-description-translator-addrequired = Adds a new required language to the piped translator entity. Example: 'ent 1234 translator:addrequired "GalacticCommon"'
command-description-translator-rmrequired = Removes a required language from the piped translator entity. Example: 'ent 1234 translator:rmrequired "GalacticCommon"'
command-description-translator-lsspoken = Lists all spoken languages for the piped translator entity. Example: 'ent 1234 translator:lsspoken'
command-description-translator-lsunderstood = Lists all understood languages for the piped translator entity. Example: 'ent 1234 translator:lssunderstood'
command-description-translator-lsrequired = Lists all required languages for the piped translator entity. Example: 'ent 1234 translator:lsrequired'
command-language-error-this-will-not-work = This will not work.
command-language-error-not-a-translator = Entity {$entity} is not a translator.

0 comments on commit 81ad859

Please sign in to comment.