diff --git a/Content.Server/Verbs/Commands/InvokeVerbCommand.cs b/Content.Server/Verbs/Commands/InvokeVerbCommand.cs new file mode 100644 index 0000000000..7985fc761b --- /dev/null +++ b/Content.Server/Verbs/Commands/InvokeVerbCommand.cs @@ -0,0 +1,103 @@ +using System; +using System.Linq; +using System.Net.Security; +using Content.Server.Administration; +using Content.Shared.Administration; +using Content.Shared.Verbs; +using Robust.Shared.Console; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; + +namespace Content.Server.Verbs.Commands +{ + [AdminCommand(AdminFlags.Admin)] + public class InvokeVerbCommand : IConsoleCommand + { + public string Command => "invokeverb"; + public string Description => Loc.GetString("invoke-verb-command-description"); + public string Help => Loc.GetString("invoke-verb-command-help"); + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 3) + { + shell.WriteLine(Loc.GetString("invoke-verb-command-invalid-args")); + return; + } + + var entityManager = IoCManager.Resolve(); + var verbSystem = EntitySystem.Get(); + + // get the 'player' entity (defaulting to command user, otherwise uses a uid) + IEntity? playerEntity = null; + if (!int.TryParse(args[0], out var intPlayerUid)) + { + if (args[0] == "self" && shell.Player?.AttachedEntity != null) + { + playerEntity = shell.Player.AttachedEntity; + } + else + { + shell.WriteError(Loc.GetString("invoke-verb-command-invalid-player-uid")); + return; + } + } + else + { + entityManager.TryGetEntity(new EntityUid(intPlayerUid), out playerEntity); + } + + // gets the target entity + if (!int.TryParse(args[1], out var intUid)) + { + shell.WriteError(Loc.GetString("invoke-verb-command-invalid-target-uid")); + return; + } + + if (playerEntity == null) + { + shell.WriteError(Loc.GetString("invoke-verb-command-invalid-player-entity")); + return; + } + + var entUid = new EntityUid(intUid); + if (!entityManager.TryGetEntity(entUid, out var target)) + { + shell.WriteError(Loc.GetString("invoke-verb-command-invalid-target-entity")); + return; + } + + var verbName = args[2].ToLowerInvariant(); + var verbs = verbSystem.GetLocalVerbs( + target, playerEntity, VerbType.All, true + ); + + if ((Enum.TryParse(typeof(VerbType), verbName, ignoreCase: true, out var vtype) && + vtype is VerbType key) && + verbs.TryGetValue(key, out var vset) && + vset.Any()) + { + verbSystem.ExecuteVerb(vset.First()); + shell.WriteLine(Loc.GetString("invoke-verb-command-success", ("verb", verbName), ("target", target), ("player", playerEntity))); + return; + } + + foreach (var (_, set) in verbs) + { + foreach (var verb in set) + { + if (verb.Text.ToLowerInvariant() == verbName) + { + verbSystem.ExecuteVerb(verb); + shell.WriteLine(Loc.GetString("invoke-verb-command-success", ("verb", verb.Text), ("target", target), ("player", playerEntity))); + return; + } + } + } + + // found nothing + shell.WriteError(Loc.GetString("invoke-verb-command-verb-not-found", ("verb", verbName), ("target", target))); + } + } +} diff --git a/Content.Server/Verbs/Commands/ListVerbsCommand.cs b/Content.Server/Verbs/Commands/ListVerbsCommand.cs new file mode 100644 index 0000000000..57a27ba900 --- /dev/null +++ b/Content.Server/Verbs/Commands/ListVerbsCommand.cs @@ -0,0 +1,81 @@ +using Content.Server.Administration; +using Content.Shared.Administration; +using Content.Shared.Verbs; +using Robust.Shared.Console; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; + +namespace Content.Server.Verbs.Commands +{ + [AdminCommand(AdminFlags.Admin)] + public class ListVerbsCommand : IConsoleCommand + { + public string Command => "listverbs"; + public string Description => Loc.GetString("list-verbs-command-description"); + public string Help => Loc.GetString("list-verbs-command-help"); + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 2) + { + shell.WriteLine(Loc.GetString("list-verbs-command-invalid-args")); + return; + } + + var entityManager = IoCManager.Resolve(); + var verbSystem = EntitySystem.Get(); + + // get the 'player' entity (defaulting to command user, otherwise uses a uid) + IEntity? playerEntity = null; + if (!int.TryParse(args[0], out var intPlayerUid)) + { + if (args[0] == "self" && shell.Player?.AttachedEntity != null) + { + playerEntity = shell.Player.AttachedEntity; + } + else + { + shell.WriteError(Loc.GetString("list-verbs-command-invalid-player-uid")); + return; + } + } + else + { + entityManager.TryGetEntity(new EntityUid(intPlayerUid), out playerEntity); + } + + // gets the target entity + if (!int.TryParse(args[1], out var intUid)) + { + shell.WriteError(Loc.GetString("list-verbs-command-invalid-target-uid")); + return; + } + + if (playerEntity == null) + { + shell.WriteError(Loc.GetString("list-verbs-command-invalid-player-entity")); + return; + } + + var entUid = new EntityUid(intUid); + if (!entityManager.TryGetEntity(entUid, out var target)) + { + shell.WriteError(Loc.GetString("list-verbs-command-invalid-target-entity")); + return; + } + + var verbs = verbSystem.GetLocalVerbs( + target, playerEntity, VerbType.All, true + ); + + foreach (var (type, set) in verbs) + { + foreach (var verb in set) + { + shell.WriteLine(Loc.GetString("list-verbs-verb-listing", ("type", type), ("verb", verb.Text))); + } + } + } + } +} diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index 8468eb0930..ab89188823 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -9,34 +9,34 @@ namespace Content.Shared.Verbs /// Raises a number of events in order to get all verbs of the given type(s) defined in local systems. This /// does not request verbs from the server. /// - public virtual Dictionary> GetLocalVerbs(IEntity target, IEntity user, VerbType verbTypes) + public virtual Dictionary> GetLocalVerbs(IEntity target, IEntity user, VerbType verbTypes, bool force=false) { Dictionary> verbs = new(); if ((verbTypes & VerbType.Interaction) == VerbType.Interaction) { - GetInteractionVerbsEvent getVerbEvent = new(user, target); + GetInteractionVerbsEvent getVerbEvent = new(user, target, force); RaiseLocalEvent(target.Uid, getVerbEvent); verbs.Add(VerbType.Interaction, getVerbEvent.Verbs); } if ((verbTypes & VerbType.Activation) == VerbType.Activation) { - GetActivationVerbsEvent getVerbEvent = new(user, target); + GetActivationVerbsEvent getVerbEvent = new(user, target, force); RaiseLocalEvent(target.Uid, getVerbEvent); verbs.Add(VerbType.Activation, getVerbEvent.Verbs); } if ((verbTypes & VerbType.Alternative) == VerbType.Alternative) { - GetAlternativeVerbsEvent getVerbEvent = new(user, target); + GetAlternativeVerbsEvent getVerbEvent = new(user, target, force); RaiseLocalEvent(target.Uid, getVerbEvent); verbs.Add(VerbType.Alternative, getVerbEvent.Verbs); } if ((verbTypes & VerbType.Other) == VerbType.Other) { - GetOtherVerbsEvent getVerbEvent = new(user, target); + GetOtherVerbsEvent getVerbEvent = new(user, target, force); RaiseLocalEvent(target.Uid, getVerbEvent); verbs.Add(VerbType.Other, getVerbEvent.Verbs); } diff --git a/Content.Shared/Verbs/VerbEvents.cs b/Content.Shared/Verbs/VerbEvents.cs index 974614f3e2..34245c9c14 100644 --- a/Content.Shared/Verbs/VerbEvents.cs +++ b/Content.Shared/Verbs/VerbEvents.cs @@ -74,7 +74,7 @@ namespace Content.Shared.Verbs /// public class GetInteractionVerbsEvent : GetVerbsEvent { - public GetInteractionVerbsEvent(IEntity user, IEntity target) : base(user, target) { } + public GetInteractionVerbsEvent(IEntity user, IEntity target, bool force=false) : base(user, target, force) { } } /// @@ -88,11 +88,11 @@ namespace Content.Shared.Verbs /// public class GetActivationVerbsEvent : GetVerbsEvent { - public GetActivationVerbsEvent(IEntity user, IEntity target) : base(user, target) { } + public GetActivationVerbsEvent(IEntity user, IEntity target, bool force=false) : base(user, target, force) { } } /// - /// Request alternative-interaction verbs. + /// Request alternative-interaction verbs. /// /// /// When interacting with an entity via alt + left-click/E/Z the highest priority alt-interact verb is executed. @@ -100,11 +100,11 @@ namespace Content.Shared.Verbs /// public class GetAlternativeVerbsEvent : GetVerbsEvent { - public GetAlternativeVerbsEvent(IEntity user, IEntity target) : base(user, target) { } + public GetAlternativeVerbsEvent(IEntity user, IEntity target, bool force=false) : base(user, target, force) { } } /// - /// Request Miscellaneous verbs. + /// Request Miscellaneous verbs. /// /// /// Includes (nearly) global interactions like "examine", "pull", or "debug". These verbs are collectively shown @@ -112,7 +112,7 @@ namespace Content.Shared.Verbs /// public class GetOtherVerbsEvent : GetVerbsEvent { - public GetOtherVerbsEvent(IEntity user, IEntity target) : base(user, target) { } + public GetOtherVerbsEvent(IEntity user, IEntity target, bool force=false) : base(user, target, force) { } } /// @@ -171,18 +171,18 @@ namespace Content.Shared.Verbs /// public IEntity? Using; - public GetVerbsEvent(IEntity user, IEntity target) + public GetVerbsEvent(IEntity user, IEntity target, bool force=false) { User = user; Target = target; - CanAccess = (Target == User) || user.IsInSameOrParentContainer(target) && + CanAccess = force || (Target == User) || user.IsInSameOrParentContainer(target) && EntitySystem.Get().InRangeUnobstructed(user, target); // A large number of verbs need to check action blockers. Instead of repeatedly having each system individually // call ActionBlocker checks, just cache it for the verb request. var actionBlockerSystem = EntitySystem.Get(); - CanInteract = actionBlockerSystem.CanInteract(user); + CanInteract = force || actionBlockerSystem.CanInteract(user); if (!user.TryGetComponent(out Hands) || !actionBlockerSystem.CanUse(user)) diff --git a/Resources/Locale/en-US/verbs/invoke-verb-command.ftl b/Resources/Locale/en-US/verbs/invoke-verb-command.ftl new file mode 100644 index 0000000000..c805eb6364 --- /dev/null +++ b/Resources/Locale/en-US/verbs/invoke-verb-command.ftl @@ -0,0 +1,17 @@ +### Localization used for the invoke verb command. +# Mostly help + error messages. + +invoke-verb-command-description = Invokes a verb with the given name on an entity, with the player entity +invoke-verb-command-help = invokeverb + +invoke-verb-command-invalid-args = invokeverb takes 2 arguments. + +invoke-verb-command-invalid-player-uid = Player uid could not be parsed, or "self" was not passed. +invoke-verb-command-invalid-target-uid = Target uid could not be parsed. + +invoke-verb-command-invalid-player-entity = Player uid given does not correspond to a valid entity. +invoke-verb-command-invalid-target-entity = Target uid given does not correspond to a valid entity. + +invoke-verb-command-success = Invoked verb '{ $verb }' on { $target } with { $player } as the user. + +invoke-verb-command-verb-not-found = Could not find verb { $verb } on { $target }. diff --git a/Resources/Locale/en-US/verbs/list-verbs-command.ftl b/Resources/Locale/en-US/verbs/list-verbs-command.ftl new file mode 100644 index 0000000000..75a1df3ffa --- /dev/null +++ b/Resources/Locale/en-US/verbs/list-verbs-command.ftl @@ -0,0 +1,15 @@ +### Localization used for the list verbs command. +# Mostly help + error messages. + +list-verbs-command-description = Lists all verbs that a player can use on a given entity. +list-verbs-command-help = listverbs + +list-verbs-command-invalid-args = listverbs takes 2 arguments. + +list-verbs-command-invalid-player-uid = Player uid could not be parsed, or "self" was not passed. +list-verbs-command-invalid-target-uid = Target uid could not be parsed. + +list-verbs-command-invalid-player-entity = Player uid given does not correspond to a valid entity. +list-verbs-command-invalid-target-entity = Target uid given does not correspond to a valid entity. + +list-verbs-verb-listing = { $type }: { $verb }