diff --git a/Content.Client/GameObjects/Components/StackComponent.cs b/Content.Client/GameObjects/Components/StackComponent.cs index 5fe8997604..8b54c61130 100644 --- a/Content.Client/GameObjects/Components/StackComponent.cs +++ b/Content.Client/GameObjects/Components/StackComponent.cs @@ -1,6 +1,9 @@ -using Content.Client.UserInterface.Stylesheets; +#nullable enable + +using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; using Content.Shared.GameObjects.Components; +using Robust.Client.GameObjects; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Shared.GameObjects; @@ -15,6 +18,7 @@ namespace Content.Client.GameObjects.Components public class StackComponent : SharedStackComponent, IItemStatus { [ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded; + [ComponentDependency] private readonly AppearanceComponent? _appearanceComponent = default!; public Control MakeControl() => new StatusControl(this); @@ -23,12 +27,30 @@ namespace Content.Client.GameObjects.Components get => base.Count; set { + var valueChanged = value != Count; base.Count = value; + + if (valueChanged) + { + _appearanceComponent?.SetData(StackVisuals.Actual, Count); + + } _uiUpdateNeeded = true; } } + public override void Initialize() + { + base.Initialize(); + + if (!Owner.Deleted) + { + _appearanceComponent?.SetData(StackVisuals.MaxCount, MaxCount); + _appearanceComponent?.SetData(StackVisuals.Hide, false); + } + } + private sealed class StatusControl : Control { private readonly StackComponent _parent; @@ -58,4 +80,4 @@ namespace Content.Client.GameObjects.Components } } } -} +} \ No newline at end of file diff --git a/Content.Client/GameObjects/Components/StackVisualizer.cs b/Content.Client/GameObjects/Components/StackVisualizer.cs new file mode 100644 index 0000000000..d409e79f0f --- /dev/null +++ b/Content.Client/GameObjects/Components/StackVisualizer.cs @@ -0,0 +1,174 @@ +#nullable enable + +using System.Collections.Generic; +using Content.Shared.GameObjects.Components; +using Content.Shared.Utility; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Utility; +using YamlDotNet.RepresentationModel; + +namespace Content.Client.GameObjects.Components +{ + /// + /// Visualizer for items that come in stacks and have different appearance + /// depending on the size of the stack. Visualizer can work by switching between different + /// icons in _spriteLayers or if the sprite layers are supposed to be composed as transparent layers. + /// The former behavior is default and the latter behavior can be defined in prototypes. + /// + /// + /// To define a Stack Visualizer prototype insert the following + /// snippet (you can skip Appearance if already defined) + /// + /// + /// - type: Appearance + /// visuals: + /// - type: StackVisualizer + /// stackLayers: + /// - goldbar_10 + /// - goldbar_20 + /// - goldbar_30 + /// + /// + /// + /// Defining a stack visualizer with composable transparent layers + /// + /// - type: StackVisualizer + /// composite: true + /// stackLayers: + /// - cigarette_1 + /// - cigarette_2 + /// - cigarette_3 + /// - cigarette_4 + /// - cigarette_5 + /// - cigarette_6 + /// + /// + /// + /// + [UsedImplicitly] + public class StackVisualizer : AppearanceVisualizer + { + /// + /// Default IconLayer stack. + /// + private const int IconLayer = 0; + + /// + /// Sprite layers used in stack visualizer. Sprites first in layer correspond to lower stack states + /// e.g. _spriteLayers[0] is lower stack level than _spriteLayers[1]. + /// + private readonly List _spriteLayers = new(); + + /// + /// Determines if the visualizer uses composite or non-composite layers for icons. Defaults to false. + /// + /// + /// + /// false: they are opaque and mutually exclusive (e.g. sprites in a wire coil). Default value + /// + /// + /// true: they are transparent and thus layered one over another in ascending order first + /// + /// + /// + /// + private bool _isComposite; + + public override void LoadData(YamlMappingNode mapping) + { + base.LoadData(mapping); + + if (mapping.TryGetNode("stackLayers", out var spriteSequenceNode)) + { + foreach (var yamlNode in spriteSequenceNode) + { + _spriteLayers.Add(((YamlScalarNode) yamlNode).Value!); + } + } + + if (mapping.TryGetNode("composite", out var transparent)) + { + _isComposite = transparent.AsBool(); + } + } + + public override void InitializeEntity(IEntity entity) + { + base.InitializeEntity(entity); + + if (_isComposite + && _spriteLayers.Count > 0 + && entity.TryGetComponent(out var spriteComponent)) + { + foreach (var sprite in _spriteLayers) + { + var rsiPath = spriteComponent.BaseRSI!.Path!; + spriteComponent.LayerMapReserveBlank(sprite); + spriteComponent.LayerSetSprite(sprite, new SpriteSpecifier.Rsi(rsiPath, sprite)); + spriteComponent.LayerSetVisible(sprite, false); + } + } + } + + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + if (component.Owner.TryGetComponent(out var spriteComponent)) + { + if (_isComposite) + { + ProcessCompositeSprites(component, spriteComponent); + } + else + { + ProcessOpaqueSprites(component, spriteComponent); + } + } + } + + private void ProcessOpaqueSprites(AppearanceComponent component, ISpriteComponent spriteComponent) + { + // Skip processing if no actual + if (!component.TryGetData(StackVisuals.Actual, out var actual)) return; + if (!component.TryGetData(StackVisuals.MaxCount, out var maxCount)) + { + maxCount = _spriteLayers.Count; + } + + var activeLayer = ContentHelpers.RoundToNearestLevels(actual, maxCount, _spriteLayers.Count - 1); + spriteComponent.LayerSetState(IconLayer, _spriteLayers[activeLayer]); + } + + private void ProcessCompositeSprites(AppearanceComponent component, ISpriteComponent spriteComponent) + { + // If hidden, don't render any sprites + if (!component.TryGetData(StackVisuals.Hide, out var hide) + || hide) + { + foreach (var transparentSprite in _spriteLayers) + { + spriteComponent.LayerSetVisible(transparentSprite, false); + } + + return; + } + + // Skip processing if no actual/maxCount + if (!component.TryGetData(StackVisuals.Actual, out var actual)) return; + if (!component.TryGetData(StackVisuals.MaxCount, out var maxCount)) + { + maxCount = _spriteLayers.Count; + } + + + var activeTill = ContentHelpers.RoundToNearestLevels(actual, maxCount, _spriteLayers.Count); + for (var i = 0; i < _spriteLayers.Count; i++) + { + spriteComponent.LayerSetVisible(_spriteLayers[i], i < activeTill); + } + } + } +} diff --git a/Content.Client/GameObjects/Components/Storage/BagOpenCloseVisualizer.cs b/Content.Client/GameObjects/Components/Storage/BagOpenCloseVisualizer.cs new file mode 100644 index 0000000000..035fe4e6ce --- /dev/null +++ b/Content.Client/GameObjects/Components/Storage/BagOpenCloseVisualizer.cs @@ -0,0 +1,68 @@ +#nullable enable + +using Content.Shared.GameObjects.Components; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Log; +using Robust.Shared.Utility; +using YamlDotNet.RepresentationModel; + +namespace Content.Client.GameObjects.Components.Storage +{ + [UsedImplicitly] + public class BagOpenCloseVisualizer : AppearanceVisualizer + { + private const string OpenIcon = "openIcon"; + private string? _openIcon; + + public override void LoadData(YamlMappingNode node) + { + base.LoadData(node); + + if (node.TryGetNode(OpenIcon, out var openIconNode)) + { + _openIcon = openIconNode.Value; + } + else + { + Logger.Warning("BagOpenCloseVisualizer is useless with no `openIcon`"); + } + } + + public override void InitializeEntity(IEntity entity) + { + base.InitializeEntity(entity); + + if (_openIcon != null && entity.TryGetComponent(out var spriteComponent)) + { + var rsiPath = spriteComponent.BaseRSI!.Path!; + spriteComponent.LayerMapReserveBlank(OpenIcon); + spriteComponent.LayerSetSprite(OpenIcon, new SpriteSpecifier.Rsi(rsiPath, _openIcon)); + spriteComponent.LayerSetVisible(OpenIcon, false); + } + } + + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + if (_openIcon != null + && component.Owner.TryGetComponent(out var spriteComponent)) + { + if (component.TryGetData(SharedBagOpenVisuals.BagState, out var bagState)) + { + switch (bagState) + { + case SharedBagState.Open: + spriteComponent.LayerSetVisible(OpenIcon, true); + break; + default: + spriteComponent.LayerSetVisible(OpenIcon, false); + break; + } + } + } + } + } +} diff --git a/Content.Client/GameObjects/Components/Storage/ClientStorageComponent.cs b/Content.Client/GameObjects/Components/Storage/ClientStorageComponent.cs index c00aee4f6f..fb5f9117fc 100644 --- a/Content.Client/GameObjects/Components/Storage/ClientStorageComponent.cs +++ b/Content.Client/GameObjects/Components/Storage/ClientStorageComponent.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Content.Client.Animations; using Content.Client.GameObjects.Components.Items; +using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.Components.Storage; using Content.Shared.Interfaces.GameObjects.Components; using Robust.Client.GameObjects; @@ -29,9 +30,18 @@ namespace Content.Client.GameObjects.Components.Storage private int StorageSizeUsed; private int StorageCapacityMax; private StorageWindow Window; + private SharedBagState _bagState; public override IReadOnlyList StoredEntities => _storedEntities; + public override void Initialize() + { + base.Initialize(); + + // Hide stackVisualizer on start + _bagState = SharedBagState.Close; + } + public override void OnAdd() { base.OnAdd(); @@ -69,12 +79,15 @@ namespace Content.Client.GameObjects.Components.Storage //Updates what we are storing for the UI case StorageHeldItemsMessage msg: HandleStorageMessage(msg); + ChangeStorageVisualization(_bagState); break; //Opens the UI case OpenStorageUIMessage _: + ChangeStorageVisualization(SharedBagState.Open); ToggleUI(); break; case CloseStorageUIMessage _: + ChangeStorageVisualization(SharedBagState.Close); CloseUI(); break; case AnimateInsertingEntitiesMessage msg: @@ -119,6 +132,7 @@ namespace Content.Client.GameObjects.Components.Storage private void ToggleUI() { if (Window.IsOpen) + Window.Close(); else Window.Open(); @@ -128,6 +142,16 @@ namespace Content.Client.GameObjects.Components.Storage { Window.Close(); } + + private void ChangeStorageVisualization(SharedBagState state) + { + _bagState = state; + if (Owner.TryGetComponent(out var appearanceComponent)) + { + appearanceComponent.SetData(SharedBagOpenVisuals.BagState, state); + appearanceComponent.SetData(StackVisuals.Hide, state == SharedBagState.Close); + } + } /// /// Function for clicking one of the stored entity buttons in the UI, tells server to remove that entity diff --git a/Content.Client/IgnoredComponents.cs b/Content.Client/IgnoredComponents.cs index 72e69a76b0..2f97a9199a 100644 --- a/Content.Client/IgnoredComponents.cs +++ b/Content.Client/IgnoredComponents.cs @@ -164,6 +164,7 @@ namespace Content.Client "Firelock", "AtmosPlaque", "Spillable", + "StorageCounter", "SpaceVillainArcade", "Flammable", "CreamPie", diff --git a/Content.Server/GameObjects/Components/Items/Storage/StorageCounterComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/StorageCounterComponent.cs new file mode 100644 index 0000000000..6454ff3147 --- /dev/null +++ b/Content.Server/GameObjects/Components/Items/Storage/StorageCounterComponent.cs @@ -0,0 +1,86 @@ +#nullable enable + +using System.Collections.Generic; +using Content.Shared.GameObjects.Components; +using Content.Shared.GameObjects.Components.Tag; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Log; +using Robust.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Items.Storage +{ + /// + /// Storage that spawns and counts a single item. + /// Usually used for things like matchboxes, cigarette packs, + /// cigar cases etc. + /// + /// + /// - type: StorageCounter + /// amount: 6 # Note: this field can be omitted + /// countTag: Cigarette # Note: field doesn't point to entity Id, but its tag + /// + [RegisterComponent] + public class StorageCounterComponent : Component + { + private string? _countTag; + private int? _maxAmount; + + /// + /// Single item storage component usually have an attached StackedVisualizer. + /// + [ComponentDependency] private readonly AppearanceComponent? _appearanceComponent = default; + + public override string Name => "StorageCounter"; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _countTag, "countTag", null); + if (_countTag == null) + { + Logger.Warning("StorageCounterComponent without a `countTag` is useless"); + } + serializer.DataField(ref _maxAmount, "amount", null); + } + + public override void HandleMessage(ComponentMessage message, IComponent? component) + { + base.HandleMessage(message, component); + + if (_appearanceComponent != null) + { + switch (message) + { + case ContainerContentsModifiedMessage msg: + var actual = Count(msg.Container.ContainedEntities); + _appearanceComponent.SetData(StackVisuals.Actual, actual); + if (_maxAmount != null) + { + _appearanceComponent.SetData(StackVisuals.MaxCount, _maxAmount); + } + + break; + } + } + } + + private int Count(IReadOnlyList containerContainedEntities) + { + var count = 0; + if (_countTag != null) + { + foreach (var entity in containerContainedEntities) + { + if (entity.HasTag(_countTag)) + { + count++; + } + } + } + + return count; + } + } +} diff --git a/Content.Shared/GameObjects/Components/SharedBagOpenVisuals.cs b/Content.Shared/GameObjects/Components/SharedBagOpenVisuals.cs new file mode 100644 index 0000000000..3b2178900a --- /dev/null +++ b/Content.Shared/GameObjects/Components/SharedBagOpenVisuals.cs @@ -0,0 +1,19 @@ +#nullable enable +using System; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components +{ + [Serializable, NetSerializable] + public enum SharedBagOpenVisuals : byte + { + BagState, + } + + [Serializable, NetSerializable] + public enum SharedBagState : byte + { + Open, + Close, + } +} diff --git a/Content.Shared/GameObjects/Components/StackVisuals.cs b/Content.Shared/GameObjects/Components/StackVisuals.cs new file mode 100644 index 0000000000..30d9d3ae44 --- /dev/null +++ b/Content.Shared/GameObjects/Components/StackVisuals.cs @@ -0,0 +1,21 @@ +#nullable enable +using System; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components +{ + [Serializable, NetSerializable] + public enum StackVisuals : byte + { + /// + /// The amount of elements in the stack + /// + Actual, + /// + /// The total amount of elements in the stack. If unspecified, the visualizer assumes + /// its + /// + MaxCount, + Hide + } +} diff --git a/Content.Shared/Utility/ContentHelpers.cs b/Content.Shared/Utility/ContentHelpers.cs index 69132a4082..772d549288 100644 --- a/Content.Shared/Utility/ContentHelpers.cs +++ b/Content.Shared/Utility/ContentHelpers.cs @@ -25,14 +25,17 @@ namespace Content.Shared.Utility { throw new ArgumentException("Levels must be greater than 0.", nameof(levels)); } + if (actual >= max) { return levels - 1; } + if (actual <= 0) { return 0; } + var toOne = actual / max; double threshold; if (levels % 2 == 0) @@ -49,11 +52,11 @@ namespace Content.Shared.Utility var preround = toOne * (levels - 1); if (toOne <= threshold || levels <= 2) { - return (int)Math.Ceiling(preround); + return (int) Math.Ceiling(preround); } else { - return (int)Math.Floor(preround); + return (int) Math.Floor(preround); } } @@ -81,28 +84,18 @@ namespace Content.Shared.Utility { throw new ArgumentException("Levels must be greater than 1.", nameof(levels)); } + if (actual >= max) { return levels; } + if (actual <= 0) { return 0; } - double step = max / levels; - int nearest = 0; - double nearestDiff = actual; - for (var i = 1; i <= levels; i++) - { - var diff = Math.Abs(actual - i * step); - if (diff < nearestDiff) - { - nearestDiff = diff; - nearest = i; - } - } - return nearest; + return (int) Math.Round(actual / max * levels, MidpointRounding.AwayFromZero); } } } diff --git a/Content.Tests/Shared/Utility/ContentHelpers_Test.cs b/Content.Tests/Shared/Utility/ContentHelpers_Test.cs index cf75f026df..186fde1db4 100644 --- a/Content.Tests/Shared/Utility/ContentHelpers_Test.cs +++ b/Content.Tests/Shared/Utility/ContentHelpers_Test.cs @@ -9,41 +9,78 @@ namespace Content.Tests.Shared.Utility [TestOf(typeof(ContentHelpers))] public class ContentHelpers_Test { + public static readonly IEnumerable<(double val, double max, int levels, int expected)> TestData = + new (double, double, int, int)[] + { + // Testing odd level counts. These are easy. + (-1, 10, 5, 0), + (0, 10, 5, 0), + (0.01f, 10, 5, 1), + (1, 10, 5, 1), + (2, 10, 5, 1), + (2.5f, 10, 5, 1), + (2.51f, 10, 5, 2), + (3, 10, 5, 2), + (4, 10, 5, 2), + (5, 10, 5, 2), + (6, 10, 5, 2), + (7, 10, 5, 2), + (7.49f, 10, 5, 2), + (7.5f, 10, 5, 3), + (8, 10, 5, 3), + (9, 10, 5, 3), + (10, 10, 5, 4), + (11, 10, 5, 4), - public static readonly IEnumerable<(double val, double max, int levels, int expected)> TestData = new(double, double, int, int)[] - { - // Testing odd level counts. These are easy. - (-1, 10, 5, 0), - ( 0, 10, 5, 0), - ( 0.01f, 10, 5, 1), - ( 1, 10, 5, 1), - ( 2, 10, 5, 1), - ( 2.5f, 10, 5, 1), - ( 2.51f, 10, 5, 2), - ( 3, 10, 5, 2), - ( 4, 10, 5, 2), - ( 5, 10, 5, 2), - ( 6, 10, 5, 2), - ( 7, 10, 5, 2), - ( 7.49f, 10, 5, 2), - ( 7.5f, 10, 5, 3), - ( 8, 10, 5, 3), - ( 9, 10, 5, 3), - (10, 10, 5, 4), - (11, 10, 5, 4), + // Even level counts though.. + (1, 10, 6, 1), + (2, 10, 6, 1), + (3, 10, 6, 2), + (4, 10, 6, 2), + (5, 10, 6, 2), + (6, 10, 6, 3), + (7, 10, 6, 3), + (8, 10, 6, 4), + (9, 10, 6, 4), + (10, 10, 6, 5), + }; - // Even level counts though.. - ( 1, 10, 6, 1), - ( 2, 10, 6, 1), - ( 3, 10, 6, 2), - ( 4, 10, 6, 2), - ( 5, 10, 6, 2), - ( 6, 10, 6, 3), - ( 7, 10, 6, 3), - ( 8, 10, 6, 4), - ( 9, 10, 6, 4), - (10, 10, 6, 5), - }; + public static readonly IEnumerable<(double val, double max, int levels, int expected)> TestNear = + new (double, double, int, int)[] + { + // Testing odd counts + (0, 5, 2, 0), + (1, 5, 2, 0), + (2, 5, 2, 1), + (3, 5, 2, 1), + (4, 5, 2, 2), + (5, 5, 2, 2), + + // Testing even counts + (0, 6, 5, 0), + (1, 6, 5, 1), + (2, 6, 5, 2), + (3, 6, 5, 3), + (4, 6, 5, 3), + (5, 6, 5, 4), + (6, 6, 5, 5), + + // Testing transparency disable use case + (0, 6, 6, 0), + (1, 6, 6, 1), + (2, 6, 6, 2), + (3, 6, 6, 3), + (4, 6, 6, 4), + (5, 6, 6, 5), + (6, 6, 6, 6), + + // Testing edge cases + (0.1, 6, 5, 0), + (-32, 6, 5, 0), + (2.4, 6, 5, 2), + (2.5, 6, 5, 2), + (320, 6, 5, 5), + }; [Parallelizable] [Test] @@ -52,5 +89,13 @@ namespace Content.Tests.Shared.Utility (double val, double max, int levels, int expected) = data; Assert.That(ContentHelpers.RoundToLevels(val, max, levels), Is.EqualTo(expected)); } + + [Parallelizable] + [Test] + public void TestNearest([ValueSource(nameof(TestNear))] (double val, double max, int size, int expected) data) + { + (double val, double max, int size, int expected) = data; + Assert.That(ContentHelpers.RoundToNearestLevels(val, max, size), Is.EqualTo(expected)); + } } } diff --git a/Resources/Prototypes/Entities/Objects/Consumable/fancy.yml b/Resources/Prototypes/Entities/Objects/Consumable/fancy.yml index fcf5d0dc31..be305e3ff9 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/fancy.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/fancy.yml @@ -1,4 +1,6 @@ -- type: entity +- type: Tag + id: Cigarette +- type: entity name: "Base Cigarette" id: BaseCigarette parent: BaseItem @@ -13,6 +15,9 @@ Slots: [ mask ] HeldPrefix: unlit size: 1 + - type: Tag + tags: + - Cigarette - type: Smoking duration: 30 - type: Appearance @@ -33,10 +38,25 @@ components: - type: Sprite sprite: Objects/Consumable/Fancy/cigarettes.rsi + netsync: false layers: - state: cig - type: StorageFill contents: - name: Cigarette amount: 6 - + - type: StorageCounter + countTag: Cigarette + - type: Appearance + visuals: + - type: BagOpenCloseVisualizer + openIcon: cig_open + - type: StackVisualizer + composite: true + stackLayers: + - cigarette_1 + - cigarette_2 + - cigarette_3 + - cigarette_4 + - cigarette_5 + - cigarette_6 diff --git a/Resources/Prototypes/Entities/Objects/Misc/material_stacks.yml b/Resources/Prototypes/Entities/Objects/Misc/material_stacks.yml index c4aa4a1465..68dd39ce8b 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/material_stacks.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/material_stacks.yml @@ -4,9 +4,9 @@ abstract: true parent: BaseItem components: - - type: Stack - - type: Material - - type: ItemStatus + - type: Stack + - type: Material + - type: ItemStatus - type: entity name: steel sheet @@ -14,21 +14,21 @@ parent: MaterialStack suffix: Full components: - - type: Material - materials: - - key: enum.MaterialKeys.Stack - mat: steel - - type: Stack - stacktype: enum.StackType.Metal - - type: Sprite - sprite: Objects/Materials/sheets.rsi - state: metal - - type: Item - sprite: Objects/Materials/sheets.rsi - HeldPrefix: metal - - type: FloorTile - outputs: - - underplating + - type: Material + materials: + - key: enum.MaterialKeys.Stack + mat: steel + - type: Stack + stacktype: enum.StackType.Metal + - type: Sprite + sprite: Objects/Materials/sheets.rsi + state: metal + - type: Item + sprite: Objects/Materials/sheets.rsi + HeldPrefix: metal + - type: FloorTile + outputs: + - underplating - type: entity id: SteelSheet1 @@ -36,9 +36,9 @@ parent: MetalStack suffix: 1 components: - - type: Stack - stacktype: enum.StackType.Metal - count: 1 + - type: Stack + stacktype: enum.StackType.Metal + count: 1 - type: entity name: glass sheet @@ -46,18 +46,18 @@ parent: MaterialStack suffix: Full components: - - type: Material - materials: - - key: enum.MaterialKeys.Stack - mat: glass - - type: Stack - stacktype: enum.StackType.Glass - - type: Sprite - sprite: Objects/Materials/sheets.rsi - state: glass - - type: Item - sprite: Objects/Materials/sheets.rsi - HeldPrefix: glass + - type: Material + materials: + - key: enum.MaterialKeys.Stack + mat: glass + - type: Stack + stacktype: enum.StackType.Glass + - type: Sprite + sprite: Objects/Materials/sheets.rsi + state: glass + - type: Item + sprite: Objects/Materials/sheets.rsi + HeldPrefix: glass - type: entity id: GlassSheet1 @@ -65,9 +65,9 @@ parent: GlassStack suffix: 1 components: - - type: Stack - stacktype: enum.StackType.Glass - count: 1 + - type: Stack + stacktype: enum.StackType.Glass + count: 1 - type: entity name: Reinforced Glass @@ -94,9 +94,9 @@ parent: rglass suffix: 1 components: - - type: Stack - StackType: enum.StackType.ReinforcedGlass - count: 1 + - type: Stack + StackType: enum.StackType.ReinforcedGlass + count: 1 - type: entity @@ -134,15 +134,23 @@ parent: MaterialStack suffix: Full components: - - type: Material - materials: - - key: enum.MaterialKeys.Stack - mat: gold - - type: Stack - stacktype: enum.StackType.Gold - - type: Sprite - sprite: Objects/Materials/materials.rsi - state: goldbar_30 + - type: Material + materials: + - key: enum.MaterialKeys.Stack + mat: gold + - type: Stack + stacktype: enum.StackType.Gold + - type: Sprite + sprite: Objects/Materials/materials.rsi + state: goldbar_30 + netsync: false + - type: Appearance + visuals: + - type: StackVisualizer + stackLayers: + - goldbar_10 + - goldbar_20 + - goldbar_30 - type: entity id: GoldStack1 @@ -150,11 +158,11 @@ parent: GoldStack suffix: 1 components: - - type: Sprite - sprite: Objects/Materials/materials.rsi - state: goldbar_10 - - type: Stack - count: 1 + - type: Sprite + sprite: Objects/Materials/materials.rsi + state: goldbar_10 + - type: Stack + count: 1 - type: entity name: plasma ore diff --git a/Resources/Prototypes/Entities/Objects/Power/cable_coils.yml b/Resources/Prototypes/Entities/Objects/Power/cable_coils.yml index 7ad05e8511..c7a9a2f11c 100644 --- a/Resources/Prototypes/Entities/Objects/Power/cable_coils.yml +++ b/Resources/Prototypes/Entities/Objects/Power/cable_coils.yml @@ -1,4 +1,3 @@ - # If you're looking at the rsi for this file, you'll probably be confused why # I didn't just use an alpha for most of this stuff. Well icons don't have the # ability to have applied colors yet in GUIs. -Swept @@ -10,14 +9,16 @@ name: cable stack suffix: Full components: - - type: Stack - stacktype: enum.StackType.Cable - - type: Sprite - sprite: Objects/Tools/cables.rsi - - type: Item - sprite: Objects/Tools/cables.rsi - - type: WirePlacer - - type: Clickable + - type: Stack + stacktype: enum.StackType.Cable + - type: Sprite + sprite: Objects/Tools/cables.rsi + netsync: false + - type: Item + sprite: Objects/Tools/cables.rsi + - type: WirePlacer + - type: Clickable + - type: entity id: HVWireStack @@ -25,16 +26,23 @@ name: HV cable coil suffix: Full components: - - type: Stack - stacktype: enum.StackType.HVCable - - type: Sprite - state: coilhv-30 - - type: Item - size: 10 - HeldPrefix: coilhv - - type: WirePlacer - wirePrototypeID: HVWire - blockingWireType: HighVoltage + - type: Stack + stacktype: enum.StackType.HVCable + - type: Sprite + state: coilhv-30 + - type: Item + size: 10 + HeldPrefix: coilhv + - type: WirePlacer + wirePrototypeID: HVWire + blockingWireType: HighVoltage + - type: Appearance + visuals: + - type: StackVisualizer + stackLayers: + - coilhv-10 + - coilhv-20 + - coilhv-30 - type: entity parent: HVWireStack @@ -42,7 +50,7 @@ suffix: 1 components: - type: Sprite - state: coilhv-10 + state: coilhv-10 - type: Item size: 3 - type: Stack @@ -55,14 +63,21 @@ description: Low-Voltage stack of wires for connecting APCs to machines and other purposes. suffix: Full components: - - type: Sprite - state: coillv-30 - - type: Item - size: 10 - HeldPrefix: coillv - - type: WirePlacer - wirePrototypeID: ApcExtensionCable - blockingWireType: Apc + - type: Sprite + state: coillv-30 + - type: Item + size: 10 + HeldPrefix: coillv + - type: WirePlacer + wirePrototypeID: ApcExtensionCable + blockingWireType: Apc + - type: Appearance + visuals: + - type: StackVisualizer + stackLayers: + - coillv-10 + - coillv-20 + - coillv-30 - type: entity parent: ApcExtensionCableStack @@ -92,6 +107,13 @@ - type: WirePlacer wirePrototypeID: MVWire blockingWireType: MediumVoltage + - type: Appearance + visuals: + - type: StackVisualizer + stackLayers: + - coilmv-10 + - coilmv-20 + - coilmv-30 - type: entity parent: MVWireStack diff --git a/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_1.png b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_1.png new file mode 100644 index 0000000000..93f34c641c Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_1.png differ diff --git a/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_2.png b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_2.png new file mode 100644 index 0000000000..ec90405507 Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_2.png differ diff --git a/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_3.png b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_3.png new file mode 100644 index 0000000000..7e3eb0c1b9 Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_3.png differ diff --git a/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_4.png b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_4.png new file mode 100644 index 0000000000..bf9361b9b5 Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_4.png differ diff --git a/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_5.png b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_5.png new file mode 100644 index 0000000000..6d0a066c52 Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_5.png differ diff --git a/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_6.png b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_6.png new file mode 100644 index 0000000000..53048fed17 Binary files /dev/null and b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/cigarette_6.png differ diff --git a/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/meta.json b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/meta.json index 7837a45f44..a33f00be16 100644 --- a/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/meta.json +++ b/Resources/Textures/Objects/Consumable/Fancy/cigarettes.rsi/meta.json @@ -15,6 +15,24 @@ }, { "name": "cig_open" + }, + { + "name": "cigarette_1" + }, + { + "name": "cigarette_2" + }, + { + "name": "cigarette_3" + }, + { + "name": "cigarette_4" + }, + { + "name": "cigarette_5" + }, + { + "name": "cigarette_6" } ] }