Stacked sprite visualizer (#3096)

* Add Stack Visualizer

* Add cigarette pack resources

Adds transparent layers for visualizing cigarettes

* Add Bag Open/Close Visualizer

So storage opened in inventory can have different icons when opened
or closed.

* Create a component that only enumerates single item

Used for creating stuff like matchbox, or cigarettes. As a bonus.
It will only update stack visualizer for that particullar item.

* Refactoring stuff

* Fix other usage of stack in Resources

* Add docs

* Apply suggestions from code review

Apply metalgearsloth suggestions

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>

* Applied suggestions from metalgearsloth

* Changed SingleItemStorageComponent to StorageCounterComponent

Difference. New component doesn't spawn items, merely counts them.

* Refactored StackVisualizer

* Fix breakage with master

* Update Resources/Prototypes/Entities/Objects/Consumable/fancy.yml

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>

* Update with MGS suggestions

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
Ygg01
2021-02-17 14:02:36 +01:00
committed by GitHub
parent 83f102ea75
commit 55d65889ae
20 changed files with 655 additions and 134 deletions

View File

@@ -1,6 +1,9 @@
using Content.Client.UserInterface.Stylesheets; #nullable enable
using Content.Client.UserInterface.Stylesheets;
using Content.Client.Utility; using Content.Client.Utility;
using Content.Shared.GameObjects.Components; using Content.Shared.GameObjects.Components;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
@@ -15,6 +18,7 @@ namespace Content.Client.GameObjects.Components
public class StackComponent : SharedStackComponent, IItemStatus public class StackComponent : SharedStackComponent, IItemStatus
{ {
[ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded; [ViewVariables(VVAccess.ReadWrite)] private bool _uiUpdateNeeded;
[ComponentDependency] private readonly AppearanceComponent? _appearanceComponent = default!;
public Control MakeControl() => new StatusControl(this); public Control MakeControl() => new StatusControl(this);
@@ -23,12 +27,30 @@ namespace Content.Client.GameObjects.Components
get => base.Count; get => base.Count;
set set
{ {
var valueChanged = value != Count;
base.Count = value; base.Count = value;
if (valueChanged)
{
_appearanceComponent?.SetData(StackVisuals.Actual, Count);
}
_uiUpdateNeeded = true; _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 sealed class StatusControl : Control
{ {
private readonly StackComponent _parent; private readonly StackComponent _parent;
@@ -58,4 +80,4 @@ namespace Content.Client.GameObjects.Components
} }
} }
} }
} }

View File

