Lathe Refactor and ECS (#11201)

* lathe and material storage refactor

* materialStorage ECS

it kinda sus tho

* beginning the lathe shitcode dive

* couple lathe visuals and lathe system

* lathe changes and such

* dynamic lathe databases

* rewrote internal logic

on to ui

* da newI

* material display clientside

* misc ui changes

* component state handling and various other things

* moar

* Update Content.Shared/Lathe/LatheComponent.cs

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

* first volley of sloth review

* more fixes

* losin' my mind

* all da changes

* test fix and other review

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
Nemanja
2022-09-16 19:49:05 -04:00
committed by GitHub
parent b62ab67fcb
commit 2e7dcb1ed8
40 changed files with 1225 additions and 1554 deletions

View File

@@ -0,0 +1,73 @@
using Content.Shared.Research.Prototypes;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Lathe
{
[RegisterComponent, NetworkedComponent]
public sealed class LatheComponent : Component
{
/// <summary>
/// All of the recipes that the lathe has by default
/// </summary>
[DataField("staticRecipes", customTypeSerializer: typeof(PrototypeIdListSerializer<LatheRecipePrototype>))]
public readonly List<string> StaticRecipes = new();
/// <summary>
/// All of the recipes that the lathe is capable of researching
/// </summary>
[DataField("dynamicRecipes", customTypeSerializer: typeof(PrototypeIdListSerializer<LatheRecipePrototype>))]
public readonly List<string>? DynamicRecipes;
/// <summary>
/// The lathe's construction queue
/// </summary>
[DataField("queue")]
public List<LatheRecipePrototype> Queue = new();
/// <summary>
/// How long the inserting animation will play
/// </summary>
[DataField("insertionTime")]
public float InsertionTime = 0.79f; // 0.01 off for animation timing
/// <summary>
/// The sound that plays when the lathe is producing an item, if any
/// </summary>
[DataField("producingSound")]
public SoundSpecifier? ProducingSound;
#region Visualizer info
[DataField("idleState", required: true)]
public string IdleState = default!;
[DataField("runningState", required: true)]
public string RunningState = default!;
[ViewVariables]
[DataField("ignoreColor")]
public bool IgnoreColor;
#endregion
/// <summary>
/// The recipe the lathe is currently producing
/// </summary>
[ViewVariables]
public LatheRecipePrototype? CurrentRecipe;
}
public sealed class LatheGetRecipesEvent : EntityEventArgs
{
public readonly EntityUid Lathe;
public List<string> Recipes = new();
public LatheGetRecipesEvent(EntityUid lathe)
{
Lathe = lathe;
}
}
}

View File

@@ -1,94 +1,60 @@
using Content.Shared.Research.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.Lathe;
/// <summary>
/// Sent to the server to sync material storage and the recipe queue.
/// </summary>
[Serializable, NetSerializable]
public sealed class LatheSyncRequestMessage : BoundUserInterfaceMessage
{
public LatheSyncRequestMessage()
{
}
}
[Serializable, NetSerializable]
public sealed class LatheUpdateState : BoundUserInterfaceState
{
public List<string> Recipes;
/// <summary>
/// Sent to the server to sync the lathe's technology database with the research server.
/// </summary>
[Serializable, NetSerializable]
public sealed class LatheServerSyncMessage : BoundUserInterfaceMessage
{
public LatheServerSyncMessage()
{
}
}
public List<LatheRecipePrototype> Queue;
/// <summary>
/// Sent to the server to open the ResearchClient UI.
/// </summary>
[Serializable, NetSerializable]
public sealed class LatheServerSelectionMessage : BoundUserInterfaceMessage
{
public LatheServerSelectionMessage()
{
}
}
public LatheRecipePrototype? CurrentlyProducing;
/// <summary>
/// Sent to the client when the lathe is producing a recipe.
/// </summary>
[Serializable, NetSerializable]
public sealed class LatheProducingRecipeMessage : BoundUserInterfaceMessage
{
public readonly string ID;
public LatheProducingRecipeMessage(string id)
{
ID = id;
}
}
public LatheUpdateState(List<string> recipes, List<LatheRecipePrototype> queue, LatheRecipePrototype? currentlyProducing = null)
{
Recipes = recipes;
Queue = queue;
CurrentlyProducing = currentlyProducing;
}
}
/// <summary>
/// Sent to the client when the lathe stopped/finished producing a recipe.
/// </summary>
[Serializable, NetSerializable]
public sealed class LatheStoppedProducingRecipeMessage : BoundUserInterfaceMessage
{
public LatheStoppedProducingRecipeMessage()
{
}
}
/// <summary>
/// Sent to the server to sync material storage and the recipe queue.
/// </summary>
[Serializable, NetSerializable]
public sealed class LatheSyncRequestMessage : BoundUserInterfaceMessage { }
/// <summary>
/// Sent to the client to let it know about the recipe queue.
/// </summary>
[Serializable, NetSerializable]
public sealed class LatheFullQueueMessage : BoundUserInterfaceMessage
{
public readonly List<string> Recipes;
public LatheFullQueueMessage(List<string> recipes)
{
Recipes = recipes;
}
}
/// <summary>
/// Sent to the server to sync the lathe's technology database with the research server.
/// </summary>
[Serializable, NetSerializable]
public sealed class LatheServerSyncMessage : BoundUserInterfaceMessage { }
/// <summary>
/// Sent to the server when a client queues a new recipe.
/// </summary>
[Serializable, NetSerializable]
public sealed class LatheQueueRecipeMessage : BoundUserInterfaceMessage
{
public readonly string ID;
public readonly int Quantity;
public LatheQueueRecipeMessage(string id, int quantity)
{
ID = id;
Quantity = quantity;
}
}
/// <summary>
/// Sent to the server to open the ResearchClient UI.
/// </summary>
[Serializable, NetSerializable]
public sealed class LatheServerSelectionMessage : BoundUserInterfaceMessage { }
[NetSerializable, Serializable]
public enum LatheUiKey
{
Key,
}
/// <summary>
/// Sent to the server when a client queues a new recipe.
/// </summary>
[Serializable, NetSerializable]
public sealed class LatheQueueRecipeMessage : BoundUserInterfaceMessage
{
public readonly string ID;
public readonly int Quantity;
public LatheQueueRecipeMessage(string id, int quantity)
{
ID = id;
Quantity = quantity;
}
}
[NetSerializable, Serializable]
public enum LatheUiKey
{
Key,
}

View File

@@ -2,12 +2,12 @@ using Robust.Shared.Serialization;
namespace Content.Shared.Lathe
{
[Serializable, NetSerializable]
/// <summary>
/// Stores bools for if the machine is on
/// and if it's currently running and/or inserting.
/// Used for the visualizer
/// </summary>
[Serializable, NetSerializable]
public enum LatheVisuals : byte
{
IsRunning,

View File

@@ -1,33 +0,0 @@
using Content.Shared.Research.Prototypes;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared.Lathe
{
[NetworkedComponent()]
[Virtual]
public class SharedLatheComponent : Component
{
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
public bool CanProduce(LatheRecipePrototype recipe, int quantity = 1)
{
if (!_entMan.TryGetComponent(Owner, out SharedMaterialStorageComponent? storage)
|| !_entMan.TryGetComponent(Owner, out SharedLatheDatabaseComponent? database)) return false;
if (!database.Contains(recipe)) return false;
foreach (var (material, amount) in recipe.RequiredMaterials)
{
if (storage[material] < (amount * quantity)) return false;
}
return true;
}
public bool CanProduce(string id, int quantity = 1)
{
return PrototypeManager.TryIndex(id, out LatheRecipePrototype? recipe) && CanProduce(recipe, quantity);
}
}
}

View File

@@ -1,127 +0,0 @@
using System.Collections;
using Content.Shared.Research.Prototypes;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Lathe
{
[NetworkedComponent()]
public abstract class SharedLatheDatabaseComponent : Component, IEnumerable<LatheRecipePrototype>, ISerializationHooks
{
[DataField("recipes", customTypeSerializer: typeof(PrototypeIdListSerializer<LatheRecipePrototype>))] private List<string> _recipeIds = new();
public readonly List<LatheRecipePrototype> _recipes = new();
void ISerializationHooks.BeforeSerialization()
{
var list = new List<string>();
foreach (var recipe in _recipes)
{
list.Add(recipe.ID);
}
_recipeIds = list;
}
void ISerializationHooks.AfterDeserialization()
{
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
foreach (var id in _recipeIds)
{
if (prototypeManager.TryIndex(id, out LatheRecipePrototype? recipe))
{
_recipes.Add(recipe);
}
}
}
/// <summary>
/// Removes all recipes from the database if it's not static.
/// </summary>
/// <returns>Whether it could clear the database or not.</returns>
public virtual void Clear()
{
_recipes.Clear();
}
/// <summary>
/// Adds a recipe to the database if it's not static.
/// </summary>
/// <param name="recipe">The recipe to be added.</param>
/// <returns>Whether it could be added or not</returns>
public virtual void AddRecipe(LatheRecipePrototype recipe)
{
if (!Contains(recipe))
_recipes.Add(recipe);
}
/// <summary>
/// Removes a recipe from the database if it's not static.
/// </summary>
/// <param name="recipe">The recipe to be removed.</param>
/// <returns>Whether it could be removed or not</returns>
public virtual bool RemoveRecipe(LatheRecipePrototype recipe)
{
return _recipes.Remove(recipe);
}
/// <summary>
/// Returns whether the database contains the recipe or not.
/// </summary>
/// <param name="recipe">The recipe to check</param>
/// <returns>Whether the database contained the recipe or not.</returns>
public virtual bool Contains(LatheRecipePrototype recipe)
{
return _recipes.Contains(recipe);
}
/// <summary>
/// Returns whether the database contains the recipe or not.
/// </summary>
/// <param name="id">The recipe id to check</param>
/// <returns>Whether the database contained the recipe or not.</returns>
public virtual bool Contains(string id)
{
foreach (var recipe in _recipes)
{
if (recipe.ID == id) return true;
}
return false;
}
public List<string> GetRecipeIdList()
{
var list = new List<string>();
foreach (var recipe in this)
{
list.Add(recipe.ID);
}
return list;
}
public IEnumerator<LatheRecipePrototype> GetEnumerator()
{
return _recipes.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
[NetSerializable, Serializable]
public sealed class LatheDatabaseState : ComponentState
{
public readonly List<string> Recipes;
public LatheDatabaseState(List<string> recipes)
{
Recipes = recipes;
}
}
}

View File

@@ -0,0 +1,38 @@
using Content.Shared.Materials;
using Content.Shared.Research.Prototypes;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Shared.Lathe;
/// <summary>
/// This handles...
/// </summary>
public abstract class SharedLatheSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly SharedMaterialStorageSystem _materialStorage = default!;
[PublicAPI]
public bool CanProduce(EntityUid uid, string recipe, int amount = 1, LatheComponent? component = null)
{
return _proto.TryIndex<LatheRecipePrototype>(recipe, out var proto) && CanProduce(uid, proto, amount, component);
}
public bool CanProduce(EntityUid uid, LatheRecipePrototype recipe, int amount = 1, LatheComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;
if (!HasRecipe(uid, recipe, component))
return false;
foreach (var (material, needed) in recipe.RequiredMaterials)
{
if (_materialStorage.GetMaterialAmount(component.Owner, material) < (amount * needed))
return false;
}
return true;
}
protected abstract bool HasRecipe(EntityUid uid, LatheRecipePrototype recipe, LatheComponent component);
}

View File

@@ -1,73 +0,0 @@
using System.Collections;
using Content.Shared.Materials;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
namespace Content.Shared.Lathe
{
[NetworkedComponent()]
public abstract class SharedMaterialStorageComponent : Component, IEnumerable<KeyValuePair<string, int>>
{
[ViewVariables]
protected virtual Dictionary<string, int> Storage { get; set; } = new();
public int this[string id]
{
get
{
if (!Storage.ContainsKey(id))
return 0;
return Storage[id];
}
}
public int this[MaterialPrototype material]
{
get
{
var id = material.ID;
if (!Storage.ContainsKey(id))
return 0;
return Storage[id];
}
}
/// <summary>
/// The total volume of material stored currently.
/// </summary>
[ViewVariables] public int CurrentAmount
{
get
{
var value = 0;
foreach (var amount in Storage.Values)
{
value += amount;
}
return value;
}
}
public IEnumerator<KeyValuePair<string, int>> GetEnumerator()
{
return Storage.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
[NetSerializable, Serializable]
public sealed class MaterialStorageState : ComponentState
{
public readonly Dictionary<string, int> Storage;
public MaterialStorageState(Dictionary<string, int> storage)
{
Storage = storage;
}
}
}

View File

@@ -1,42 +0,0 @@
using Content.Shared.Research.Prototypes;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Lathe
{
[ComponentReference(typeof(SharedLatheDatabaseComponent))]
[NetworkedComponent()]
public abstract class SharedProtolatheDatabaseComponent : SharedLatheDatabaseComponent, ISerializationHooks
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[DataField("protolatherecipes", customTypeSerializer:typeof(PrototypeIdListSerializer<LatheRecipePrototype>))]
private List<string> _recipeIds = new();
/// <summary>
/// A full list of recipes this protolathe can print.
/// </summary>
public IEnumerable<LatheRecipePrototype> ProtolatheRecipes
{
get
{
foreach (var id in _recipeIds)
{
yield return _prototypeManager.Index<LatheRecipePrototype>(id);
}
}
}
}
[NetSerializable, Serializable]
public sealed class ProtolatheDatabaseState : ComponentState
{
public readonly List<string> Recipes;
public ProtolatheDatabaseState(List<string> recipes)
{
Recipes = recipes;
}
}
}

View File

@@ -0,0 +1,42 @@
using System.Linq;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
namespace Content.Shared.Materials
{
/// <summary>
/// Component to store data such as "this object is made out of steel".
/// This is not a storage system for say smelteries.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed class MaterialComponent : Component
{
[ViewVariables]
[DataField("materials", customTypeSerializer:typeof(PrototypeIdDictionarySerializer<int, MaterialPrototype>))]
// ReSharper disable once CollectionNeverUpdated.Local
public readonly Dictionary<string, int> _materials = new();
public List<string> MaterialIds => _materials.Keys.ToList();
/// <summary>
/// Returns all materials which make up this entity.
/// This property has an IoC resolve and is generally slow, so be sure to cache the results if needed.
/// </summary>
[ViewVariables]
public IEnumerable<MaterialPrototype> Materials
{
get
{
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
foreach (var id in MaterialIds)
{
if(prototypeManager.TryIndex<MaterialPrototype>(id, out var material))
yield return material;
else
Logger.Error($"Material prototype {id} does not exist! Entity: {Owner}");
}
}
}
}
}

View File

@@ -0,0 +1,88 @@
using Content.Shared.Whitelist;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Materials;
[Access(typeof(SharedMaterialStorageSystem))]
[RegisterComponent, NetworkedComponent]
public sealed class MaterialStorageComponent : Component
{
[ViewVariables]
public Dictionary<string, int> Storage { get; set; } = new();
/// <summary>
/// How much material the storage can store in total.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("storageLimit")]
public int? StorageLimit;
/// <summary>
/// Whitelist for specifying the kind of items that can be insert into this entity.
/// </summary>
[ViewVariables]
[DataField("whitelist")]
public EntityWhitelist? EntityWhitelist;
/// <summary>
/// Whitelist generated on runtime for what specific materials can be inserted into this entity.
/// </summary>
[ViewVariables]
[DataField("materialWhiteList", customTypeSerializer: typeof(PrototypeIdListSerializer<MaterialPrototype>))]
public List<string>? MaterialWhiteList;
/// <summary>
/// The sound that plays when inserting an item into the storage
/// </summary>
[DataField("insertingSound")]
public SoundSpecifier? InsertingSound;
}
/// <summary>
/// event raised on the materialStorage when a material entity is inserted into it.
/// </summary>
public readonly struct MaterialEntityInsertedEvent
{
public readonly Dictionary<string, int> Materials;
public MaterialEntityInsertedEvent(Dictionary<string, int> materials)
{
Materials = materials;
}
}
/// <summary>
/// Event raised when a material amount is changed
/// </summary>
public readonly struct MaterialAmountChangedEvent
{
}
public sealed class GetMaterialWhitelistEvent : EntityEventArgs
{
public readonly EntityUid Storage;
public List<string> Whitelist = new();
public GetMaterialWhitelistEvent(EntityUid storage)
{
Storage = storage;
}
}
[Serializable, NetSerializable]
public sealed class MaterialStorageComponentState : ComponentState
{
public Dictionary<string, int> Storage;
public List<string>? MaterialWhitelist;
public MaterialStorageComponentState(Dictionary<string, int> storage, List<string>? materialWhitelist)
{
Storage = storage;
MaterialWhitelist = materialWhitelist;
}
}

View File

@@ -0,0 +1,217 @@
using System.Linq;
using Content.Shared.Interaction;
using Content.Shared.Stacks;
using JetBrains.Annotations;
using Robust.Shared.GameStates;
namespace Content.Shared.Materials;
/// <summary>
/// This handles storing materials and modifying their amounts
/// <see cref="MaterialStorageComponent"/>
/// </summary>
public abstract class SharedMaterialStorageSystem : EntitySystem
{
/// <inheritdoc/>
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MaterialStorageComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<MaterialStorageComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<MaterialStorageComponent, ComponentHandleState>(OnHandleState);
}
private void OnGetState(EntityUid uid, MaterialStorageComponent component, ref ComponentGetState args)
{
args.State = new MaterialStorageComponentState(component.Storage, component.MaterialWhiteList);
}
private void OnHandleState(EntityUid uid, MaterialStorageComponent component, ref ComponentHandleState args)
{
if (args.Current is not MaterialStorageComponentState state)
return;
component.Storage = new Dictionary<string, int>(state.Storage);
if (state.MaterialWhitelist != null)
component.MaterialWhiteList = new List<string>(state.MaterialWhitelist);
}
/// <summary>
/// Gets the volume of a specified material contained in this storage.
/// </summary>
/// <param name="uid"></param>
/// <param name="material"></param>
/// <param name="component"></param>
/// <returns>The volume of the material</returns>
[PublicAPI]
public int GetMaterialAmount(EntityUid uid, MaterialPrototype material, MaterialStorageComponent? component = null)
{
return GetMaterialAmount(uid, material.ID, component);
}
/// <summary>
/// Gets the volume of a specified material contained in this storage.
/// </summary>
/// <param name="uid"></param>
/// <param name="material"></param>
/// <param name="component"></param>
/// <returns>The volume of the material</returns>
public int GetMaterialAmount(EntityUid uid, string material, MaterialStorageComponent? component = null)
{
if (!Resolve(uid, ref component))
return 0; //you have nothing
return !component.Storage.TryGetValue(material, out var amount) ? 0 : amount;
}
/// <summary>
/// Gets the total volume of all materials in the storage.
/// </summary>
/// <param name="uid"></param>
/// <param name="component"></param>
/// <returns>The volume of all materials in the storage</returns>
public int GetTotalMaterialAmount(EntityUid uid, MaterialStorageComponent? component = null)
{
if (!Resolve(uid, ref component))
return 0;
return component.Storage.Values.Sum();
}
/// <summary>
/// Tests if a specific amount of volume will fit in the storage.
/// </summary>
/// <param name="uid"></param>
/// <param name="volume"></param>
/// <param name="component"></param>
/// <returns>If the specified volume will fit</returns>
public bool CanTakeVolume(EntityUid uid, int volume, MaterialStorageComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;
return component.StorageLimit == null || GetTotalMaterialAmount(uid, component) + volume <= component.StorageLimit;
}
/// <summary>
/// Checks if the specified material can be changed by the specified volume.
/// </summary>
/// <param name="uid"></param>
/// <param name="materialId"></param>
/// <param name="volume"></param>
/// <param name="component"></param>
/// <returns>If the amount can be changed</returns>
public bool CanChangeMaterialAmount(EntityUid uid, string materialId, int volume, MaterialStorageComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;
return CanTakeVolume(uid, volume, component) &&
(component.MaterialWhiteList == null || component.MaterialWhiteList.Contains(materialId)) &&
(!component.Storage.TryGetValue(materialId, out var amount) || amount + volume >= 0);
}
/// <summary>
/// Changes the amount of a specific material in the storage.
/// Still respects the filters in place.
/// </summary>
/// <param name="uid"></param>
/// <param name="materialId"></param>
/// <param name="volume"></param>
/// <param name="component"></param>
/// <returns>If it was successful</returns>
public bool TryChangeMaterialAmount(EntityUid uid, string materialId, int volume, MaterialStorageComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;
if (!CanChangeMaterialAmount(uid, materialId, volume, component))
return false;
if (!component.Storage.ContainsKey(materialId))
component.Storage.Add(materialId, 0);
component.Storage[materialId] += volume;
RaiseLocalEvent(uid, new MaterialAmountChangedEvent());
Dirty(component);
return true;
}
/// <summary>
/// Tries to insert an entity into the material storage.
/// </summary>
/// <param name="toInsert"></param>
/// <param name="receiver"></param>
/// <param name="component"></param>
/// <returns>If it was successful</returns>
public bool TryInsertMaterialEntity(EntityUid toInsert, EntityUid receiver, MaterialStorageComponent? component = null)
{
if (!Resolve(receiver, ref component))
return false;
if (!TryComp<MaterialComponent>(toInsert, out var material))
return false;
if (component.EntityWhitelist?.IsValid(toInsert) == false)
return false;
if (component.MaterialWhiteList != null)
{
var matUsed = false;
foreach (var mat in material.Materials)
{
if (component.MaterialWhiteList.Contains(mat.ID))
matUsed = true;
}
if (!matUsed)
return false;
}
var multiplier = TryComp<SharedStackComponent>(toInsert, out var stackComponent) ? stackComponent.Count : 1;
var totalVolume = 0;
foreach (var (mat, vol) in component.Storage)
{
if (!CanChangeMaterialAmount(receiver, mat, vol, component))
return false;
totalVolume += vol * multiplier;
}
if (!CanTakeVolume(receiver, totalVolume, component))
return false;
foreach (var (mat, vol) in material._materials)
{
TryChangeMaterialAmount(receiver, mat, vol * multiplier, component);
}
OnFinishInsertMaterialEntity(toInsert, component);
RaiseLocalEvent(component.Owner, new MaterialEntityInsertedEvent(material._materials));
return true;
}
/// <summary>
/// Broadcasts an event that will collect a list of which materials
/// are allowed to be inserted into the materialStorage.
/// </summary>
/// <param name="uid"></param>
/// <param name="component"></param>
public void UpdateMaterialWhitelist(EntityUid uid, MaterialStorageComponent? component = null)
{
if (!Resolve(uid, ref component, false))
return;
var ev = new GetMaterialWhitelistEvent(uid);
RaiseLocalEvent(uid, ev);
component.MaterialWhiteList = ev.Whitelist;
}
/// <remarks>
/// This is done because of popup spam and not being able
/// to do entity deletion clientside.
/// </remarks>
protected abstract void OnFinishInsertMaterialEntity(EntityUid toInsert, MaterialStorageComponent component);
private void OnInteractUsing(EntityUid uid, MaterialStorageComponent component, InteractUsingEvent args)
{
if (args.Handled)
return;
args.Handled = TryInsertMaterialEntity(args.Used, uid, component);
}
}