Added mapped storage for things like crayon belts and tools (#4201)

* Added mapped storage for things like crayon belts and tools

* Attempt to get StorageFillEvent to work

* Managed to get it working with Visualizer logi

* Improved PR and did some light refactoring of components

* Update Content.Client/Storage/Visualizers/MappedItemVisualizer.cs

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

* Removed event, went with stateful ApperanceData

* Removed ids in favor of whitelist

* Refactor YAML, Moved functionality to Shared and renamed it.

* Changed so insert/remove always send full state.

* Move logic to component

* Fix some issues on MappedVisualizer and few nitpicks

- Fix mapped visualizer only doing init or update layers
- Fixed naming of systems
- Fixed sort of crayons

* Forgot to apply Vera's suggestion

* Fix the data to be more strict and to avoid unnecessary clearing

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
Ygg01
2021-07-22 11:56:55 +02:00
committed by GitHub
parent e15151d052
commit 3fd28c2565
8 changed files with 333 additions and 20 deletions

View File

@@ -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<string> _spriteLayers = new();
public override void InitializeEntity(IEntity entity)
{
base.InitializeEntity(entity);
if (entity.TryGetComponent<ISpriteComponent>(out var spriteComponent))
{
_rsiPath ??= spriteComponent.BaseRSI!.Path!;
}
}
public override void OnChangeData(AppearanceComponent component)
{
base.OnChangeData(component);
if (component.Owner.TryGetComponent<ISpriteComponent>(out var spriteComponent))
{
if (_spriteLayers.Count == 0)
{
InitLayers(spriteComponent, component);
}
EnableLayers(spriteComponent, component);
}
}
private void InitLayers(ISpriteComponent spriteComponent, AppearanceComponent component)
{
if (!component.TryGetData<ShowLayerData>(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<ShowLayerData>(StorageMapVisuals.LayerChanged, out var wrapper))
return;
foreach (var layerName in _spriteLayers)
{
var show = wrapper.QueuedEntities.Contains(layerName);
spriteComponent.LayerSetVisible(layerName, show);
}
}
}
}

View File

@@ -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
/// </code>
[Obsolete("Should be deprecated in favor of SharedItemCounterSystem")]
[RegisterComponent]
public class StorageCounterComponent : Component, ISerializationHooks
{

View File

@@ -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<StorageFillEntry> _contents = new();
[DataField("contents")] private List<StorageFillEntry> _contents = new();
public IReadOnlyList<StorageFillEntry> 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<EntityPrototype>))]
public string? PrototypeId;
[DataField("prob")]
public float SpawnProbability;
[DataField("prob")] public float SpawnProbability;
/// <summary>
/// The probability that an item will spawn. Takes decimal form so 0.05 is 5%, 0.50 is 50% etc.
/// </summary>
[DataField("orGroup")]
public string GroupId;
[DataField("orGroup")] public string GroupId;
/// <summary>
/// orGroup signifies to pick between entities designated with an ID.
///
@@ -92,8 +95,7 @@ namespace Content.Server.Storage.Components
/// </code>
/// </example>
/// </summary>
[DataField("amount")]
public int Amount;
[DataField("amount")] public int Amount;
public void PopulateDefaultValues()
{

View File

@@ -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<string> showLayers)
{
if (msg.Container.Owner.TryGetComponent(out ServerStorageComponent? component))
{
var containedLayers = component.StoredEntities ?? new List<IEntity>();
var list = new List<string>();
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<string>();
return false;
}
}
}

View File

@@ -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<string, SharedMapLayerData> MapLayers = new();
void ISerializationHooks.AfterDeserialization()
{
foreach (var (layerName, val) in MapLayers)
{
val.Layer = layerName;
}
}
}
}

View File

@@ -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
{
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ItemCounterComponent, ComponentInit>(InitLayers);
SubscribeLocalEvent<ItemCounterComponent, EntInsertedIntoContainerMessage>(HandleEntityInsert);
SubscribeLocalEvent<ItemCounterComponent, EntRemovedFromContainerMessage>(HandleEntityRemoved);
}
private void InitLayers(EntityUid uid, ItemCounterComponent component, ComponentInit args)
{
if (component.Owner.TryGetComponent(out SharedAppearanceComponent? appearanceComponent))
{
var list = new List<string>(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<string> containedLayers);
}
}

View File

@@ -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<string> QueuedEntities { get; internal set; }
public ShowLayerData()
{
QueuedEntities = new List<string>();
}
public ShowLayerData(IReadOnlyList<string> other)
{
QueuedEntities = other;
}
public ShowLayerData(ShowLayerData other)
{
QueuedEntities = other.QueuedEntities;
}
}
}

View File

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