@@ -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
{
/// <summary>
/// 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 <c>_spriteLayers</c> 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.
///
/// <example>
/// <para>To define a Stack Visualizer prototype insert the following
/// snippet (you can skip Appearance if already defined)
/// </para>
/// <code>
/// - type: Appearance
/// visuals:
/// - type: StackVisualizer
/// stackLayers:
/// - goldbar_10
/// - goldbar_20
/// - goldbar_30
/// </code>
/// </example>
/// <example>
/// <para>Defining a stack visualizer with composable transparent layers</para>
/// <code>
/// - type: StackVisualizer
/// composite: true
/// stackLayers:
/// - cigarette_1
/// - cigarette_2
/// - cigarette_3
/// - cigarette_4
/// - cigarette_5
/// - cigarette_6
/// </code>
/// </example>
/// <seealso cref="_spriteLayers"/>
/// </summary>
[UsedImplicitly]
public class StackVisualizer : AppearanceVisualizer
{
/// <summary>
/// Default IconLayer stack.
/// </summary>
private const int IconLayer = 0;
/// <summary>
/// Sprite layers used in stack visualizer. Sprites first in layer correspond to lower stack states
/// e.g. <code>_spriteLayers[0]</code> is lower stack level than <code>_spriteLayers[1]</code>.
/// </summary>
private readonly List<string> _spriteLayers = new();
/// <summary>
/// Determines if the visualizer uses composite or non-composite layers for icons. Defaults to false.
///
/// <list type="bullet">
/// <item>
/// <description>false: they are opaque and mutually exclusive (e.g. sprites in a wire coil). <b>Default value</b></description>
/// </item>
/// <item>
/// <description>true: they are transparent and thus layered one over another in ascending order first</description>
/// </item>
/// </list>
///
/// </summary>
private bool _isComposite;
public override void LoadData(YamlMappingNode mapping)
{
base.LoadData(mapping);
if (mapping.TryGetNode<YamlSequenceNode>("stackLayers", out var spriteSequenceNode))
{
foreach (var yamlNode in spriteSequenceNode)
{
_spriteLayers.Add(((YamlScalarNode) yamlNode).Value!);
}
}
if (mapping.TryGetNode<YamlScalarNode>("composite", out var transparent))
{
_isComposite = transparent.AsBool();
}
}
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
if (_isComposite
&& _spriteLayers.Count > 0
&& entity.TryGetComponent<ISpriteComponent>(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<ISpriteComponent>(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<int>(StackVisuals.Actual, out var actual)) return;
if (!component.TryGetData<int>(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<bool>(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<int>(StackVisuals.Actual, out var actual)) return;
if (!component.TryGetData<int>(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);
}
}
}
}

View File

@@ -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<YamlScalarNode>(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<SpriteComponent>(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<SpriteComponent>(out var spriteComponent))
{
if (component.TryGetData<SharedBagState>(SharedBagOpenVisuals.BagState, out var bagState))
{
switch (bagState)
{
case SharedBagState.Open:
spriteComponent.LayerSetVisible(OpenIcon, true);
break;
default:
spriteComponent.LayerSetVisible(OpenIcon, false);
break;
}
}
}
}
}
}

View File

@@ -1,8 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Client.Animations; using Content.Client.Animations;
using Content.Client.GameObjects.Components.Items; using Content.Client.GameObjects.Components.Items;
using Content.Shared.GameObjects.Components;
using Content.Shared.GameObjects.Components.Storage; using Content.Shared.GameObjects.Components.Storage;
using Content.Shared.Interfaces.GameObjects.Components; using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
@@ -29,9 +30,18 @@ namespace Content.Client.GameObjects.Components.Storage
private int StorageSizeUsed; private int StorageSizeUsed;
private int StorageCapacityMax; private int StorageCapacityMax;
private StorageWindow Window; private StorageWindow Window;
private SharedBagState _bagState;
public override IReadOnlyList<IEntity> StoredEntities => _storedEntities; public override IReadOnlyList<IEntity> StoredEntities => _storedEntities;
public override void Initialize()
{
base.Initialize();
// Hide stackVisualizer on start
_bagState = SharedBagState.Close;
}
public override void OnAdd() public override void OnAdd()
{ {
base.OnAdd(); base.OnAdd();
@@ -69,12 +79,15 @@ namespace Content.Client.GameObjects.Components.Storage
//Updates what we are storing for the UI //Updates what we are storing for the UI
case StorageHeldItemsMessage msg: case StorageHeldItemsMessage msg:
HandleStorageMessage(msg); HandleStorageMessage(msg);
ChangeStorageVisualization(_bagState);
break; break;
//Opens the UI //Opens the UI
case OpenStorageUIMessage _: case OpenStorageUIMessage _:
ChangeStorageVisualization(SharedBagState.Open);
ToggleUI(); ToggleUI();
break; break;
case CloseStorageUIMessage _: case CloseStorageUIMessage _:
ChangeStorageVisualization(SharedBagState.Close);
CloseUI(); CloseUI();
break; break;
case AnimateInsertingEntitiesMessage msg: case AnimateInsertingEntitiesMessage msg:
@@ -119,6 +132,7 @@ namespace Content.Client.GameObjects.Components.Storage
private void ToggleUI() private void ToggleUI()
{ {
if (Window.IsOpen) if (Window.IsOpen)
Window.Close(); Window.Close();
else else
Window.Open(); Window.Open();
@@ -128,6 +142,16 @@ namespace Content.Client.GameObjects.Components.Storage
{ {
Window.Close(); Window.Close();
} }
private void ChangeStorageVisualization(SharedBagState state)
{
_bagState = state;
if (Owner.TryGetComponent<AppearanceComponent>(out var appearanceComponent))
{
appearanceComponent.SetData(SharedBagOpenVisuals.BagState, state);
appearanceComponent.SetData(StackVisuals.Hide, state == SharedBagState.Close);
}
}
/// <summary> /// <summary>
/// Function for clicking one of the stored entity buttons in the UI, tells server to remove that entity /// Function for clicking one of the stored entity buttons in the UI, tells server to remove that entity

View File

@@ -164,6 +164,7 @@ namespace Content.Client
"Firelock", "Firelock",
"AtmosPlaque", "AtmosPlaque",
"Spillable", "Spillable",
"StorageCounter",
"SpaceVillainArcade", "SpaceVillainArcade",
"Flammable", "Flammable",
"CreamPie", "CreamPie",

View File

@@ -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
{
/// <summary>
/// Storage that spawns and counts a single item.
/// Usually used for things like matchboxes, cigarette packs,
/// cigar cases etc.
/// </summary>
/// <code>
/// - type: StorageCounter
/// amount: 6 # Note: this field can be omitted
/// countTag: Cigarette # Note: field doesn't point to entity Id, but its tag
/// </code>
[RegisterComponent]
public class StorageCounterComponent : Component
{
private string? _countTag;
private int? _maxAmount;
/// <summary>
/// Single item storage component usually have an attached StackedVisualizer.
/// </summary>
[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<IEntity> containerContainedEntities)
{
var count = 0;
if (_countTag != null)
{
foreach (var entity in containerContainedEntities)
{
if (entity.HasTag(_countTag))
{
count++;
}
}
}
return count;
}
}
}

View File

@@ -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,
}
}

View File

@@ -0,0 +1,21 @@
#nullable enable
using System;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components
{
[Serializable, NetSerializable]
public enum StackVisuals : byte
{
/// <summary>
/// The amount of elements in the stack
/// </summary>
Actual,
/// <summary>
/// The total amount of elements in the stack. If unspecified, the visualizer assumes
/// its
/// </summary>
MaxCount,
Hide
}
}

View File

@@ -25,14 +25,17 @@ namespace Content.Shared.Utility
{ {
throw new ArgumentException("Levels must be greater than 0.", nameof(levels)); throw new ArgumentException("Levels must be greater than 0.", nameof(levels));
} }
if (actual >= max) if (actual >= max)
{ {
return levels - 1; return levels - 1;
} }
if (actual <= 0) if (actual <= 0)
{ {
return 0; return 0;
} }
var toOne = actual / max; var toOne = actual / max;
double threshold; double threshold;
if (levels % 2 == 0) if (levels % 2 == 0)
@@ -49,11 +52,11 @@ namespace Content.Shared.Utility
var preround = toOne * (levels - 1); var preround = toOne * (levels - 1);
if (toOne <= threshold || levels <= 2) if (toOne <= threshold || levels <= 2)
{ {
return (int)Math.Ceiling(preround); return (int) Math.Ceiling(preround);
} }
else 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)); throw new ArgumentException("Levels must be greater than 1.", nameof(levels));
} }
if (actual >= max) if (actual >= max)
{ {
return levels; return levels;
} }
if (actual <= 0) if (actual <= 0)
{ {
return 0; return 0;
} }
double step = max / levels;
int nearest = 0; return (int) Math.Round(actual / max * levels, MidpointRounding.AwayFromZero);
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;
} }
} }
} }

