From 4a2875dca6fe21f7295fe4dab041c615bd21165c Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Fri, 27 Nov 2020 08:18:33 +0100 Subject: [PATCH] Improve hurt command feedback and usability (#2559) * Improve hurt command feedback and usability * Move hurt command to its own separate class away from body commands * NuLlAbLe * NuLlAbLe part 2 * Change hurt command to use a string builder * Char overload * Give back the number of arguments when unexpected * RemieMoment --- Content.Server/Commands/HurtCommand.cs | 225 ++++++++++++++++++ .../Components/Body/BodyCommands.cs | 87 +------ 2 files changed, 226 insertions(+), 86 deletions(-) create mode 100644 Content.Server/Commands/HurtCommand.cs diff --git a/Content.Server/Commands/HurtCommand.cs b/Content.Server/Commands/HurtCommand.cs new file mode 100644 index 0000000000..1810852aaf --- /dev/null +++ b/Content.Server/Commands/HurtCommand.cs @@ -0,0 +1,225 @@ +#nullable enable +using System; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using Content.Server.Administration; +using Content.Shared.Administration; +using Content.Shared.Damage; +using Content.Shared.GameObjects.Components.Damage; +using Robust.Server.Interfaces.Console; +using Robust.Server.Interfaces.Player; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; + +namespace Content.Server.Commands +{ + [AdminCommand(AdminFlags.Fun)] + class HurtCommand : IClientCommand + { + public string Command => "hurt"; + public string Description => "Ouch"; + public string Help => $"Usage: {Command} () ()"; + + private string DamageTypes() + { + var msg = new StringBuilder(); + foreach (var dClass in Enum.GetNames(typeof(DamageClass))) + { + msg.Append($"\n{dClass}"); + + var types = Enum.Parse(dClass).ToTypes(); + + if (types.Count > 0) + { + msg.Append(": "); + msg.AppendJoin('|', types); + } + } + + return $"Damage Types:{msg}"; + } + + private delegate void Damage(IDamageableComponent damageable, bool ignoreResistances); + + private bool TryParseEntity(IConsoleShell shell, IPlayerSession? player, string arg, + [NotNullWhen(true)] out IEntity? entity) + { + entity = null; + + if (arg == "_") + { + var playerEntity = player?.AttachedEntity; + + if (playerEntity == null) + { + shell.SendText(player, $"You must have a player entity to use this command without specifying an entity.\n{Help}"); + return false; + } + + entity = playerEntity; + return true; + } + + if (!EntityUid.TryParse(arg, out var entityUid)) + { + shell.SendText(player, $"{arg} is not a valid entity uid.\n{Help}"); + + return false; + } + + var entityManager = IoCManager.Resolve(); + + if (!entityManager.TryGetEntity(entityUid, out var parsedEntity)) + { + shell.SendText(player, $"No entity found with uid {entityUid}"); + + return false; + } + + entity = parsedEntity; + return true; + } + + private bool TryParseDamageArgs( + IConsoleShell shell, + IPlayerSession? player, + string[] args, + [NotNullWhen(true)] out Damage? func) + { + if (!int.TryParse(args[1], out var amount)) + { + shell.SendText(player, $"{args[1]} is not a valid damage integer."); + + func = null; + return false; + } + + if (Enum.TryParse(args[0], true, out var damageClass)) + { + func = (damageable, ignoreResistances) => + { + if (!damageable.DamageClasses.ContainsKey(damageClass)) + { + shell.SendText(player, $"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage class {damageClass}"); + + return; + } + + if (!damageable.ChangeDamage(damageClass, amount, ignoreResistances)) + { + shell.SendText(player, $"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage."); + + return; + } + + shell.SendText(player, + $"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageClass} damage{(ignoreResistances ? ", ignoring resistances." : ".")}"); + }; + + return true; + } + // Fall back to DamageType + else if (Enum.TryParse(args[0], true, out var damageType)) + { + func = (damageable, ignoreResistances) => + { + if (!damageable.DamageTypes.ContainsKey(damageType)) + { + shell.SendText(player, $"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage class {damageType}"); + + return; + } + + if (!damageable.ChangeDamage(damageType, amount, ignoreResistances)) + { + shell.SendText(player, $"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage."); + + return; + } + + shell.SendText(player, $"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageType} damage{(ignoreResistances ? ", ignoring resistances." : ".")}"); + }; + + return true; + } + else + { + shell.SendText(player, $"{args[0]} is not a valid damage class or type."); + + var types = DamageTypes(); + shell.SendText(player, types); + + func = null; + return false; + } + } + + public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args) + { + bool ignoreResistances; + IEntity entity; + Damage? damageFunc; + + switch (args.Length) + { + // Check if we have enough for the dmg types to show + case var n when n > 0 && (args[0] == "?" || args[0] == "¿"): + var types = DamageTypes(); + + if (args[0] == "¿") + { + types = types.Replace('e', 'é'); + } + + shell.SendText(player, types); + + return; + // Not enough args + case var n when n < 2: + shell.SendText(player, $"Invalid number of arguments ({args.Length}).\n{Help}"); + return; + case var n when n >= 2 && n <= 4: + if (!TryParseDamageArgs(shell, player, args, out damageFunc)) + { + return; + } + + var entityUid = n == 2 ? "_" : args[2]; + + if (!TryParseEntity(shell, player, entityUid, out var parsedEntity)) + { + return; + } + + entity = parsedEntity; + + if (n == 4) + { + if (!bool.TryParse(args[3], out ignoreResistances)) + { + shell.SendText(player, $"{args[3]} is not a valid boolean value for ignoreResistances.\n{Help}"); + return; + } + } + else + { + ignoreResistances = false; + } + + break; + default: + shell.SendText(player, $"Invalid amount of arguments ({args.Length}).\n{Help}"); + return; + } + + if (!entity.TryGetComponent(out IDamageableComponent? damageable)) + { + shell.SendText(player, $"Entity {entity.Name} with id {entity.Uid} does not have a {nameof(IDamageableComponent)}."); + return; + } + + damageFunc(damageable, ignoreResistances); + } + } +} diff --git a/Content.Server/GameObjects/Components/Body/BodyCommands.cs b/Content.Server/GameObjects/Components/Body/BodyCommands.cs index d0129b7f1a..66366fffb9 100644 --- a/Content.Server/GameObjects/Components/Body/BodyCommands.cs +++ b/Content.Server/GameObjects/Components/Body/BodyCommands.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Server.Administration; using Content.Shared.Administration; @@ -246,90 +247,4 @@ namespace Content.Server.GameObjects.Components.Body shell.SendText(player, $"No mechanism was found with name {mechanismName}."); } } - - [AdminCommand(AdminFlags.Fun)] - class HurtCommand : IClientCommand - { - public string Command => "hurt"; - public string Description => "Ouch"; - public string Help => $"Usage: {Command} () ()"; - - private void SendDamageTypes(IConsoleShell shell, IPlayerSession? player) - { - var msg = ""; - foreach (var dClass in Enum.GetNames(typeof(DamageClass))) - { - msg += $"\n{dClass}"; - var types = Enum.Parse(dClass).ToTypes(); - foreach (var dType in types) - { - msg += $"\n - {dType}"; - } - } - shell.SendText(player, $"Damage Types:{msg}"); - } - - public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args) - { - // Check if we have enough for the dmg types to show - if (args.Length > 0 && args[0] == "?") - { - SendDamageTypes(shell, player); - return; - } - - // Not enough args - if (args.Length < 2) - { - shell.SendText(player, Help); - return; - } - - var ignoreResistance = false; - var entityUid = player != null && player.AttachedEntityUid.HasValue ? player.AttachedEntityUid.Value : EntityUid.Invalid; - if (!int.TryParse(args[1], out var amount) || - args.Length >= 3 && args[2] != "_" && !EntityUid.TryParse(args[2], out entityUid) || - args.Length >= 4 && !bool.TryParse(args[3], out ignoreResistance)) - { - shell.SendText(player, Help); - return; - } - - if (entityUid == EntityUid.Invalid) - { - shell.SendText(player, "Not a valid entity."); - return; - } - - if (!IoCManager.Resolve().TryGetEntity(entityUid, out var ent)) - { - shell.SendText(player, "Entity couldn't be found."); - return; - } - - if (!ent.TryGetComponent(out IDamageableComponent? damageable)) - { - shell.SendText(player, "Entity can't be damaged."); - return; - } - - if (Enum.TryParse(args[0], true, out var dmgClass)) - { - if (!damageable.ChangeDamage(dmgClass, amount, ignoreResistance)) - shell.SendText(player, "Something went wrong!"); - return; - } - // Fall back to DamageType - else if (Enum.TryParse(args[0], true, out var dmgType)) - { - if (!damageable.ChangeDamage(dmgType, amount, ignoreResistance)) - shell.SendText(player, "Something went wrong!"); - return; - } - else - { - SendDamageTypes(shell, player); - } - } - } }