diff --git a/Content.Client/Clothing/ClothingSystem.cs b/Content.Client/Clothing/ClothingSystem.cs index eb10082602..972a8edce7 100644 --- a/Content.Client/Clothing/ClothingSystem.cs +++ b/Content.Client/Clothing/ClothingSystem.cs @@ -10,9 +10,7 @@ using Content.Shared.Item; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.ResourceManagement; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Log; +using static Robust.Client.GameObjects.SpriteComponent; using static Robust.Shared.GameObjects.SharedSpriteComponent; namespace Content.Client.Clothing; @@ -204,6 +202,9 @@ public sealed class ClothingSystem : EntitySystem }); } + if (!_inventorySystem.TryGetSlot(equipee, slot, out var slotDef, inventory)) + return; + // Remove old layers. We could also just set them to invisible, but as items may add arbitrary layers, this // may eventually bloat the player with lots of invisible layers. if (inventory.VisualLayerKeys.TryGetValue(slot, out var revealedLayers)) @@ -239,17 +240,20 @@ public sealed class ClothingSystem : EntitySystem } var index = sprite.LayerMapReserveBlank(key); + if (sprite[index] is not Layer layer) + return; // In case no RSI is given, use the item's base RSI as a default. This cuts down on a lot of unnecessary yaml entries. if (layerData.RsiPath == null && layerData.TexturePath == null - && sprite[index].Rsi == null + && layer.RSI == null && TryComp(equipment, out SpriteComponent? clothingSprite)) { - sprite.LayerSetRSI(index, clothingSprite.BaseRSI); + layer.SetRsi(clothingSprite.BaseRSI); } sprite.LayerSetData(index, layerData); + layer.Offset += slotDef.Offset; } RaiseLocalEvent(equipment, new EquipmentVisualsUpdatedEvent(equipee, slot, revealedLayers)); diff --git a/Content.Client/Hands/HandsComponent.cs b/Content.Client/Hands/HandsComponent.cs index 8b8d7b54d2..36a15f1440 100644 --- a/Content.Client/Hands/HandsComponent.cs +++ b/Content.Client/Hands/HandsComponent.cs @@ -1,7 +1,4 @@ using Content.Shared.Hands.Components; -using Robust.Shared.Analyzers; -using Robust.Shared.GameObjects; -using System.Collections.Generic; namespace Content.Client.Hands { @@ -10,6 +7,12 @@ namespace Content.Client.Hands [Friend(typeof(HandsSystem))] public sealed class HandsComponent : SharedHandsComponent { + /// + /// Whether or not to add in-hand sprites for held items. Some entities (e.g., drones) don't want these. + /// + [DataField("showInHands")] + public bool ShowInHands = true; + public HandsGui? Gui { get; set; } /// diff --git a/Content.Client/Hands/Systems/HandsSystem.cs b/Content.Client/Hands/Systems/HandsSystem.cs index 7f3e236629..97d7f77adf 100644 --- a/Content.Client/Hands/Systems/HandsSystem.cs +++ b/Content.Client/Hands/Systems/HandsSystem.cs @@ -203,6 +203,9 @@ namespace Content.Client.Hands if (uid == _playerManager.LocalPlayer?.ControlledEntity) UpdateGui(); + if (!handComp.ShowInHands) + return; + // Remove old layers. We could also just set them to invisible, but as items may add arbitrary layers, this // may eventually bloat the player with lots of layers. if (handComp.RevealedLayers.TryGetValue(hand.Location, out var revealedLayers)) diff --git a/Content.Shared/Clothing/ClothingEvents.cs b/Content.Shared/Clothing/ClothingEvents.cs index 3b38719b18..84603eb385 100644 --- a/Content.Shared/Clothing/ClothingEvents.cs +++ b/Content.Shared/Clothing/ClothingEvents.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using Robust.Shared.GameObjects; using static Robust.Shared.GameObjects.SharedSpriteComponent; namespace Content.Shared.Clothing; @@ -7,7 +5,7 @@ namespace Content.Shared.Clothing; /// /// Raised directed at a piece of clothing to get the set of layers to show on the wearer's sprite /// -public class GetEquipmentVisualsEvent : EntityEventArgs +public sealed class GetEquipmentVisualsEvent : EntityEventArgs { /// /// Entity that is wearing the item. @@ -37,7 +35,7 @@ public class GetEquipmentVisualsEvent : EntityEventArgs /// /// Useful for systems/components that modify the visual layers that an item adds to a player. (e.g. RGB memes) /// -public class EquipmentVisualsUpdatedEvent : EntityEventArgs +public sealed class EquipmentVisualsUpdatedEvent : EntityEventArgs { /// /// Entity that is wearing the item. diff --git a/Content.Shared/Inventory/InventoryTemplatePrototype.cs b/Content.Shared/Inventory/InventoryTemplatePrototype.cs index 66478ba7a6..128430593f 100644 --- a/Content.Shared/Inventory/InventoryTemplatePrototype.cs +++ b/Content.Shared/Inventory/InventoryTemplatePrototype.cs @@ -34,6 +34,11 @@ public sealed class SlotDefinition [DataField("dependsOn")] public string? DependsOn { get; } [DataField("displayName", required: true)] public string DisplayName { get; } = string.Empty; + + /// + /// Offset for the clothing sprites. + /// + [DataField("offset")] public Vector2 Offset { get; } = Vector2.Zero; } public enum SlotUIContainer diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index d751e7d4ca..6106736f7e 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -575,7 +575,7 @@ - type: SentienceTarget flavorKind: primate - type: Inventory - templateId: head + templateId: monkey - type: Strippable - type: UserInterface interfaces: diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index d9433a6940..cdad71a0eb 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -52,6 +52,7 @@ - type: Physics bodyType: KinematicController - type: Hands + showInHands: false - type: Body template: DroneTemplate preset: DronePreset @@ -77,7 +78,7 @@ - type: NameIdentifier group: Drone - type: Inventory - templateId: head + templateId: drone - type: Strippable - type: UserInterface interfaces: @@ -105,8 +106,6 @@ layers: - state: shell sprite: Mobs/Silicon/drone.rsi - - map: [ "head" ] - offset: 0, -0.45 - type: MovementIgnoreGravity - type: Fixtures fixtures: diff --git a/Resources/Prototypes/InventoryTemplates/head_inventory_template.yml b/Resources/Prototypes/InventoryTemplates/drone_inventory_template.yml similarity index 83% rename from Resources/Prototypes/InventoryTemplates/head_inventory_template.yml rename to Resources/Prototypes/InventoryTemplates/drone_inventory_template.yml index c4328ee2dd..aa331d2d54 100644 --- a/Resources/Prototypes/InventoryTemplates/head_inventory_template.yml +++ b/Resources/Prototypes/InventoryTemplates/drone_inventory_template.yml @@ -1,5 +1,5 @@ - type: inventoryTemplate - id: head + id: drone slots: - name: head slotTexture: head @@ -7,3 +7,4 @@ uiContainer: BottomLeft uiWindowPos: 0,0 displayName: Head + offset: 0, -0.45 diff --git a/Resources/Prototypes/InventoryTemplates/monkey_inventory_template.yml b/Resources/Prototypes/InventoryTemplates/monkey_inventory_template.yml new file mode 100644 index 0000000000..9bd19c9952 --- /dev/null +++ b/Resources/Prototypes/InventoryTemplates/monkey_inventory_template.yml @@ -0,0 +1,9 @@ +- type: inventoryTemplate + id: monkey + slots: + - name: head + slotTexture: head + slotFlags: HEAD + uiContainer: BottomLeft + uiWindowPos: 0,0 + displayName: Head \ No newline at end of file