View File

@@ -9,41 +9,78 @@ namespace Content.Tests.Shared.Utility
[TestOf(typeof(ContentHelpers))] [TestOf(typeof(ContentHelpers))]
public class ContentHelpers_Test 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)[] // Even level counts though..
{ (1, 10, 6, 1),
// Testing odd level counts. These are easy. (2, 10, 6, 1),
(-1, 10, 5, 0), (3, 10, 6, 2),
( 0, 10, 5, 0), (4, 10, 6, 2),
( 0.01f, 10, 5, 1), (5, 10, 6, 2),
( 1, 10, 5, 1), (6, 10, 6, 3),
( 2, 10, 5, 1), (7, 10, 6, 3),
( 2.5f, 10, 5, 1), (8, 10, 6, 4),
( 2.51f, 10, 5, 2), (9, 10, 6, 4),
( 3, 10, 5, 2), (10, 10, 6, 5),
( 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.. public static readonly IEnumerable<(double val, double max, int levels, int expected)> TestNear =
( 1, 10, 6, 1), new (double, double, int, int)[]
( 2, 10, 6, 1), {
( 3, 10, 6, 2), // Testing odd counts
( 4, 10, 6, 2), (0, 5, 2, 0),
( 5, 10, 6, 2), (1, 5, 2, 0),
( 6, 10, 6, 3), (2, 5, 2, 1),
( 7, 10, 6, 3), (3, 5, 2, 1),
( 8, 10, 6, 4), (4, 5, 2, 2),
( 9, 10, 6, 4), (5, 5, 2, 2),
(10, 10, 6, 5),
}; // 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] [Parallelizable]
[Test] [Test]
@@ -52,5 +89,13 @@ namespace Content.Tests.Shared.Utility
(double val, double max, int levels, int expected) = data; (double val, double max, int levels, int expected) = data;
Assert.That(ContentHelpers.RoundToLevels(val, max, levels), Is.EqualTo(expected)); 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));
}
} }
} }

