diff --git a/Content.Server/Administration/Commands/SetOutfitCommand.cs b/Content.Server/Administration/Commands/SetOutfitCommand.cs index 9240e7b91b..d4b1da7b17 100644 --- a/Content.Server/Administration/Commands/SetOutfitCommand.cs +++ b/Content.Server/Administration/Commands/SetOutfitCommand.cs @@ -1,36 +1,22 @@ using Content.Server.Administration.UI; +using Content.Server.Clothing.Systems; using Content.Server.EUI; -using Content.Server.Hands.Systems; -using Content.Server.Preferences.Managers; -using Content.Shared.Access.Components; using Content.Shared.Administration; -using Content.Shared.Clothing; -using Content.Shared.Hands.Components; -using Content.Shared.Humanoid; using Content.Shared.Inventory; -using Content.Shared.PDA; -using Content.Shared.Preferences; -using Content.Shared.Preferences.Loadouts; -using Content.Shared.Roles; -using Content.Shared.Station; using Robust.Shared.Console; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; namespace Content.Server.Administration.Commands { [AdminCommand(AdminFlags.Admin)] - public sealed class SetOutfitCommand : IConsoleCommand + public sealed class SetOutfitCommand : LocalizedEntityCommands { - [Dependency] private readonly IEntityManager _entities = default!; + [Dependency] private readonly EuiManager _euiManager = default!; + [Dependency] private readonly OutfitSystem _outfitSystem = default!; - public string Command => "setoutfit"; + public override string Command => "setoutfit"; + public override string Description => Loc.GetString("cmd-setoutfit-desc", ("requiredComponent", nameof(InventoryComponent))); - public string Description => Loc.GetString("set-outfit-command-description", ("requiredComponent", nameof(InventoryComponent))); - - public string Help => Loc.GetString("set-outfit-command-help-text", ("command", Command)); - - public void Execute(IConsoleShell shell, string argStr, string[] args) + public override void Execute(IConsoleShell shell, string argStr, string[] args) { if (args.Length < 1) { @@ -46,13 +32,13 @@ namespace Content.Server.Administration.Commands var nent = new NetEntity(entInt); - if (!_entities.TryGetEntity(nent, out var target)) + if (!EntityManager.TryGetEntity(nent, out var target)) { shell.WriteLine(Loc.GetString("shell-invalid-entity-id")); return; } - if (!_entities.HasComponent(target)) + if (!EntityManager.HasComponent(target)) { shell.WriteLine(Loc.GetString("shell-target-entity-does-not-have-message", ("missing", "inventory"))); return; @@ -62,109 +48,17 @@ namespace Content.Server.Administration.Commands { if (shell.Player is not { } player) { - shell.WriteError(Loc.GetString("set-outfit-command-is-not-player-error")); + shell.WriteError(Loc.GetString("cmd-setoutfit-is-not-player-error")); return; } - var eui = IoCManager.Resolve(); var ui = new SetOutfitEui(nent); - eui.OpenEui(ui, player); + _euiManager.OpenEui(ui, player); return; } - if (!SetOutfit(target.Value, args[1], _entities)) - shell.WriteLine(Loc.GetString("set-outfit-command-invalid-outfit-id-error")); - } - - public static bool SetOutfit(EntityUid target, string gear, IEntityManager entityManager, Action? onEquipped = null) - { - if (!entityManager.TryGetComponent(target, out InventoryComponent? inventoryComponent)) - return false; - - var prototypeManager = IoCManager.Resolve(); - if (!prototypeManager.TryIndex(gear, out var startingGear)) - return false; - - HumanoidCharacterProfile? profile = null; - ICommonSession? session = null; - // Check if we are setting the outfit of a player to respect the preferences - if (entityManager.TryGetComponent(target, out ActorComponent? actorComponent)) - { - session = actorComponent.PlayerSession; - var userId = actorComponent.PlayerSession.UserId; - var preferencesManager = IoCManager.Resolve(); - var prefs = preferencesManager.GetPreferences(userId); - profile = prefs.SelectedCharacter as HumanoidCharacterProfile; - } - - var invSystem = entityManager.System(); - if (invSystem.TryGetSlots(target, out var slots)) - { - foreach (var slot in slots) - { - invSystem.TryUnequip(target, slot.Name, true, true, false, inventoryComponent); - var gearStr = ((IEquipmentLoadout) startingGear).GetGear(slot.Name); - if (gearStr == string.Empty) - { - continue; - } - - var equipmentEntity = entityManager.SpawnEntity(gearStr, entityManager.GetComponent(target).Coordinates); - if (slot.Name == "id" && - entityManager.TryGetComponent(equipmentEntity, out PdaComponent? pdaComponent) && - entityManager.TryGetComponent(pdaComponent.ContainedId, out var id)) - { - id.FullName = entityManager.GetComponent(target).EntityName; - } - - invSystem.TryEquip(target, equipmentEntity, slot.Name, silent: true, force: true, inventory: inventoryComponent); - - onEquipped?.Invoke(target, equipmentEntity); - } - } - - if (entityManager.TryGetComponent(target, out HandsComponent? handsComponent)) - { - var handsSystem = entityManager.System(); - var coords = entityManager.GetComponent(target).Coordinates; - foreach (var prototype in startingGear.Inhand) - { - var inhandEntity = entityManager.SpawnEntity(prototype, coords); - handsSystem.TryPickup(target, inhandEntity, checkActionBlocker: false, handsComp: handsComponent); - } - } - - // See if this starting gear is associated with a job - var jobs = prototypeManager.EnumeratePrototypes(); - foreach (var job in jobs) - { - if (job.StartingGear != gear) - continue; - - var jobProtoId = LoadoutSystem.GetJobPrototype(job.ID); - if (!prototypeManager.TryIndex(jobProtoId, out var jobProto)) - break; - - // Don't require a player, so this works on Urists - profile ??= entityManager.TryGetComponent(target, out var comp) - ? HumanoidCharacterProfile.DefaultWithSpecies(comp.Species) - : new HumanoidCharacterProfile(); - // Try to get the user's existing loadout for the role - profile.Loadouts.TryGetValue(jobProtoId, out var roleLoadout); - - if (roleLoadout == null) - { - // If they don't have a loadout for the role, make a default one - roleLoadout = new RoleLoadout(jobProtoId); - roleLoadout.SetDefault(profile, session, prototypeManager); - } - - // Equip the target with the job loadout - var stationSpawning = entityManager.System(); - stationSpawning.EquipRoleLoadout(target, roleLoadout, jobProto); - } - - return true; + if (!_outfitSystem.SetOutfit(target.Value, args[1])) + shell.WriteLine(Loc.GetString("cmd-setoutfit-invalid-outfit-id-error")); } } } diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs index 79c5c86fe1..6c7727ab4e 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs @@ -1,9 +1,9 @@ -using Content.Server.Administration.Commands; using Content.Server.Antag; using Content.Server.GameTicking; using Content.Server.GameTicking.Rules.Components; using Content.Server.Zombies; using Content.Shared.Administration; +using Content.Server.Clothing.Systems; using Content.Shared.Database; using Content.Shared.Humanoid; using Content.Shared.Mind.Components; @@ -20,6 +20,7 @@ public sealed partial class AdminVerbSystem [Dependency] private readonly AntagSelectionSystem _antag = default!; [Dependency] private readonly ZombieSystem _zombie = default!; [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly OutfitSystem _outfit = default!; [ValidatePrototypeId] private const string DefaultTraitorRule = "Traitor"; @@ -126,7 +127,7 @@ public sealed partial class AdminVerbSystem Act = () => { // pirates just get an outfit because they don't really have logic associated with them - SetOutfitCommand.SetOutfit(args.Target, PirateGearId, EntityManager); + _outfit.SetOutfit(args.Target, PirateGearId); }, Impact = LogImpact.High, Message = string.Join(": ", pirateName, Loc.GetString("admin-verb-make-pirate")), diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index 1f04df23f2..3c6782d08e 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -1,10 +1,10 @@ using System.Threading; -using Content.Server.Administration.Commands; using Content.Server.Administration.Components; using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Components; using Content.Server.Body.Systems; +using Content.Server.Clothing.Systems; using Content.Server.Electrocution; using Content.Server.Explosion.EntitySystems; using Content.Server.GhostKick; @@ -42,7 +42,6 @@ using Content.Shared.Slippery; using Content.Shared.Tabletop.Components; using Content.Shared.Tools.Systems; using Content.Shared.Verbs; -using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; @@ -80,6 +79,7 @@ public sealed partial class AdminVerbSystem [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SuperBonkSystem _superBonkSystem = default!; [Dependency] private readonly SlipperySystem _slipperySystem = default!; + [Dependency] private readonly OutfitSystem _outfitSystem = default!; // All smite verbs have names so invokeverb works. private void AddSmiteVerbs(GetVerbsEvent args) @@ -587,7 +587,7 @@ public sealed partial class AdminVerbSystem Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Uniforms/Jumpskirt/janimaid.rsi"), "icon"), Act = () => { - SetOutfitCommand.SetOutfit(args.Target, "JanitorMaidGear", EntityManager, (_, clothing) => + _outfitSystem.SetOutfit(args.Target, "JanitorMaidGear", (_, clothing) => { if (HasComp(clothing)) EnsureComp(clothing); diff --git a/Content.Server/Clothing/Systems/OutfitSystem.cs b/Content.Server/Clothing/Systems/OutfitSystem.cs new file mode 100644 index 0000000000..c3fea28bcf --- /dev/null +++ b/Content.Server/Clothing/Systems/OutfitSystem.cs @@ -0,0 +1,109 @@ +using Content.Server.Hands.Systems; +using Content.Server.Preferences.Managers; +using Content.Shared.Access.Components; +using Content.Shared.Clothing; +using Content.Shared.Hands.Components; +using Content.Shared.Humanoid; +using Content.Shared.Inventory; +using Content.Shared.PDA; +using Content.Shared.Preferences; +using Content.Shared.Preferences.Loadouts; +using Content.Shared.Roles; +using Content.Shared.Station; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server.Clothing.Systems; + +public sealed class OutfitSystem : EntitySystem +{ + [Dependency] private readonly IServerPreferencesManager _preferenceManager = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly HandsSystem _handSystem = default!; + [Dependency] private readonly InventorySystem _invSystem = default!; + [Dependency] private readonly SharedStationSpawningSystem _spawningSystem = default!; + + public bool SetOutfit(EntityUid target, string gear, Action? onEquipped = null) + { + if (!EntityManager.TryGetComponent(target, out InventoryComponent? inventoryComponent)) + return false; + + if (!_prototypeManager.TryIndex(gear, out var startingGear)) + return false; + + HumanoidCharacterProfile? profile = null; + ICommonSession? session = null; + // Check if we are setting the outfit of a player to respect the preferences + if (EntityManager.TryGetComponent(target, out ActorComponent? actorComponent)) + { + session = actorComponent.PlayerSession; + var userId = actorComponent.PlayerSession.UserId; + var prefs = _preferenceManager.GetPreferences(userId); + profile = prefs.SelectedCharacter as HumanoidCharacterProfile; + } + + if (_invSystem.TryGetSlots(target, out var slots)) + { + foreach (var slot in slots) + { + _invSystem.TryUnequip(target, slot.Name, true, true, false, inventoryComponent); + var gearStr = ((IEquipmentLoadout) startingGear).GetGear(slot.Name); + if (gearStr == string.Empty) + continue; + + var equipmentEntity = EntityManager.SpawnEntity(gearStr, EntityManager.GetComponent(target).Coordinates); + if (slot.Name == "id" && + EntityManager.TryGetComponent(equipmentEntity, out PdaComponent? pdaComponent) && + EntityManager.TryGetComponent(pdaComponent.ContainedId, out var id)) + { + id.FullName = EntityManager.GetComponent(target).EntityName; + } + + _invSystem.TryEquip(target, equipmentEntity, slot.Name, silent: true, force: true, inventory: inventoryComponent); + + onEquipped?.Invoke(target, equipmentEntity); + } + } + + if (EntityManager.TryGetComponent(target, out HandsComponent? handsComponent)) + { + var coords = EntityManager.GetComponent(target).Coordinates; + foreach (var prototype in startingGear.Inhand) + { + var inhandEntity = EntityManager.SpawnEntity(prototype, coords); + _handSystem.TryPickup(target, inhandEntity, checkActionBlocker: false, handsComp: handsComponent); + } + } + + // See if this starting gear is associated with a job + var jobs = _prototypeManager.EnumeratePrototypes(); + foreach (var job in jobs) + { + if (job.StartingGear != gear) + continue; + + var jobProtoId = LoadoutSystem.GetJobPrototype(job.ID); + if (!_prototypeManager.TryIndex(jobProtoId, out var jobProto)) + break; + + // Don't require a player, so this works on Urists + profile ??= EntityManager.TryGetComponent(target, out var comp) + ? HumanoidCharacterProfile.DefaultWithSpecies(comp.Species) + : new HumanoidCharacterProfile(); + // Try to get the user's existing loadout for the role + profile.Loadouts.TryGetValue(jobProtoId, out var roleLoadout); + + if (roleLoadout == null) + { + // If they don't have a loadout for the role, make a default one + roleLoadout = new RoleLoadout(jobProtoId); + roleLoadout.SetDefault(profile, session, _prototypeManager); + } + + // Equip the target with the job loadout + _spawningSystem.EquipRoleLoadout(target, roleLoadout, jobProto); + } + + return true; + } +} diff --git a/Content.Server/Cluwne/CluwneSystem.cs b/Content.Server/Cluwne/CluwneSystem.cs index 1fbb0c7269..73476ece17 100644 --- a/Content.Server/Cluwne/CluwneSystem.cs +++ b/Content.Server/Cluwne/CluwneSystem.cs @@ -1,9 +1,9 @@ -using Content.Server.Administration.Commands; using Content.Server.Popups; using Content.Shared.Popups; using Content.Shared.Mobs; using Content.Server.Chat; using Content.Server.Chat.Systems; +using Content.Server.Clothing.Systems; using Content.Shared.Chat.Prototypes; using Robust.Shared.Random; using Content.Shared.Stunnable; @@ -13,7 +13,6 @@ using Robust.Shared.Prototypes; using Content.Server.Emoting.Systems; using Content.Server.Speech.EntitySystems; using Content.Shared.Cluwne; -using Content.Shared.Interaction.Components; using Robust.Shared.Audio.Systems; using Content.Shared.NameModifier.EntitySystems; using Content.Shared.Clumsy; @@ -31,6 +30,7 @@ public sealed class CluwneSystem : EntitySystem [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly AutoEmoteSystem _autoEmote = default!; [Dependency] private readonly NameModifierSystem _nameMod = default!; + [Dependency] private readonly OutfitSystem _outfitSystem = default!; public override void Initialize() { @@ -78,7 +78,7 @@ public sealed class CluwneSystem : EntitySystem _nameMod.RefreshNameModifiers(uid); - SetOutfitCommand.SetOutfit(uid, "CluwneGear", EntityManager); + _outfitSystem.SetOutfit(uid, "CluwneGear"); } /// diff --git a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs index 2e97a72f3b..9ae8c5ff83 100644 --- a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs @@ -1,5 +1,5 @@ using System.Linq; -using Content.Server.Administration.Commands; +using Content.Server.Clothing.Systems; using Content.Server.GameTicking.Rules.Components; using Content.Server.KillTracking; using Content.Server.Mind; @@ -23,6 +23,7 @@ public sealed class DeathMatchRuleSystem : GameRuleSystem(mob); _respawn.AddToTracker(ev.Player.UserId, (uid, tracker)); diff --git a/Resources/Locale/en-US/administration/commands/set-outfit-command.ftl b/Resources/Locale/en-US/administration/commands/set-outfit-command.ftl index 793c339c28..247dae37f4 100644 --- a/Resources/Locale/en-US/administration/commands/set-outfit-command.ftl +++ b/Resources/Locale/en-US/administration/commands/set-outfit-command.ftl @@ -1,4 +1,4 @@ -set-outfit-command-description = Sets the outfit of the specified entity. The entity must have {INDEFINITE($requiredComponent)} {$requiredComponent} -set-outfit-command-help-text = Usage: {$command} | {$command} -set-outfit-command-is-not-player-error = This command requires both arguments to work from the server console. -set-outfit-command-invalid-outfit-id-error = Invalid outfit id +cmd-setoutfit-desc = Sets the outfit of the specified entity. The entity must have {INDEFINITE($requiredComponent)} {$requiredComponent} +cmd-setoutfit-help = Usage: setoutfit | setoutfit +cmd-setoutfit-is-not-player-error = This command requires both arguments to work from the server console. +cmd-setoutfit-invalid-outfit-id-error = Invalid outfit id.