diff --git a/Content.Client/Storage/Visualizers/MappedItemVisualizer.cs b/Content.Client/Storage/Visualizers/MappedItemVisualizer.cs new file mode 100644 index 0000000000..e3efd2712a --- /dev/null +++ b/Content.Client/Storage/Visualizers/MappedItemVisualizer.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Linq; +using Content.Shared.Storage.ItemCounter; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Utility; + +namespace Content.Client.Storage.Visualizers +{ + [UsedImplicitly] + public class MappedItemVisualizer : AppearanceVisualizer + { + [DataField("sprite")] private ResourcePath? _rsiPath; + private List _spriteLayers = new(); + + public override void InitializeEntity(IEntity entity) + { + base.InitializeEntity(entity); + + if (entity.TryGetComponent(out var spriteComponent)) + { + _rsiPath ??= spriteComponent.BaseRSI!.Path!; + } + } + + + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + if (component.Owner.TryGetComponent(out var spriteComponent)) + { + if (_spriteLayers.Count == 0) + { + InitLayers(spriteComponent, component); + } + EnableLayers(spriteComponent, component); + + } + } + + private void InitLayers(ISpriteComponent spriteComponent, AppearanceComponent component) + { + if (!component.TryGetData(StorageMapVisuals.InitLayers, out var wrapper)) + return; + + _spriteLayers.AddRange(wrapper.QueuedEntities); + + foreach (var sprite in _spriteLayers) + { + spriteComponent.LayerMapReserveBlank(sprite); + spriteComponent.LayerSetSprite(sprite, new SpriteSpecifier.Rsi(_rsiPath!, sprite)); + spriteComponent.LayerSetVisible(sprite, false); + } + } + + private void EnableLayers(ISpriteComponent spriteComponent, AppearanceComponent component) + { + if (!component.TryGetData(StorageMapVisuals.LayerChanged, out var wrapper)) + return; + + + foreach (var layerName in _spriteLayers) + { + var show = wrapper.QueuedEntities.Contains(layerName); + spriteComponent.LayerSetVisible(layerName, show); + } + } + } +} diff --git a/Content.Server/Storage/Components/StorageCounterComponent.cs b/Content.Server/Storage/Components/StorageCounterComponent.cs index 2279c66e52..113b466751 100644 --- a/Content.Server/Storage/Components/StorageCounterComponent.cs +++ b/Content.Server/Storage/Components/StorageCounterComponent.cs @@ -1,4 +1,5 @@ +using System; using System.Collections.Generic; using Content.Shared.Stacks; using Content.Shared.Tag; @@ -21,6 +22,7 @@ namespace Content.Server.Storage.Components /// amount: 6 # Note: this field can be omitted /// countTag: Cigarette # Note: field doesn't point to entity Id, but its tag /// + [Obsolete("Should be deprecated in favor of SharedItemCounterSystem")] [RegisterComponent] public class StorageCounterComponent : Component, ISerializationHooks { diff --git a/Content.Server/Storage/Components/StorageFillComponent.cs b/Content.Server/Storage/Components/StorageFillComponent.cs index db253074e9..788a1727bb 100644 --- a/Content.Server/Storage/Components/StorageFillComponent.cs +++ b/Content.Server/Storage/Components/StorageFillComponent.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Content.Shared.Storage; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Log; @@ -16,8 +17,7 @@ namespace Content.Server.Storage.Components { public override string Name => "StorageFill"; - [DataField("contents")] - private List _contents = new(); + [DataField("contents")] private List _contents = new(); public IReadOnlyList Contents => _contents; @@ -40,7 +40,8 @@ namespace Content.Server.Storage.Components foreach (var storageItem in _contents) { if (string.IsNullOrEmpty(storageItem.PrototypeId)) continue; - if (!string.IsNullOrEmpty(storageItem.GroupId) && alreadySpawnedGroups.Contains(storageItem.GroupId)) continue; + if (!string.IsNullOrEmpty(storageItem.GroupId) && + alreadySpawnedGroups.Contains(storageItem.GroupId)) continue; if (storageItem.SpawnProbability != 1f && !random.Prob(storageItem.SpawnProbability)) @@ -50,8 +51,10 @@ namespace Content.Server.Storage.Components for (var i = 0; i < storageItem.Amount; i++) { - storage.Insert(Owner.EntityManager.SpawnEntity(storageItem.PrototypeId, Owner.Transform.Coordinates)); + storage.Insert( + Owner.EntityManager.SpawnEntity(storageItem.PrototypeId, Owner.Transform.Coordinates)); } + if (!string.IsNullOrEmpty(storageItem.GroupId)) alreadySpawnedGroups.Add(storageItem.GroupId); } } @@ -63,13 +66,13 @@ namespace Content.Server.Storage.Components [DataField("id", customTypeSerializer: typeof(PrototypeIdSerializer))] public string? PrototypeId; - [DataField("prob")] - public float SpawnProbability; + [DataField("prob")] public float SpawnProbability; + /// /// The probability that an item will spawn. Takes decimal form so 0.05 is 5%, 0.50 is 50% etc. /// - [DataField("orGroup")] - public string GroupId; + [DataField("orGroup")] public string GroupId; + /// /// orGroup signifies to pick between entities designated with an ID. /// @@ -92,8 +95,7 @@ namespace Content.Server.Storage.Components /// /// /// - [DataField("amount")] - public int Amount; + [DataField("amount")] public int Amount; public void PopulateDefaultValues() { diff --git a/Content.Server/Storage/ItemCounterSystem.cs b/Content.Server/Storage/ItemCounterSystem.cs new file mode 100644 index 0000000000..9e79ab984f --- /dev/null +++ b/Content.Server/Storage/ItemCounterSystem.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using Content.Server.Storage.Components; +using Content.Shared.Storage.ItemCounter; +using JetBrains.Annotations; +using Robust.Shared.Containers; +using Robust.Shared.GameObjects; + +namespace Content.Server.Storage +{ + [UsedImplicitly] + public class ItemCounterSystem : SharedItemCounterSystem + { + protected override bool TryGetContainer(ContainerModifiedMessage msg, + ItemCounterComponent itemCounter, + out IReadOnlyList showLayers) + { + if (msg.Container.Owner.TryGetComponent(out ServerStorageComponent? component)) + { + var containedLayers = component.StoredEntities ?? new List(); + var list = new List(); + foreach (var mapLayerData in itemCounter.MapLayers.Values) + { + foreach (var entity in containedLayers) + { + if (mapLayerData.Whitelist.IsValid(entity)) + { + list.Add(mapLayerData.Layer); + break; + } + } + } + + showLayers = list; + return true; + } + + showLayers = new List(); + return false; + } + } +} diff --git a/Content.Shared/Storage/ItemCounter/ItemCounterComponent.cs b/Content.Shared/Storage/ItemCounter/ItemCounterComponent.cs new file mode 100644 index 0000000000..c06dc53987 --- /dev/null +++ b/Content.Shared/Storage/ItemCounter/ItemCounterComponent.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Shared.Storage.ItemCounter +{ + [RegisterComponent] + public class ItemCounterComponent : Component, ISerializationHooks + { + public override string Name => "ItemCounter"; + + [DataField("mapLayers")] public readonly Dictionary MapLayers = new(); + + void ISerializationHooks.AfterDeserialization() + { + foreach (var (layerName, val) in MapLayers) + { + val.Layer = layerName; + } + } + + } +} \ No newline at end of file diff --git a/Content.Shared/Storage/ItemCounter/SharedItemCounterSystem.cs b/Content.Shared/Storage/ItemCounter/SharedItemCounterSystem.cs new file mode 100644 index 0000000000..cde603bec9 --- /dev/null +++ b/Content.Shared/Storage/ItemCounter/SharedItemCounterSystem.cs @@ -0,0 +1,53 @@ +using System.Collections.Generic; +using JetBrains.Annotations; +using Robust.Shared.Containers; +using Robust.Shared.GameObjects; + +namespace Content.Shared.Storage.ItemCounter +{ + [UsedImplicitly] + public abstract class SharedItemCounterSystem : EntitySystem + { + /// + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(InitLayers); + SubscribeLocalEvent(HandleEntityInsert); + SubscribeLocalEvent(HandleEntityRemoved); + } + + private void InitLayers(EntityUid uid, ItemCounterComponent component, ComponentInit args) + { + if (component.Owner.TryGetComponent(out SharedAppearanceComponent? appearanceComponent)) + { + var list = new List(component.MapLayers.Keys); + appearanceComponent.SetData(StorageMapVisuals.InitLayers, new ShowLayerData(list)); + } + } + + private void HandleEntityRemoved(EntityUid uid, ItemCounterComponent itemCounter, + EntRemovedFromContainerMessage args) + { + if (itemCounter.Owner.TryGetComponent(out SharedAppearanceComponent? appearanceComponent) + && TryGetContainer(args, itemCounter, out var containedLayers)) + { + appearanceComponent.SetData(StorageMapVisuals.LayerChanged, new ShowLayerData(containedLayers)); + } + } + + private void HandleEntityInsert(EntityUid uid, ItemCounterComponent itemCounter, + EntInsertedIntoContainerMessage args) + { + if (itemCounter.Owner.TryGetComponent(out SharedAppearanceComponent? appearanceComponent) + && TryGetContainer(args, itemCounter, out var containedLayers)) + { + appearanceComponent.SetData(StorageMapVisuals.LayerChanged, new ShowLayerData(containedLayers)); + } + } + + protected abstract bool TryGetContainer(ContainerModifiedMessage msg, + ItemCounterComponent itemCounter, + out IReadOnlyList containedLayers); + } +} \ No newline at end of file diff --git a/Content.Shared/Storage/ItemCounter/SharedMapLayerData.cs b/Content.Shared/Storage/ItemCounter/SharedMapLayerData.cs new file mode 100644 index 0000000000..4bc7867ddf --- /dev/null +++ b/Content.Shared/Storage/ItemCounter/SharedMapLayerData.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using Content.Shared.Whitelist; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Shared.Storage.ItemCounter +{ + [Serializable, NetSerializable] + public enum StorageMapVisuals : sbyte + { + InitLayers, + LayerChanged, + } + + [Serializable] + [DataDefinition] + public class SharedMapLayerData + { + public string Layer = string.Empty; + + [DataField("whitelist", required: true)] + public EntityWhitelist Whitelist { get; set; } = new(); + } + + [Serializable, NetSerializable] + public class ShowLayerData + { + public IReadOnlyList QueuedEntities { get; internal set; } + + public ShowLayerData() + { + QueuedEntities = new List(); + } + + public ShowLayerData(IReadOnlyList other) + { + QueuedEntities = other; + } + + public ShowLayerData(ShowLayerData other) + { + QueuedEntities = other.QueuedEntities; + } + } +} \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Fun/crayons.yml b/Resources/Prototypes/Entities/Objects/Fun/crayons.yml index 919fa8056c..158d3979ee 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/crayons.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/crayons.yml @@ -1,6 +1,30 @@ - type: Tag id: Crayon +- type: Tag + id: CrayonBlack + +- type: Tag + id: CrayonBlue + +- type: Tag + id: CrayonGreen + +- type: Tag + id: CrayonOrange + +- type: Tag + id: CrayonPurple + +- type: Tag + id: CrayonRed + +- type: Tag + id: CrayonWhite + +- type: Tag + id: CrayonYellow + - type: entity abstract: true parent: BaseItem @@ -30,6 +54,9 @@ - type: Crayon color: white capacity: 5 + - type: Tag + tags: + - CrayonWhite - type: entity parent: Crayon @@ -42,6 +69,9 @@ - type: Crayon color: white capacity: 5 + - type: Tag + tags: + - CrayonWhite - type: entity parent: Crayon @@ -54,6 +84,9 @@ - type: Crayon color: black capacity: 5 + - type: Tag + tags: + - CrayonBlack - type: entity parent: Crayon @@ -66,6 +99,9 @@ - type: Crayon color: red capacity: 5 + - type: Tag + tags: + - CrayonRed - type: entity parent: Crayon @@ -78,6 +114,9 @@ - type: Crayon color: orange capacity: 5 + - type: Tag + tags: + - CrayonOrange - type: entity parent: Crayon @@ -90,6 +129,9 @@ - type: Crayon color: yellow capacity: 5 + - type: Tag + tags: + - CrayonYellow - type: entity parent: Crayon @@ -102,6 +144,9 @@ - type: Crayon color: green capacity: 5 + - type: Tag + tags: + - CrayonGreen - type: entity parent: Crayon @@ -114,6 +159,9 @@ - type: Crayon color: lightblue capacity: 5 + - type: Tag + tags: + - CrayonBlue - type: entity parent: Crayon @@ -126,6 +174,9 @@ - type: Crayon color: purple capacity: 5 + - type: Tag + tags: + - CrayonPurple - type: entity parent: BaseItem @@ -152,15 +203,38 @@ - id: CrayonBlue - id: CrayonPurple - id: CrayonBlack + - type: ItemCounter + mapLayers: + black_box: + whitelist: + tags: + - CrayonBlack + blue_box: + whitelist: + tags: + - CrayonBlue + green_box: + whitelist: + tags: + - CrayonGreen + orange_box: + whitelist: + tags: + - CrayonOrange + purple_box: + whitelist: + tags: + - CrayonPurple + red_box: + whitelist: + tags: + - CrayonRed + yellow_box: + whitelist: + tags: + - CrayonYellow + - type: Appearance visuals: - - type: StackVisualizer - composite: true - stackLayers: - - red_box - - orange_box - - yellow_box - - green_box - - blue_box - - purple_box - - black_box + - type: MappedItemVisualizer +