View File

@@ -1,4 +1,6 @@
- type: entity - type: Tag
id: Cigarette
- type: entity
name: "Base Cigarette" name: "Base Cigarette"
id: BaseCigarette id: BaseCigarette
parent: BaseItem parent: BaseItem
@@ -13,6 +15,9 @@
Slots: [ mask ] Slots: [ mask ]
HeldPrefix: unlit HeldPrefix: unlit
size: 1 size: 1
- type: Tag
tags:
- Cigarette
- type: Smoking - type: Smoking
duration: 30 duration: 30
- type: Appearance - type: Appearance
@@ -33,10 +38,25 @@
components: components:
- type: Sprite - type: Sprite
sprite: Objects/Consumable/Fancy/cigarettes.rsi sprite: Objects/Consumable/Fancy/cigarettes.rsi
netsync: false
layers: layers:
- state: cig - state: cig
- type: StorageFill - type: StorageFill
contents: contents:
- name: Cigarette - name: Cigarette
amount: 6 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

View File

@@ -4,9 +4,9 @@
abstract: true abstract: true
parent: BaseItem parent: BaseItem
components: components:
- type: Stack - type: Stack
- type: Material - type: Material
- type: ItemStatus - type: ItemStatus
- type: entity - type: entity
name: steel sheet name: steel sheet
@@ -14,21 +14,21 @@
parent: MaterialStack parent: MaterialStack
suffix: Full suffix: Full
components: components:
- type: Material - type: Material
materials: materials:
- key: enum.MaterialKeys.Stack - key: enum.MaterialKeys.Stack
mat: steel mat: steel
- type: Stack - type: Stack
stacktype: enum.StackType.Metal stacktype: enum.StackType.Metal
- type: Sprite - type: Sprite
sprite: Objects/Materials/sheets.rsi sprite: Objects/Materials/sheets.rsi
state: metal state: metal
- type: Item - type: Item
sprite: Objects/Materials/sheets.rsi sprite: Objects/Materials/sheets.rsi
HeldPrefix: metal HeldPrefix: metal
- type: FloorTile - type: FloorTile
outputs: outputs:
- underplating - underplating
- type: entity - type: entity
id: SteelSheet1 id: SteelSheet1
@@ -36,9 +36,9 @@
parent: MetalStack parent: MetalStack
suffix: 1 suffix: 1
components: components:
- type: Stack - type: Stack
stacktype: enum.StackType.Metal stacktype: enum.StackType.Metal
count: 1 count: 1
- type: entity - type: entity
name: glass sheet name: glass sheet
@@ -46,18 +46,18 @@
parent: MaterialStack parent: MaterialStack
suffix: Full suffix: Full
components: components:
- type: Material - type: Material
materials: materials:
- key: enum.MaterialKeys.Stack - key: enum.MaterialKeys.Stack
mat: glass mat: glass
- type: Stack - type: Stack
stacktype: enum.StackType.Glass stacktype: enum.StackType.Glass
- type: Sprite - type: Sprite
sprite: Objects/Materials/sheets.rsi sprite: Objects/Materials/sheets.rsi
state: glass state: glass
- type: Item - type: Item
sprite: Objects/Materials/sheets.rsi sprite: Objects/Materials/sheets.rsi
HeldPrefix: glass HeldPrefix: glass
- type: entity - type: entity
id: GlassSheet1 id: GlassSheet1
@@ -65,9 +65,9 @@
parent: GlassStack parent: GlassStack
suffix: 1 suffix: 1
components: components:
- type: Stack - type: Stack
stacktype: enum.StackType.Glass stacktype: enum.StackType.Glass
count: 1 count: 1
- type: entity - type: entity
name: Reinforced Glass name: Reinforced Glass
@@ -94,9 +94,9 @@
parent: rglass parent: rglass
suffix: 1 suffix: 1
components: components:
- type: Stack - type: Stack
StackType: enum.StackType.ReinforcedGlass StackType: enum.StackType.ReinforcedGlass
count: 1 count: 1
- type: entity - type: entity
@@ -134,15 +134,23 @@
parent: MaterialStack parent: MaterialStack
suffix: Full suffix: Full
components: components:
- type: Material - type: Material
materials: materials:
- key: enum.MaterialKeys.Stack - key: enum.MaterialKeys.Stack
mat: gold mat: gold
- type: Stack - type: Stack
stacktype: enum.StackType.Gold stacktype: enum.StackType.Gold
- type: Sprite - type: Sprite
sprite: Objects/Materials/materials.rsi sprite: Objects/Materials/materials.rsi
state: goldbar_30 state: goldbar_30
netsync: false
- type: Appearance
visuals:
- type: StackVisualizer
stackLayers:
- goldbar_10
- goldbar_20
- goldbar_30
- type: entity - type: entity
id: GoldStack1 id: GoldStack1
@@ -150,11 +158,11 @@
parent: GoldStack parent: GoldStack
suffix: 1 suffix: 1
components: components:
- type: Sprite - type: Sprite
sprite: Objects/Materials/materials.rsi sprite: Objects/Materials/materials.rsi
state: goldbar_10 state: goldbar_10
- type: Stack - type: Stack
count: 1 count: 1
- type: entity - type: entity
name: plasma ore name: plasma ore

