Revert "Add support for multi-layer in-hand and clothing sprites" (#6521)
This commit is contained in:
@@ -1,6 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
|
||||||
using Content.Client.Inventory;
|
using Content.Client.Inventory;
|
||||||
using Content.Shared.CharacterAppearance;
|
using Content.Shared.CharacterAppearance;
|
||||||
using Content.Shared.Clothing;
|
using Content.Shared.Clothing;
|
||||||
@@ -11,9 +9,8 @@ using Robust.Client.GameObjects;
|
|||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.ResourceManagement;
|
using Robust.Client.ResourceManagement;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Log;
|
|
||||||
using static Robust.Shared.GameObjects.SharedSpriteComponent;
|
|
||||||
|
|
||||||
namespace Content.Client.Clothing;
|
namespace Content.Client.Clothing;
|
||||||
|
|
||||||
@@ -51,94 +48,11 @@ public class ClothingSystem : EntitySystem
|
|||||||
|
|
||||||
SubscribeLocalEvent<ClothingComponent, GotEquippedEvent>(OnGotEquipped);
|
SubscribeLocalEvent<ClothingComponent, GotEquippedEvent>(OnGotEquipped);
|
||||||
SubscribeLocalEvent<ClothingComponent, GotUnequippedEvent>(OnGotUnequipped);
|
SubscribeLocalEvent<ClothingComponent, GotUnequippedEvent>(OnGotUnequipped);
|
||||||
|
SubscribeLocalEvent<ClientInventoryComponent, ItemPrefixChangeEvent>(OnPrefixChanged);
|
||||||
SubscribeLocalEvent<SharedItemComponent, GetEquipmentVisualsEvent>(OnGetVisuals);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<ClientInventoryComponent, VisualsChangedEvent>(OnVisualsChanged);
|
|
||||||
SubscribeLocalEvent<SpriteComponent, DidUnequipEvent>(OnDidUnequip);
|
SubscribeLocalEvent<SpriteComponent, DidUnequipEvent>(OnDidUnequip);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGetVisuals(EntityUid uid, SharedItemComponent item, GetEquipmentVisualsEvent args)
|
private void OnPrefixChanged(EntityUid uid, ClientInventoryComponent component, ItemPrefixChangeEvent args)
|
||||||
{
|
|
||||||
if (!TryComp(args.Equipee, out ClientInventoryComponent? inventory))
|
|
||||||
return;
|
|
||||||
|
|
||||||
List<PrototypeLayerData>? layers = null;
|
|
||||||
|
|
||||||
// first attempt to get species specific data.
|
|
||||||
if (inventory.SpeciesId != null)
|
|
||||||
item.ClothingVisuals.TryGetValue($"{args.Slot}-{inventory.SpeciesId}", out layers);
|
|
||||||
|
|
||||||
// if that returned nothing, attempt to find generic data
|
|
||||||
if (layers == null && !item.ClothingVisuals.TryGetValue(args.Slot, out layers))
|
|
||||||
{
|
|
||||||
// No generic data either. Attempt to generate defaults from the item's RSI & item-prefixes
|
|
||||||
if (!TryGetDefaultVisuals(uid, item, args.Slot, inventory.SpeciesId, out layers))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add each layer to the visuals
|
|
||||||
var i = 0;
|
|
||||||
foreach (var layer in layers)
|
|
||||||
{
|
|
||||||
var key = layer.MapKeys?.FirstOrDefault();
|
|
||||||
if (key == null)
|
|
||||||
{
|
|
||||||
key = i == 0 ? args.Slot : $"{args.Slot}-{i}";
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.Layers.Add((key, layer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If no explicit clothing visuals were specified, this attempts to populate with default values.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Useful for lazily adding clothing sprites without modifying yaml. And for backwards compatibility.
|
|
||||||
/// </remarks>
|
|
||||||
private bool TryGetDefaultVisuals(EntityUid uid, SharedItemComponent item, string slot, string? speciesId,
|
|
||||||
[NotNullWhen(true)] out List<PrototypeLayerData>? layers)
|
|
||||||
{
|
|
||||||
layers = null;
|
|
||||||
|
|
||||||
RSI? rsi = null;
|
|
||||||
|
|
||||||
if (item.RsiPath != null)
|
|
||||||
rsi = _cache.GetResource<RSIResource>(TextureRoot / item.RsiPath).RSI;
|
|
||||||
else if (TryComp(uid, out SpriteComponent? sprite))
|
|
||||||
rsi = sprite.BaseRSI;
|
|
||||||
|
|
||||||
if (rsi == null || rsi.Path == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var correctedSlot = slot;
|
|
||||||
TemporarySlotMap.TryGetValue(correctedSlot, out correctedSlot);
|
|
||||||
|
|
||||||
var state = (item.EquippedPrefix == null)
|
|
||||||
? $"equipped-{correctedSlot}"
|
|
||||||
: $"{item.EquippedPrefix}-equipped-{correctedSlot}";
|
|
||||||
|
|
||||||
// species specific
|
|
||||||
if (speciesId != null && rsi.TryGetState($"{state}-{speciesId}", out _))
|
|
||||||
{
|
|
||||||
state = $"{state}-{speciesId}";
|
|
||||||
}
|
|
||||||
else if (!rsi.TryGetState(state, out _))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var layer = PrototypeLayerData.New();
|
|
||||||
layer.RsiPath = rsi.Path.ToString();
|
|
||||||
layer.State = state;
|
|
||||||
layers = new() { layer };
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnVisualsChanged(EntityUid uid, ClientInventoryComponent component, VisualsChangedEvent args)
|
|
||||||
{
|
{
|
||||||
if (!TryComp(args.Item, out ClothingComponent? clothing) || clothing.InSlot == null)
|
if (!TryComp(args.Item, out ClothingComponent? clothing) || clothing.InSlot == null)
|
||||||
return;
|
return;
|
||||||
@@ -153,19 +67,7 @@ public class ClothingSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnDidUnequip(EntityUid uid, SpriteComponent component, DidUnequipEvent args)
|
private void OnDidUnequip(EntityUid uid, SpriteComponent component, DidUnequipEvent args)
|
||||||
{
|
{
|
||||||
if (!TryComp(uid, out ClientInventoryComponent? inventory) || !TryComp(uid, out SpriteComponent? sprite))
|
component.LayerSetVisible(args.Slot, false);
|
||||||
return;
|
|
||||||
|
|
||||||
if (!inventory.VisualLayerKeys.TryGetValue(args.Slot, out var revealedLayers))
|
|
||||||
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.
|
|
||||||
foreach (var layer in revealedLayers)
|
|
||||||
{
|
|
||||||
sprite.RemoveLayer(layer);
|
|
||||||
}
|
|
||||||
revealedLayers.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InitClothing(EntityUid uid, ClientInventoryComponent? component = null, SpriteComponent? sprite = null)
|
public void InitClothing(EntityUid uid, ClientInventoryComponent? component = null, SpriteComponent? sprite = null)
|
||||||
@@ -174,6 +76,8 @@ public class ClothingSystem : EntitySystem
|
|||||||
|
|
||||||
foreach (var slot in slots)
|
foreach (var slot in slots)
|
||||||
{
|
{
|
||||||
|
sprite.LayerMapReserveBlank(slot.Name);
|
||||||
|
|
||||||
if (!_inventorySystem.TryGetSlotContainer(uid, slot.Name, out var containerSlot, out _, component) ||
|
if (!_inventorySystem.TryGetSlotContainer(uid, slot.Name, out var containerSlot, out _, component) ||
|
||||||
!containerSlot.ContainedEntity.HasValue) continue;
|
!containerSlot.ContainedEntity.HasValue) continue;
|
||||||
|
|
||||||
@@ -185,14 +89,54 @@ public class ClothingSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
component.InSlot = args.Slot;
|
component.InSlot = args.Slot;
|
||||||
|
|
||||||
RenderEquipment(args.Equipee, uid, args.Slot, clothingComponent: component);
|
if (!TryComp<SpriteComponent>(args.Equipee, out var sprite) || !TryComp<ClientInventoryComponent>(args.Equipee, out var invComp))
|
||||||
|
{
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RenderEquipment(EntityUid equipee, EntityUid equipment, string slot,
|
var data = GetEquippedStateInfo(args.Equipment, args.Slot, invComp.SpeciesId, component);
|
||||||
ClientInventoryComponent? inventory = null, SpriteComponent? sprite = null, ClothingComponent? clothingComponent = null)
|
if (data != null)
|
||||||
{
|
{
|
||||||
if(!Resolve(equipee, ref inventory, ref sprite) || !Resolve(equipment, ref clothingComponent, false))
|
var (rsi, state) = data.Value;
|
||||||
|
sprite.LayerSetVisible(args.Slot, true);
|
||||||
|
sprite.LayerSetState(args.Slot, state, rsi);
|
||||||
|
sprite.LayerSetAutoAnimated(args.Slot, true);
|
||||||
|
|
||||||
|
if (args.Slot == "jumpsuit" && sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out _))
|
||||||
|
{
|
||||||
|
sprite.LayerSetState(HumanoidVisualLayers.StencilMask, component.FemaleMask switch
|
||||||
|
{
|
||||||
|
FemaleClothingMask.NoMask => "female_none",
|
||||||
|
FemaleClothingMask.UniformTop => "female_top",
|
||||||
|
_ => "female_full",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sprite.LayerSetVisible(args.Slot, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RenderEquipment(EntityUid uid, EntityUid equipment, string slot,
|
||||||
|
ClientInventoryComponent? inventoryComponent = null, SpriteComponent? sprite = null, ClothingComponent? clothingComponent = null)
|
||||||
|
{
|
||||||
|
if(!Resolve(uid, ref inventoryComponent, ref sprite))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!Resolve(equipment, ref clothingComponent, false))
|
||||||
|
{
|
||||||
|
sprite.LayerSetVisible(slot, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = GetEquippedStateInfo(equipment, slot, inventoryComponent.SpeciesId, clothingComponent);
|
||||||
|
if (data == null) return;
|
||||||
|
var (rsi, state) = data.Value;
|
||||||
|
sprite.LayerSetVisible(slot, true);
|
||||||
|
sprite.LayerSetState(slot, state, rsi);
|
||||||
|
sprite.LayerSetAutoAnimated(slot, true);
|
||||||
|
|
||||||
if (slot == "jumpsuit" && sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out _))
|
if (slot == "jumpsuit" && sprite.LayerMapTryGet(HumanoidVisualLayers.StencilMask, out _))
|
||||||
{
|
{
|
||||||
@@ -203,55 +147,34 @@ public class ClothingSystem : EntitySystem
|
|||||||
_ => "female_full",
|
_ => "female_full",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Remove old layers. We could also just set them to invisible, but as items may add arbitrary layers, this
|
public (RSI rsi, RSI.StateId stateId)? GetEquippedStateInfo(EntityUid uid, string slot, string? speciesId=null, ClothingComponent? component = null)
|
||||||
// may eventually bloat the player with lots of invisible layers.
|
|
||||||
if (inventory.VisualLayerKeys.TryGetValue(slot, out var revealedLayers))
|
|
||||||
{
|
{
|
||||||
foreach (var key in revealedLayers)
|
if (!Resolve(uid, ref component))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (component.RsiPath == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var rsi = _cache.GetResource<RSIResource>(SharedSpriteComponent.TextureRoot / component.RsiPath).RSI;
|
||||||
|
var correctedSlot = slot;
|
||||||
|
TemporarySlotMap.TryGetValue(correctedSlot, out correctedSlot);
|
||||||
|
var stateId = component.EquippedPrefix != null ? $"{component.EquippedPrefix}-equipped-{correctedSlot}" : $"equipped-{correctedSlot}";
|
||||||
|
if (speciesId != null)
|
||||||
{
|
{
|
||||||
sprite.RemoveLayer(key);
|
var speciesState = $"{stateId}-{speciesId}";
|
||||||
}
|
if (rsi.TryGetState(speciesState, out _))
|
||||||
revealedLayers.Clear();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
revealedLayers = new();
|
return (rsi, speciesState);
|
||||||
inventory.VisualLayerKeys[slot] = revealedLayers;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var ev = new GetEquipmentVisualsEvent(equipee, slot);
|
if (rsi.TryGetState(stateId, out _))
|
||||||
RaiseLocalEvent(equipment, ev, false);
|
|
||||||
|
|
||||||
if (ev.Layers.Count == 0)
|
|
||||||
{
|
{
|
||||||
RaiseLocalEvent(equipment, new EquipmentVisualsUpdatedEvent(equipee, slot, revealedLayers));
|
return (rsi, stateId);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add the new layers
|
return null;
|
||||||
foreach (var (key, layerData) in ev.Layers)
|
|
||||||
{
|
|
||||||
if (!revealedLayers.Add(key))
|
|
||||||
{
|
|
||||||
Logger.Warning($"Duplicate key for clothing visuals: {key}. Are multiple components attempting to modify the same layer? Equipment: {ToPrettyString(equipment)}");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = sprite.LayerMapReserveBlank(key);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
&& TryComp(equipment, out SpriteComponent? clothingSprite))
|
|
||||||
{
|
|
||||||
sprite.LayerSetRSI(index, clothingSprite.BaseRSI);
|
|
||||||
}
|
|
||||||
|
|
||||||
sprite.LayerSetData(index, layerData);
|
|
||||||
}
|
|
||||||
|
|
||||||
RaiseLocalEvent(equipment, new EquipmentVisualsUpdatedEvent(equipee, slot, revealedLayers));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Robust.Shared.Analyzers;
|
using Robust.Shared.Analyzers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using System.Collections.Generic;
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
namespace Content.Client.Hands
|
namespace Content.Client.Hands
|
||||||
{
|
{
|
||||||
@@ -11,10 +11,5 @@ namespace Content.Client.Hands
|
|||||||
public class HandsComponent : SharedHandsComponent
|
public class HandsComponent : SharedHandsComponent
|
||||||
{
|
{
|
||||||
public HandsGui? Gui { get; set; }
|
public HandsGui? Gui { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data about the current sprite layers that the hand is contributing to the owner entity. Used for sprite in-hands.
|
|
||||||
/// </summary>
|
|
||||||
public readonly Dictionary<HandLocation, HashSet<string>> RevealedLayers = new();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
62
Content.Client/Hands/HandsVisualizer.cs
Normal file
62
Content.Client/Hands/HandsVisualizer.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Shared.Hands.Components;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.ResourceManagement;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
|
namespace Content.Client.Hands
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class HandsVisualizer : AppearanceVisualizer
|
||||||
|
{
|
||||||
|
public override void OnChangeData(AppearanceComponent component)
|
||||||
|
{
|
||||||
|
base.OnChangeData(component);
|
||||||
|
|
||||||
|
var entities = IoCManager.Resolve<IEntityManager>();
|
||||||
|
if (!entities.TryGetComponent<ISpriteComponent>(component.Owner, out var sprite)) return;
|
||||||
|
if (!component.TryGetData(HandsVisuals.VisualState, out HandsVisualState visualState)) return;
|
||||||
|
|
||||||
|
foreach (HandLocation location in Enum.GetValues(typeof(HandLocation)))
|
||||||
|
{
|
||||||
|
var layerKey = LocationToLayerKey(location);
|
||||||
|
if (sprite.LayerMapTryGet(layerKey, out var layer))
|
||||||
|
{
|
||||||
|
sprite.RemoveLayer(layer);
|
||||||
|
sprite.LayerMapRemove(layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var resourceCache = IoCManager.Resolve<IResourceCache>();
|
||||||
|
var hands = visualState.Hands;
|
||||||
|
|
||||||
|
foreach (var hand in hands)
|
||||||
|
{
|
||||||
|
var rsi = resourceCache.GetResource<RSIResource>(SharedSpriteComponent.TextureRoot / hand.RsiPath).RSI;
|
||||||
|
|
||||||
|
var state = $"inhand-{hand.Location.ToString().ToLowerInvariant()}";
|
||||||
|
if (hand.EquippedPrefix != null)
|
||||||
|
state = $"{hand.EquippedPrefix}-" + state;
|
||||||
|
|
||||||
|
if (rsi.TryGetState(state, out var _))
|
||||||
|
{
|
||||||
|
var layerKey = LocationToLayerKey(hand.Location);
|
||||||
|
sprite.LayerMapReserveBlank(layerKey);
|
||||||
|
|
||||||
|
var layer = sprite.LayerMapGet(layerKey);
|
||||||
|
sprite.LayerSetVisible(layer, true);
|
||||||
|
sprite.LayerSetRSI(layer, rsi);
|
||||||
|
sprite.LayerSetColor(layer, hand.Color);
|
||||||
|
sprite.LayerSetState(layer, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string LocationToLayerKey(HandLocation location)
|
||||||
|
{
|
||||||
|
return location.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ using Content.Client.Animations;
|
|||||||
using Content.Client.HUD;
|
using Content.Client.HUD;
|
||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Item;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
@@ -12,7 +11,6 @@ using Robust.Shared.Containers;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Log;
|
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
@@ -34,7 +32,6 @@ namespace Content.Client.Hands
|
|||||||
SubscribeLocalEvent<HandsComponent, PlayerDetachedEvent>(HandlePlayerDetached);
|
SubscribeLocalEvent<HandsComponent, PlayerDetachedEvent>(HandlePlayerDetached);
|
||||||
SubscribeLocalEvent<HandsComponent, ComponentRemove>(HandleCompRemove);
|
SubscribeLocalEvent<HandsComponent, ComponentRemove>(HandleCompRemove);
|
||||||
SubscribeLocalEvent<HandsComponent, ComponentHandleState>(HandleComponentState);
|
SubscribeLocalEvent<HandsComponent, ComponentHandleState>(HandleComponentState);
|
||||||
SubscribeLocalEvent<HandsComponent, VisualsChangedEvent>(OnVisualsChanged);
|
|
||||||
|
|
||||||
SubscribeNetworkEvent<PickupAnimationEvent>(HandlePickupAnimation);
|
SubscribeNetworkEvent<PickupAnimationEvent>(HandlePickupAnimation);
|
||||||
}
|
}
|
||||||
@@ -183,95 +180,6 @@ namespace Content.Client.Hands
|
|||||||
RaiseNetworkEvent(new ActivateInHandMsg(handName));
|
RaiseNetworkEvent(new ActivateInHandMsg(handName));
|
||||||
}
|
}
|
||||||
|
|
||||||
#region visuals
|
|
||||||
protected override void HandleContainerModified(EntityUid uid, SharedHandsComponent handComp, ContainerModifiedMessage args)
|
|
||||||
{
|
|
||||||
if (handComp.TryGetHand(args.Container.ID, out var hand))
|
|
||||||
{
|
|
||||||
UpdateHandVisuals(uid, args.Entity, hand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Update the players sprite with new in-hand visuals.
|
|
||||||
/// </summary>
|
|
||||||
private void UpdateHandVisuals(EntityUid uid, EntityUid held, Hand hand, HandsComponent? handComp = null, SpriteComponent? sprite = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref handComp, ref sprite, false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (uid == _playerManager.LocalPlayer?.ControlledEntity)
|
|
||||||
UpdateGui();
|
|
||||||
|
|
||||||
// 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))
|
|
||||||
{
|
|
||||||
foreach (var key in revealedLayers)
|
|
||||||
{
|
|
||||||
sprite.RemoveLayer(key);
|
|
||||||
}
|
|
||||||
revealedLayers.Clear();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
revealedLayers = new();
|
|
||||||
handComp.RevealedLayers[hand.Location] = revealedLayers;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hand.HeldEntity == null)
|
|
||||||
{
|
|
||||||
// the held item was removed.
|
|
||||||
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ev = new GetInhandVisualsEvent(uid, hand.Location);
|
|
||||||
RaiseLocalEvent(held, ev, false);
|
|
||||||
|
|
||||||
if (ev.Layers.Count == 0)
|
|
||||||
{
|
|
||||||
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the new layers
|
|
||||||
foreach (var (key, layerData) in ev.Layers)
|
|
||||||
{
|
|
||||||
if (!revealedLayers.Add(key))
|
|
||||||
{
|
|
||||||
Logger.Warning($"Duplicate key for in-hand visuals: {key}. Are multiple components attempting to modify the same layer? Entity: {ToPrettyString(held)}");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = sprite.LayerMapReserveBlank(key);
|
|
||||||
|
|
||||||
// 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
|
|
||||||
&& TryComp(held, out SpriteComponent? clothingSprite))
|
|
||||||
{
|
|
||||||
sprite.LayerSetRSI(index, clothingSprite.BaseRSI);
|
|
||||||
}
|
|
||||||
|
|
||||||
sprite.LayerSetData(index, layerData);
|
|
||||||
}
|
|
||||||
|
|
||||||
RaiseLocalEvent(held, new HeldVisualsUpdatedEvent(uid, revealedLayers));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnVisualsChanged(EntityUid uid, HandsComponent component, VisualsChangedEvent args)
|
|
||||||
{
|
|
||||||
// update hands visuals if this item is in a hand (rather then inventory or other container).
|
|
||||||
if (component.TryGetHand(args.ContainerId, out var hand))
|
|
||||||
{
|
|
||||||
UpdateHandVisuals(uid, args.Item, hand, component);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
#region Gui
|
#region Gui
|
||||||
public void UpdateGui(HandsComponent? hands = null)
|
public void UpdateGui(HandsComponent? hands = null)
|
||||||
{
|
{
|
||||||
@@ -285,6 +193,14 @@ namespace Content.Client.Hands
|
|||||||
hands.Gui.Update(new HandsGuiState(states, hands.ActiveHand));
|
hands.Gui.Update(new HandsGuiState(states, hands.ActiveHand));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void UpdateHandVisuals(EntityUid uid, SharedHandsComponent? handComp = null, AppearanceComponent? appearance = null)
|
||||||
|
{
|
||||||
|
base.UpdateHandVisuals(uid, handComp, appearance);
|
||||||
|
|
||||||
|
if (uid == _playerManager.LocalPlayer?.ControlledEntity)
|
||||||
|
UpdateGui();
|
||||||
|
}
|
||||||
|
|
||||||
public override bool TrySetActiveHand(EntityUid uid, string? value, SharedHandsComponent? handComp = null)
|
public override bool TrySetActiveHand(EntityUid uid, string? value, SharedHandsComponent? handComp = null)
|
||||||
{
|
{
|
||||||
if (!base.TrySetActiveHand(uid, value, handComp))
|
if (!base.TrySetActiveHand(uid, value, handComp))
|
||||||
|
|||||||
@@ -30,12 +30,6 @@ namespace Content.Client.Inventory
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[DataField("speciesId")] public string? SpeciesId { get; set; }
|
[DataField("speciesId")] public string? SpeciesId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Data about the current layers that have been added to the players sprite due to the items in each equipment slot.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public readonly Dictionary<string, HashSet<string>> VisualLayerKeys = new();
|
|
||||||
|
|
||||||
public bool AttachedToGameHud;
|
public bool AttachedToGameHud;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
using Content.Shared.Hands;
|
|
||||||
using Content.Shared.Hands.Components;
|
|
||||||
using Content.Shared.Item;
|
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.ResourceManagement;
|
|
||||||
using Robust.Shared.Containers;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Log;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
|
||||||
using static Robust.Shared.GameObjects.SharedSpriteComponent;
|
|
||||||
|
|
||||||
namespace Content.Client.Items.Systems;
|
|
||||||
|
|
||||||
public sealed class ItemSystem : SharedItemSystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
|
||||||
[Dependency] private readonly IResourceCache _resCache = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<SharedItemComponent, GetInhandVisualsEvent>(OnGetVisuals);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region InhandVisuals
|
|
||||||
/// <summary>
|
|
||||||
/// When an items visual state changes, notify and entities that are holding this item that their sprite may need updating.
|
|
||||||
/// </summary>
|
|
||||||
public override void VisualsChanged(EntityUid uid, SharedItemComponent? item = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref item))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// if the item is in a container, it might be equipped to hands or inventory slots --> update visuals.
|
|
||||||
if (_containerSystem.TryGetContainingContainer(uid, out var container))
|
|
||||||
RaiseLocalEvent(container.Owner, new VisualsChangedEvent(uid, container.ID));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An entity holding this item is requesting visual information for in-hand sprites.
|
|
||||||
/// </summary>
|
|
||||||
private void OnGetVisuals(EntityUid uid, SharedItemComponent item, GetInhandVisualsEvent args)
|
|
||||||
{
|
|
||||||
var defaultKey = $"inhand-{args.Location.ToString().ToLowerInvariant()}";
|
|
||||||
|
|
||||||
// try get explicit visuals
|
|
||||||
if (item.InhandVisuals == null || !item.InhandVisuals.TryGetValue(args.Location, out var layers))
|
|
||||||
{
|
|
||||||
// get defaults
|
|
||||||
if (!TryGetDefaultVisuals(uid, item, defaultKey, out layers))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
foreach (var layer in layers)
|
|
||||||
{
|
|
||||||
var key = layer.MapKeys?.FirstOrDefault();
|
|
||||||
if (key == null)
|
|
||||||
{
|
|
||||||
key = i == 0 ? defaultKey : $"{defaultKey}-{i}";
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.Layers.Add((key, layer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If no explicit in-hand visuals were specified, this attempts to populate with default values.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Useful for lazily adding in-hand sprites without modifying yaml. And backwards compatibility.
|
|
||||||
/// </remarks>
|
|
||||||
private bool TryGetDefaultVisuals(EntityUid uid, SharedItemComponent item, string defaultKey, [NotNullWhen(true)] out List<PrototypeLayerData>? result)
|
|
||||||
{
|
|
||||||
result = null;
|
|
||||||
|
|
||||||
RSI? rsi = null;
|
|
||||||
|
|
||||||
if (item.RsiPath != null)
|
|
||||||
rsi = _resCache.GetResource<RSIResource>(TextureRoot / item.RsiPath).RSI;
|
|
||||||
else if (TryComp(uid, out SpriteComponent? sprite))
|
|
||||||
rsi = sprite.BaseRSI;
|
|
||||||
|
|
||||||
if (rsi == null || rsi.Path == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var state = (item.EquippedPrefix == null)
|
|
||||||
? defaultKey
|
|
||||||
: $"{item.EquippedPrefix}-{defaultKey}";
|
|
||||||
|
|
||||||
if (!rsi.TryGetState(state, out var _))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var layer = PrototypeLayerData.New();
|
|
||||||
layer.RsiPath = rsi.Path.ToString();
|
|
||||||
layer.State = state;
|
|
||||||
layer.MapKeys = new() { state };
|
|
||||||
|
|
||||||
result = new() { layer };
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using Content.Shared.Item;
|
|
||||||
|
|
||||||
namespace Content.Server.Item;
|
|
||||||
|
|
||||||
public sealed class ItemSystem : SharedItemSystem
|
|
||||||
{
|
|
||||||
// Ello Guvna
|
|
||||||
}
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using static Robust.Shared.GameObjects.SharedSpriteComponent;
|
|
||||||
|
|
||||||
namespace Content.Shared.Clothing;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised directed at a piece of clothing to get the set of layers to show on the wearer's sprite
|
|
||||||
/// </summary>
|
|
||||||
public class GetEquipmentVisualsEvent : EntityEventArgs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Entity that is wearing the item.
|
|
||||||
/// </summary>
|
|
||||||
public readonly EntityUid Equipee;
|
|
||||||
|
|
||||||
public readonly string Slot;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The layers that will be added to the entity that is wearing this item.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Note that the actual ordering of the layers depends on the order in which they are added to this list;
|
|
||||||
/// </remarks>
|
|
||||||
public List<(string, PrototypeLayerData)> Layers = new();
|
|
||||||
|
|
||||||
public GetEquipmentVisualsEvent(EntityUid equipee, string slot)
|
|
||||||
{
|
|
||||||
Equipee = equipee;
|
|
||||||
Slot = slot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised directed at a piece of clothing after its visuals have been updated.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Useful for systems/components that modify the visual layers that an item adds to a player. (e.g. RGB memes)
|
|
||||||
/// </remarks>
|
|
||||||
public class EquipmentVisualsUpdatedEvent : EntityEventArgs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Entity that is wearing the item.
|
|
||||||
/// </summary>
|
|
||||||
public readonly EntityUid Equipee;
|
|
||||||
|
|
||||||
public readonly string Slot;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The layers that this item is now revealing.
|
|
||||||
/// </summary>
|
|
||||||
public HashSet<string> RevealedLayers;
|
|
||||||
|
|
||||||
public EquipmentVisualsUpdatedEvent(EntityUid equipee, string slot, HashSet<string> revealedLayers)
|
|
||||||
{
|
|
||||||
Equipee = equipee;
|
|
||||||
Slot = slot;
|
|
||||||
RevealedLayers = revealedLayers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Shared.Item;
|
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
@@ -41,7 +40,7 @@ namespace Content.Shared.Hands.Components
|
|||||||
|
|
||||||
// update hands GUI with new entity.
|
// update hands GUI with new entity.
|
||||||
if (Owner.TryGetContainer(out var containter))
|
if (Owner.TryGetContainer(out var containter))
|
||||||
EntitySystem.Get<SharedItemSystem>().VisualsChanged(Owner);
|
EntitySystem.Get<SharedHandsSystem>().UpdateHandVisuals(containter.Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
|
using Content.Shared.Administration.Logs;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
@@ -16,7 +18,6 @@ using Robust.Shared.Physics;
|
|||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using static Robust.Shared.GameObjects.SharedSpriteComponent;
|
|
||||||
|
|
||||||
namespace Content.Shared.Hands.Components
|
namespace Content.Shared.Hands.Components
|
||||||
{
|
{
|
||||||
@@ -707,6 +708,47 @@ namespace Content.Shared.Hands.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region visualizerData
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum HandsVisuals : byte
|
||||||
|
{
|
||||||
|
VisualState
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class HandsVisualState : ICloneable
|
||||||
|
{
|
||||||
|
public List<HandVisualState> Hands { get; } = new();
|
||||||
|
|
||||||
|
public HandsVisualState(List<HandVisualState> hands)
|
||||||
|
{
|
||||||
|
Hands = hands;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object Clone()
|
||||||
|
{
|
||||||
|
return new HandsVisualState(new List<HandVisualState>(Hands));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public struct HandVisualState
|
||||||
|
{
|
||||||
|
public string RsiPath { get; }
|
||||||
|
public string? EquippedPrefix { get; }
|
||||||
|
public HandLocation Location { get; }
|
||||||
|
public Color Color { get; }
|
||||||
|
|
||||||
|
public HandVisualState(string rsiPath, string? equippedPrefix, HandLocation location, Color color)
|
||||||
|
{
|
||||||
|
RsiPath = rsiPath;
|
||||||
|
EquippedPrefix = equippedPrefix;
|
||||||
|
Location = location;
|
||||||
|
Color = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class Hand
|
public class Hand
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,67 +1,13 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using static Robust.Shared.GameObjects.SharedSpriteComponent;
|
|
||||||
|
|
||||||
namespace Content.Shared.Hands
|
namespace Content.Shared.Hands
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Raised directed at an item that needs to update its in-hand sprites/layers.
|
|
||||||
/// </summary>
|
|
||||||
public class GetInhandVisualsEvent : EntityEventArgs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Entity that owns the hand holding the item.
|
|
||||||
/// </summary>
|
|
||||||
public readonly EntityUid User;
|
|
||||||
|
|
||||||
public readonly HandLocation Location;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The layers that will be added to the entity that is holding this item.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Note that the actual ordering of the layers depends on the order in which they are added to this list;
|
|
||||||
/// </remarks>
|
|
||||||
public List<(string, PrototypeLayerData)> Layers = new();
|
|
||||||
|
|
||||||
public GetInhandVisualsEvent(EntityUid user, HandLocation location)
|
|
||||||
{
|
|
||||||
User = user;
|
|
||||||
Location = location;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised directed at an item after its visuals have been updated.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Useful for systems/components that modify the visual layers that an item adds to a player. (e.g. RGB memes)
|
|
||||||
/// </remarks>
|
|
||||||
public class HeldVisualsUpdatedEvent : EntityEventArgs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Entity that is holding the item.
|
|
||||||
/// </summary>
|
|
||||||
public readonly EntityUid User;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The layers that this item is now revealing.
|
|
||||||
/// </summary>
|
|
||||||
public HashSet<string> RevealedLayers;
|
|
||||||
|
|
||||||
public HeldVisualsUpdatedEvent(EntityUid user, HashSet<string> revealedLayers)
|
|
||||||
{
|
|
||||||
User = user;
|
|
||||||
RevealedLayers = revealedLayers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised when an entity item in a hand is deselected.
|
/// Raised when an entity item in a hand is deselected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Content.Shared.Administration.Logs;
|
|||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
|
using Content.Shared.Item;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Input.Binding;
|
using Robust.Shared.Input.Binding;
|
||||||
@@ -10,6 +11,7 @@ using Robust.Shared.Log;
|
|||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Players;
|
using Robust.Shared.Players;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Content.Shared.Hands
|
namespace Content.Shared.Hands
|
||||||
{
|
{
|
||||||
@@ -23,7 +25,8 @@ namespace Content.Shared.Hands
|
|||||||
|
|
||||||
SubscribeAllEvent<RequestSetHandEvent>(HandleSetHand);
|
SubscribeAllEvent<RequestSetHandEvent>(HandleSetHand);
|
||||||
SubscribeLocalEvent<SharedHandsComponent, EntRemovedFromContainerMessage>(HandleContainerRemoved);
|
SubscribeLocalEvent<SharedHandsComponent, EntRemovedFromContainerMessage>(HandleContainerRemoved);
|
||||||
SubscribeLocalEvent<SharedHandsComponent, EntInsertedIntoContainerMessage>(HandleContainerInserted);
|
SubscribeLocalEvent<SharedHandsComponent, EntInsertedIntoContainerMessage>(HandleContainerModified);
|
||||||
|
SubscribeLocalEvent<SharedHandsComponent, ItemPrefixChangeEvent>(OnPrefixChanged);
|
||||||
|
|
||||||
CommandBinds.Builder
|
CommandBinds.Builder
|
||||||
.Bind(ContentKeyFunctions.Drop, new PointerInputCmdHandler(DropPressed))
|
.Bind(ContentKeyFunctions.Drop, new PointerInputCmdHandler(DropPressed))
|
||||||
@@ -31,6 +34,15 @@ namespace Content.Shared.Hands
|
|||||||
.Register<SharedHandsSystem>();
|
.Register<SharedHandsSystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnPrefixChanged(EntityUid uid, SharedHandsComponent component, ItemPrefixChangeEvent args)
|
||||||
|
{
|
||||||
|
// update hands visuals if this item is in a hand (rather then inventory or other container).
|
||||||
|
if (component.HasHand(args.ContainerId))
|
||||||
|
{
|
||||||
|
UpdateHandVisuals(uid, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
{
|
{
|
||||||
base.Shutdown();
|
base.Shutdown();
|
||||||
@@ -124,26 +136,44 @@ namespace Content.Shared.Hands
|
|||||||
|
|
||||||
public abstract void PickupAnimation(EntityUid item, EntityCoordinates initialPosition, Vector2 finalPosition,
|
public abstract void PickupAnimation(EntityUid item, EntityCoordinates initialPosition, Vector2 finalPosition,
|
||||||
EntityUid? exclude);
|
EntityUid? exclude);
|
||||||
#endregion
|
|
||||||
|
|
||||||
protected virtual void HandleContainerRemoved(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
|
protected virtual void HandleContainerRemoved(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
|
||||||
{
|
{
|
||||||
HandleContainerModified(uid, component, args);
|
HandleContainerModified(uid, component, args);
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
protected virtual void HandleContainerModified(EntityUid uid, SharedHandsComponent hands, ContainerModifiedMessage args)
|
#region visuals
|
||||||
|
private void HandleContainerModified(EntityUid uid, SharedHandsComponent hands, ContainerModifiedMessage args)
|
||||||
{
|
{
|
||||||
// client updates hand visuals here.
|
UpdateHandVisuals(uid, hands);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleContainerInserted(EntityUid uid, SharedHandsComponent component, EntInsertedIntoContainerMessage args)
|
/// <summary>
|
||||||
|
/// Update the In-Hand sprites
|
||||||
|
/// </summary>
|
||||||
|
public virtual void UpdateHandVisuals(EntityUid uid, SharedHandsComponent? handComp = null, AppearanceComponent? appearance = null)
|
||||||
{
|
{
|
||||||
// un-rotate entities. needed for things like directional flashlights
|
if (!Resolve(uid, ref handComp, ref appearance, false))
|
||||||
Transform(args.Entity).LocalRotation = 0;
|
return;
|
||||||
|
|
||||||
HandleContainerModified(uid, component, args);
|
var handsVisuals = new List<HandVisualState>();
|
||||||
|
foreach (var hand in handComp.Hands)
|
||||||
|
{
|
||||||
|
if (hand.HeldEntity == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!TryComp(hand.HeldEntity.Value, out SharedItemComponent? item) || item.RsiPath == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var handState = new HandVisualState(item.RsiPath, item.EquippedPrefix, hand.Location, item.Color);
|
||||||
|
handsVisuals.Add(handState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appearance.SetData(HandsVisuals.VisualState, new HandsVisualState(handsVisuals));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
private void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs)
|
private void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (eventArgs.SenderSession.AttachedEntity == null)
|
if (eventArgs.SenderSession.AttachedEntity == null)
|
||||||
|
|||||||
@@ -53,9 +53,6 @@ public abstract partial class InventorySystem
|
|||||||
if(!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
|
if(!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// un-rotate entities. needed for things like directional flashlights on hardsuit helmets
|
|
||||||
Transform(args.Entity).LocalRotation = 0;
|
|
||||||
|
|
||||||
var equippedEvent = new DidEquipEvent(uid, args.Entity, slotDef);
|
var equippedEvent = new DidEquipEvent(uid, args.Entity, slotDef);
|
||||||
RaiseLocalEvent(uid, equippedEvent);
|
RaiseLocalEvent(uid, equippedEvent);
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,10 @@ using Robust.Shared.Containers;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Content.Shared.Item
|
namespace Content.Shared.Item
|
||||||
{
|
{
|
||||||
public abstract class SharedItemSystem : EntitySystem
|
public class ItemSystem : EntitySystem
|
||||||
{
|
{
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -29,11 +28,13 @@ namespace Content.Shared.Item
|
|||||||
|
|
||||||
component.Size = state.Size;
|
component.Size = state.Size;
|
||||||
component.EquippedPrefix = state.EquippedPrefix;
|
component.EquippedPrefix = state.EquippedPrefix;
|
||||||
|
component.Color = state.Color;
|
||||||
|
component.RsiPath = state.RsiPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGetState(EntityUid uid, SharedItemComponent component, ref ComponentGetState args)
|
private void OnGetState(EntityUid uid, SharedItemComponent component, ref ComponentGetState args)
|
||||||
{
|
{
|
||||||
args.State = new ItemComponentState(component.Size, component.EquippedPrefix);
|
args.State = new ItemComponentState(component.Size, component.EquippedPrefix, component.Color, component.RsiPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUnequipped(EntityUid uid, SharedSpriteComponent component, GotUnequippedEvent args)
|
private void OnUnequipped(EntityUid uid, SharedSpriteComponent component, GotUnequippedEvent args)
|
||||||
@@ -69,11 +70,5 @@ namespace Content.Shared.Item
|
|||||||
|
|
||||||
args.Verbs.Add(verb);
|
args.Verbs.Add(verb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Notifies any entity that is holding or wearing this item that they may need to update their sprite.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void VisualsChanged(EntityUid owner, SharedItemComponent? item = null)
|
|
||||||
{ }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using Content.Shared.Hands;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Interaction.Helpers;
|
using Content.Shared.Interaction.Helpers;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Sound;
|
using Content.Shared.Sound;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
@@ -12,7 +13,6 @@ using Robust.Shared.Maths;
|
|||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using static Robust.Shared.GameObjects.SharedSpriteComponent;
|
|
||||||
|
|
||||||
namespace Content.Shared.Item
|
namespace Content.Shared.Item
|
||||||
{
|
{
|
||||||
@@ -40,18 +40,10 @@ namespace Content.Shared.Item
|
|||||||
[DataField("size")]
|
[DataField("size")]
|
||||||
private int _size;
|
private int _size;
|
||||||
|
|
||||||
[DataField("inhandVisuals")]
|
|
||||||
public Dictionary<HandLocation, List<PrototypeLayerData>> InhandVisuals = new();
|
|
||||||
|
|
||||||
[DataField("clothingVisuals")]
|
|
||||||
public Dictionary<string, List<PrototypeLayerData>> ClothingVisuals = new();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Part of the state of the sprite shown on the player when this item is in their hands or inventory.
|
/// Part of the state of the sprite shown on the player when this item is in their hands.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
// todo paul make this update slotvisuals on client on change
|
||||||
/// Only used if <see cref="InhandVisuals"/> or <see cref="ClothingVisuals"/> are unspecified.
|
|
||||||
/// </remarks>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public string? EquippedPrefix
|
public string? EquippedPrefix
|
||||||
{
|
{
|
||||||
@@ -59,7 +51,7 @@ namespace Content.Shared.Item
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
_equippedPrefix = value;
|
_equippedPrefix = value;
|
||||||
EntitySystem.Get<SharedItemSystem>().VisualsChanged(Owner, this);
|
OnEquippedPrefixChange();
|
||||||
Dirty();
|
Dirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +65,6 @@ namespace Content.Shared.Item
|
|||||||
[DataField("EquipSound")]
|
[DataField("EquipSound")]
|
||||||
public SoundSpecifier? EquipSound { get; set; } = default!;
|
public SoundSpecifier? EquipSound { get; set; } = default!;
|
||||||
|
|
||||||
// TODO REMOVE. Currently nonfunctional and only used by RGB system. #6253 Fixes this but requires #6252
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Color of the sprite shown on the player when this item is in their hands.
|
/// Color of the sprite shown on the player when this item is in their hands.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -91,11 +82,20 @@ namespace Content.Shared.Item
|
|||||||
private Color _color = Color.White;
|
private Color _color = Color.White;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rsi of the sprite shown on the player when this item is in their hands. Used to generate a default entry for <see cref="InhandVisuals"/>
|
/// Rsi of the sprite shown on the player when this item is in their hands.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public string? RsiPath
|
||||||
|
{
|
||||||
|
get => _rsiPath;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_rsiPath = value;
|
||||||
|
Dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
[DataField("sprite")]
|
[DataField("sprite")]
|
||||||
public readonly string? RsiPath;
|
private string? _rsiPath;
|
||||||
|
|
||||||
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
@@ -116,6 +116,12 @@ namespace Content.Shared.Item
|
|||||||
return hands.TryPickupEntityToActiveHand(Owner, animateUser: true);
|
return hands.TryPickupEntityToActiveHand(Owner, animateUser: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnEquippedPrefixChange()
|
||||||
|
{
|
||||||
|
if (Owner.TryGetContainer(out var container))
|
||||||
|
_entMan.EventBus.RaiseLocalEvent(container.Owner, new ItemPrefixChangeEvent(Owner, container.ID));
|
||||||
|
}
|
||||||
|
|
||||||
public void RemovedFromSlot()
|
public void RemovedFromSlot()
|
||||||
{
|
{
|
||||||
if (_entMan.TryGetComponent(Owner, out SharedSpriteComponent component))
|
if (_entMan.TryGetComponent(Owner, out SharedSpriteComponent component))
|
||||||
@@ -134,25 +140,29 @@ namespace Content.Shared.Item
|
|||||||
{
|
{
|
||||||
public int Size { get; }
|
public int Size { get; }
|
||||||
public string? EquippedPrefix { get; }
|
public string? EquippedPrefix { get; }
|
||||||
|
public Color Color { get; }
|
||||||
|
public string? RsiPath { get; }
|
||||||
|
|
||||||
public ItemComponentState(int size, string? equippedPrefix)
|
public ItemComponentState(int size, string? equippedPrefix, Color color, string? rsiPath)
|
||||||
{
|
{
|
||||||
Size = size;
|
Size = size;
|
||||||
EquippedPrefix = equippedPrefix;
|
EquippedPrefix = equippedPrefix;
|
||||||
|
Color = color;
|
||||||
|
RsiPath = rsiPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised when an item's visual state is changed. The event is directed at the entity that contains this item, so
|
/// Raised when an item's EquippedPrefix is changed. The event is directed at the entity that contains this item, so
|
||||||
/// that it can properly update its hands or inventory sprites and GUI.
|
/// that it can properly update its sprite/GUI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class VisualsChangedEvent : EntityEventArgs
|
public class ItemPrefixChangeEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
public readonly EntityUid Item;
|
public readonly EntityUid Item;
|
||||||
public readonly string ContainerId;
|
public readonly string ContainerId;
|
||||||
|
|
||||||
public VisualsChangedEvent(EntityUid item, string containerId)
|
public ItemPrefixChangeEvent(EntityUid item, string containerId)
|
||||||
{
|
{
|
||||||
Item = item;
|
Item = item;
|
||||||
ContainerId = containerId;
|
ContainerId = containerId;
|
||||||
|
|||||||
@@ -502,6 +502,7 @@
|
|||||||
normal: monkey
|
normal: monkey
|
||||||
crit: dead
|
crit: dead
|
||||||
dead: dead
|
dead: dead
|
||||||
|
- type: HandsVisualizer
|
||||||
- type: FireVisualizer
|
- type: FireVisualizer
|
||||||
sprite: Mobs/Effects/onfire.rsi
|
sprite: Mobs/Effects/onfire.rsi
|
||||||
normalState: Monkey_burning
|
normalState: Monkey_burning
|
||||||
|
|||||||
@@ -243,6 +243,7 @@
|
|||||||
fireStackAlternateState: 3
|
fireStackAlternateState: 3
|
||||||
- type: CreamPiedVisualizer
|
- type: CreamPiedVisualizer
|
||||||
state: creampie_human
|
state: creampie_human
|
||||||
|
- type: HandsVisualizer
|
||||||
- type: DamageVisualizer
|
- type: DamageVisualizer
|
||||||
thresholds: [20, 40, 100]
|
thresholds: [20, 40, 100]
|
||||||
targetLayers:
|
targetLayers:
|
||||||
|
|||||||
Reference in New Issue
Block a user