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;
}
}
}