View File

@@ -1,4 +1,3 @@
# If you're looking at the rsi for this file, you'll probably be confused why # 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 # 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 # ability to have applied colors yet in GUIs. -Swept
@@ -10,14 +9,16 @@
name: cable stack name: cable stack
suffix: Full suffix: Full
components: components:
- type: Stack - type: Stack
stacktype: enum.StackType.Cable stacktype: enum.StackType.Cable
- type: Sprite - type: Sprite
sprite: Objects/Tools/cables.rsi sprite: Objects/Tools/cables.rsi
- type: Item netsync: false
sprite: Objects/Tools/cables.rsi - type: Item
- type: WirePlacer sprite: Objects/Tools/cables.rsi
- type: Clickable - type: WirePlacer
- type: Clickable
- type: entity - type: entity
id: HVWireStack id: HVWireStack
@@ -25,16 +26,23 @@
name: HV cable coil name: HV cable coil
suffix: Full suffix: Full
components: components:
- type: Stack - type: Stack
stacktype: enum.StackType.HVCable stacktype: enum.StackType.HVCable
- type: Sprite - type: Sprite
state: coilhv-30 state: coilhv-30
- type: Item - type: Item
size: 10 size: 10
HeldPrefix: coilhv HeldPrefix: coilhv
- type: WirePlacer - type: WirePlacer
wirePrototypeID: HVWire wirePrototypeID: HVWire
blockingWireType: HighVoltage blockingWireType: HighVoltage
- type: Appearance
visuals:
- type: StackVisualizer
stackLayers:
- coilhv-10
- coilhv-20
- coilhv-30
- type: entity - type: entity
parent: HVWireStack parent: HVWireStack
@@ -42,7 +50,7 @@
suffix: 1 suffix: 1
components: components:
- type: Sprite - type: Sprite
state: coilhv-10 state: coilhv-10
- type: Item - type: Item
size: 3 size: 3
- type: Stack - type: Stack
@@ -55,14 +63,21 @@
description: Low-Voltage stack of wires for connecting APCs to machines and other purposes. description: Low-Voltage stack of wires for connecting APCs to machines and other purposes.
suffix: Full suffix: Full
components: components:
- type: Sprite - type: Sprite
state: coillv-30 state: coillv-30
- type: Item - type: Item
size: 10 size: 10
HeldPrefix: coillv HeldPrefix: coillv
- type: WirePlacer - type: WirePlacer
wirePrototypeID: ApcExtensionCable wirePrototypeID: ApcExtensionCable
blockingWireType: Apc blockingWireType: Apc
- type: Appearance
visuals:
- type: StackVisualizer
stackLayers:
- coillv-10
- coillv-20
- coillv-30
- type: entity - type: entity
parent: ApcExtensionCableStack parent: ApcExtensionCableStack
@@ -92,6 +107,13 @@
- type: WirePlacer - type: WirePlacer
wirePrototypeID: MVWire wirePrototypeID: MVWire
blockingWireType: MediumVoltage blockingWireType: MediumVoltage
- type: Appearance
visuals:
- type: StackVisualizer
stackLayers:
- coilmv-10
- coilmv-20
- coilmv-30
- type: entity - type: entity
parent: MVWireStack parent: MVWireStack

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

View File

@@ -15,6 +15,24 @@
}, },
{ {
"name": "cig_open" "name": "cig_open"
},
{
"name": "cigarette_1"
},
{
"name": "cigarette_2"
},
{
"name": "cigarette_3"
},
{
"name": "cigarette_4"
},
{
"name": "cigarette_5"
},
{
"name": "cigarette_6"
} }
] ]
} }