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:
@@ -102,7 +102,6 @@ namespace Content.Client.Entry
|
|||||||
_componentFactory.IgnoreMissingComponents();
|
_componentFactory.IgnoreMissingComponents();
|
||||||
|
|
||||||
// Do not add to these, they are legacy.
|
// Do not add to these, they are legacy.
|
||||||
_componentFactory.RegisterClass<SharedLatheComponent>();
|
|
||||||
_componentFactory.RegisterClass<SharedSpawnPointComponent>();
|
_componentFactory.RegisterClass<SharedSpawnPointComponent>();
|
||||||
_componentFactory.RegisterClass<SharedReagentDispenserComponent>();
|
_componentFactory.RegisterClass<SharedReagentDispenserComponent>();
|
||||||
_componentFactory.RegisterClass<SharedGravityGeneratorComponent>();
|
_componentFactory.RegisterClass<SharedGravityGeneratorComponent>();
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
using Content.Shared.Lathe;
|
|
||||||
using Content.Shared.Research.Prototypes;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Client.Lathe.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
[ComponentReference(typeof(SharedLatheDatabaseComponent))]
|
|
||||||
public sealed class LatheDatabaseComponent : SharedLatheDatabaseComponent
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
|
||||||
{
|
|
||||||
base.HandleComponentState(curState, nextState);
|
|
||||||
|
|
||||||
if (curState is not LatheDatabaseState state) return;
|
|
||||||
|
|
||||||
Clear();
|
|
||||||
|
|
||||||
foreach (var id in state.Recipes)
|
|
||||||
{
|
|
||||||
if (!_prototypeManager.TryIndex(id, out LatheRecipePrototype? recipe)) continue;
|
|
||||||
AddRecipe(recipe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
namespace Content.Client.Lathe;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Holds the idle and running state for machines to control
|
|
||||||
/// playing animations on the client.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed class LatheVisualsComponent : Component
|
|
||||||
{
|
|
||||||
[DataField("idleState", required: true)]
|
|
||||||
public string IdleState = default!;
|
|
||||||
|
|
||||||
[DataField("runningState", required: true)]
|
|
||||||
public string RunningState = default!;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("ignoreColor")]
|
|
||||||
public bool IgnoreColor;
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Content.Shared.Lathe;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
|
|
||||||
namespace Content.Client.Lathe.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
[ComponentReference(typeof(SharedMaterialStorageComponent))]
|
|
||||||
public sealed class MaterialStorageComponent : SharedMaterialStorageComponent
|
|
||||||
{
|
|
||||||
protected override Dictionary<string, int> Storage { get; set; } = new();
|
|
||||||
|
|
||||||
public event Action? OnMaterialStorageChanged;
|
|
||||||
|
|
||||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
|
||||||
{
|
|
||||||
base.HandleComponentState(curState, nextState);
|
|
||||||
if (curState is not MaterialStorageState state) return;
|
|
||||||
Storage = state.Storage;
|
|
||||||
OnMaterialStorageChanged?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Content.Shared.Lathe;
|
|
||||||
using Content.Shared.Research.Prototypes;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Client.Lathe.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
[ComponentReference(typeof(SharedLatheDatabaseComponent))]
|
|
||||||
public sealed class ProtolatheDatabaseComponent : SharedProtolatheDatabaseComponent
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invoked when the database gets updated.
|
|
||||||
/// </summary>
|
|
||||||
public event Action? OnDatabaseUpdated;
|
|
||||||
|
|
||||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
|
||||||
{
|
|
||||||
base.HandleComponentState(curState, nextState);
|
|
||||||
|
|
||||||
if (curState is not ProtolatheDatabaseState state) return;
|
|
||||||
|
|
||||||
Clear();
|
|
||||||
|
|
||||||
foreach (var id in state.Recipes)
|
|
||||||
{
|
|
||||||
if(!_prototypeManager.TryIndex(id, out LatheRecipePrototype? recipe)) continue;
|
|
||||||
AddRecipe(recipe);
|
|
||||||
}
|
|
||||||
|
|
||||||
OnDatabaseUpdated?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,12 +2,20 @@ using Robust.Client.GameObjects;
|
|||||||
using Content.Shared.Lathe;
|
using Content.Shared.Lathe;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Client.Power;
|
using Content.Client.Power;
|
||||||
|
using Content.Shared.Research.Prototypes;
|
||||||
|
|
||||||
namespace Content.Client.Lathe
|
namespace Content.Client.Lathe;
|
||||||
|
|
||||||
|
public sealed class LatheSystem : SharedLatheSystem
|
||||||
{
|
{
|
||||||
public sealed class LatheSystem : VisualizerSystem<LatheVisualsComponent>
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
protected override void OnAppearanceChange(EntityUid uid, LatheVisualsComponent component, ref AppearanceChangeEvent args)
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<LatheComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAppearanceChange(EntityUid uid, LatheComponent component, ref AppearanceChangeEvent args)
|
||||||
{
|
{
|
||||||
if (args.Sprite == null)
|
if (args.Sprite == null)
|
||||||
return;
|
return;
|
||||||
@@ -39,8 +47,17 @@ namespace Content.Client.Lathe
|
|||||||
args.Sprite.LayerSetVisible(isInsertingLayer, isInserting);
|
args.Sprite.LayerSetVisible(isInsertingLayer, isInserting);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///<remarks>
|
||||||
|
/// Whether or not a recipe is available is not really visible to the client,
|
||||||
|
/// so it just defaults to true.
|
||||||
|
///</remarks>
|
||||||
|
protected override bool HasRecipe(EntityUid uid, LatheRecipePrototype recipe, LatheComponent component)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum LatheVisualLayers : byte
|
public enum LatheVisualLayers : byte
|
||||||
{
|
{
|
||||||
IsRunning,
|
IsRunning,
|
||||||
|
|||||||
@@ -1,101 +1,64 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Content.Client.Lathe.Components;
|
|
||||||
using Content.Shared.Lathe;
|
using Content.Shared.Lathe;
|
||||||
using Content.Shared.Research.Prototypes;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
using static Content.Shared.Lathe.SharedLatheComponent;
|
|
||||||
|
|
||||||
namespace Content.Client.Lathe.UI
|
namespace Content.Client.Lathe.UI
|
||||||
{
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
public sealed class LatheBoundUserInterface : BoundUserInterface
|
public sealed class LatheBoundUserInterface : BoundUserInterface
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
[ViewVariables] private LatheMenu? _menu;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[ViewVariables] private LatheQueueMenu? _queueMenu;
|
||||||
|
|
||||||
[ViewVariables]
|
public EntityUid Lathe;
|
||||||
private LatheMenu? _menu;
|
|
||||||
[ViewVariables]
|
|
||||||
private LatheQueueMenu? _queueMenu;
|
|
||||||
|
|
||||||
public MaterialStorageComponent? Storage { get; private set; }
|
|
||||||
public SharedLatheComponent? Lathe { get; private set; }
|
|
||||||
public SharedLatheDatabaseComponent? Database { get; private set; }
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Queue<LatheRecipePrototype> QueuedRecipes => _queuedRecipes;
|
|
||||||
private readonly Queue<LatheRecipePrototype> _queuedRecipes = new();
|
|
||||||
|
|
||||||
public LatheBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
|
public LatheBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
|
||||||
{
|
{
|
||||||
SendMessage(new LatheSyncRequestMessage());
|
Lathe = owner.Owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Open()
|
protected override void Open()
|
||||||
{
|
{
|
||||||
base.Open();
|
base.Open();
|
||||||
|
|
||||||
if (!_entMan.TryGetComponent(Owner.Owner, out MaterialStorageComponent? storage)
|
|
||||||
|| !_entMan.TryGetComponent(Owner.Owner, out SharedLatheComponent? lathe)
|
|
||||||
|| !_entMan.TryGetComponent(Owner.Owner, out SharedLatheDatabaseComponent? database)) return;
|
|
||||||
|
|
||||||
Storage = storage;
|
|
||||||
Lathe = lathe;
|
|
||||||
Database = database;
|
|
||||||
|
|
||||||
_menu = new LatheMenu(this);
|
_menu = new LatheMenu(this);
|
||||||
_queueMenu = new LatheQueueMenu(this);
|
_queueMenu = new LatheQueueMenu(this);
|
||||||
|
|
||||||
_menu.OnClose += Close;
|
_menu.OnClose += Close;
|
||||||
|
|
||||||
_menu.Populate();
|
_menu.OnQueueButtonPressed += _ =>
|
||||||
_menu.PopulateMaterials();
|
{
|
||||||
|
_queueMenu.OpenCenteredLeft();
|
||||||
_menu.QueueButton.OnPressed += (_) => { _queueMenu.OpenCentered(); };
|
};
|
||||||
|
_menu.OnServerListButtonPressed += _ =>
|
||||||
_menu.ServerConnectButton.OnPressed += (_) =>
|
|
||||||
{
|
{
|
||||||
SendMessage(new LatheServerSelectionMessage());
|
SendMessage(new LatheServerSelectionMessage());
|
||||||
};
|
};
|
||||||
|
_menu.OnServerSyncButtonPressed += _ =>
|
||||||
_menu.ServerSyncButton.OnPressed += (_) =>
|
|
||||||
{
|
{
|
||||||
SendMessage(new LatheServerSyncMessage());
|
SendMessage(new LatheServerSyncMessage());
|
||||||
};
|
};
|
||||||
|
_menu.RecipeQueueAction += (recipe, amount) =>
|
||||||
storage.OnMaterialStorageChanged += _menu.PopulateDisabled;
|
{
|
||||||
storage.OnMaterialStorageChanged += _menu.PopulateMaterials;
|
SendMessage(new LatheQueueRecipeMessage(recipe, amount));
|
||||||
|
};
|
||||||
|
|
||||||
_menu.OpenCentered();
|
_menu.OpenCentered();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Queue(LatheRecipePrototype recipe, int quantity = 1)
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
{
|
{
|
||||||
SendMessage(new LatheQueueRecipeMessage(recipe.ID, quantity));
|
base.UpdateState(state);
|
||||||
}
|
|
||||||
|
|
||||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
switch (state)
|
||||||
{
|
{
|
||||||
switch (message)
|
case LatheUpdateState msg:
|
||||||
{
|
if (_menu != null)
|
||||||
case LatheProducingRecipeMessage msg:
|
_menu.Recipes = msg.Recipes;
|
||||||
if (!_prototypeManager.TryIndex(msg.ID, out LatheRecipePrototype? recipe)) break;
|
_menu?.PopulateRecipes(Owner.Owner);
|
||||||
_queueMenu?.SetInfo(recipe);
|
_menu?.PopulateMaterials(Lathe);
|
||||||
break;
|
_queueMenu?.PopulateList(msg.Queue);
|
||||||
case LatheStoppedProducingRecipeMessage _:
|
_queueMenu?.SetInfo(msg.CurrentlyProducing);
|
||||||
_queueMenu?.ClearInfo();
|
|
||||||
break;
|
|
||||||
case LatheFullQueueMessage msg:
|
|
||||||
_queuedRecipes.Clear();
|
|
||||||
foreach (var id in msg.Recipes)
|
|
||||||
{
|
|
||||||
if (!_prototypeManager.TryIndex(id, out LatheRecipePrototype? recipePrototype)) break;
|
|
||||||
_queuedRecipes.Enqueue(recipePrototype);
|
|
||||||
}
|
|
||||||
_queueMenu?.PopulateList();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +66,8 @@ namespace Content.Client.Lathe.UI
|
|||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
if (!disposing) return;
|
if (!disposing)
|
||||||
|
return;
|
||||||
_menu?.Dispose();
|
_menu?.Dispose();
|
||||||
_queueMenu?.Dispose();
|
_queueMenu?.Dispose();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,271 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using Content.Client.Lathe.Components;
|
|
||||||
using Content.Shared.Lathe;
|
|
||||||
using Content.Shared.Materials;
|
|
||||||
using Content.Shared.Research.Prototypes;
|
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
|
||||||
using Robust.Client.Utility;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
|
||||||
|
|
||||||
namespace Content.Client.Lathe.UI
|
|
||||||
{
|
|
||||||
public sealed class LatheMenu : DefaultWindow
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
private readonly ItemList _items;
|
|
||||||
private readonly ItemList _materials;
|
|
||||||
private readonly LineEdit _amountLineEdit;
|
|
||||||
private readonly LineEdit _searchBar;
|
|
||||||
public Button QueueButton;
|
|
||||||
public Button ServerConnectButton;
|
|
||||||
public Button ServerSyncButton;
|
|
||||||
|
|
||||||
public const float RecipeTooltipDelay = 0.5f;
|
|
||||||
|
|
||||||
public LatheBoundUserInterface Owner { get; }
|
|
||||||
|
|
||||||
private readonly List<LatheRecipePrototype> _shownRecipes = new();
|
|
||||||
|
|
||||||
public LatheMenu(LatheBoundUserInterface owner)
|
|
||||||
{
|
|
||||||
SetSize = MinSize = (300, 450);
|
|
||||||
IoCManager.InjectDependencies(this);
|
|
||||||
|
|
||||||
Owner = owner;
|
|
||||||
|
|
||||||
Title = Loc.GetString("lathe-menu-title"); // TODO Replace this with the name of the lathe itself
|
|
||||||
|
|
||||||
var vBox = new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Vertical,
|
|
||||||
VerticalExpand = true,
|
|
||||||
SeparationOverride = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
var hBoxButtons = new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Horizontal,
|
|
||||||
HorizontalExpand = true,
|
|
||||||
VerticalExpand = true,
|
|
||||||
SizeFlagsStretchRatio = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
QueueButton = new Button()
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("lathe-menu-queue"),
|
|
||||||
TextAlign = Label.AlignMode.Center,
|
|
||||||
SizeFlagsStretchRatio = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
ServerConnectButton = new Button()
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("lathe-menu-server-list"),
|
|
||||||
TextAlign = Label.AlignMode.Center,
|
|
||||||
SizeFlagsStretchRatio = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
ServerSyncButton = new Button()
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("lathe-menu-sync"),
|
|
||||||
TextAlign = Label.AlignMode.Center,
|
|
||||||
SizeFlagsStretchRatio = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
var spacer = new Control()
|
|
||||||
{
|
|
||||||
HorizontalExpand = true,
|
|
||||||
SizeFlagsStretchRatio = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
var hBoxFilter = new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Horizontal,
|
|
||||||
HorizontalExpand = true,
|
|
||||||
VerticalExpand = true,
|
|
||||||
SizeFlagsStretchRatio = 1
|
|
||||||
};
|
|
||||||
|
|
||||||
_searchBar = new LineEdit()
|
|
||||||
{
|
|
||||||
PlaceHolder = Loc.GetString("lathe-menu-search-designs"),
|
|
||||||
HorizontalExpand = true,
|
|
||||||
SizeFlagsStretchRatio = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
_searchBar.OnTextChanged += Populate;
|
|
||||||
|
|
||||||
var filterButton = new Button()
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("lathe-menu-search-filter"),
|
|
||||||
TextAlign = Label.AlignMode.Center,
|
|
||||||
SizeFlagsStretchRatio = 1,
|
|
||||||
Disabled = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
_items = new ItemList()
|
|
||||||
{
|
|
||||||
SizeFlagsStretchRatio = 8,
|
|
||||||
VerticalExpand = true,
|
|
||||||
SelectMode = ItemList.ItemListSelectMode.Button,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This is a shitty hack, because item lists apparently don't actually support tooltips. Yay..
|
|
||||||
_items.OnItemHover += (ev) =>
|
|
||||||
{
|
|
||||||
ev.ItemList.HideTooltip();
|
|
||||||
ev.ItemList.ToolTip = ev.ItemList[ev.ItemIndex].TooltipText;
|
|
||||||
};
|
|
||||||
_items.TooltipDelay = RecipeTooltipDelay;
|
|
||||||
|
|
||||||
_items.OnItemSelected += ItemSelected;
|
|
||||||
|
|
||||||
_amountLineEdit = new LineEdit()
|
|
||||||
{
|
|
||||||
PlaceHolder = Loc.GetString("lathe-menu-search-amount"),
|
|
||||||
Text = "1",
|
|
||||||
HorizontalExpand = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
_amountLineEdit.OnTextChanged += PopulateDisabled;
|
|
||||||
|
|
||||||
_materials = new ItemList()
|
|
||||||
{
|
|
||||||
VerticalExpand = true,
|
|
||||||
SizeFlagsStretchRatio = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
hBoxButtons.AddChild(spacer);
|
|
||||||
if (Owner.Database is ProtolatheDatabaseComponent database)
|
|
||||||
{
|
|
||||||
hBoxButtons.AddChild(ServerConnectButton);
|
|
||||||
hBoxButtons.AddChild(ServerSyncButton);
|
|
||||||
database.OnDatabaseUpdated += Populate;
|
|
||||||
}
|
|
||||||
hBoxButtons.AddChild(QueueButton);
|
|
||||||
|
|
||||||
hBoxFilter.AddChild(_searchBar);
|
|
||||||
hBoxFilter.AddChild(filterButton);
|
|
||||||
|
|
||||||
vBox.AddChild(hBoxButtons);
|
|
||||||
vBox.AddChild(hBoxFilter);
|
|
||||||
vBox.AddChild(_items);
|
|
||||||
vBox.AddChild(_amountLineEdit);
|
|
||||||
vBox.AddChild(_materials);
|
|
||||||
|
|
||||||
Contents.AddChild(vBox);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ItemSelected(ItemList.ItemListSelectedEventArgs args)
|
|
||||||
{
|
|
||||||
args.ItemList.HideTooltip();
|
|
||||||
args.ItemList.ToolTip = args.ItemList[args.ItemIndex].TooltipText;
|
|
||||||
|
|
||||||
int.TryParse(_amountLineEdit.Text, out var quantity);
|
|
||||||
if (quantity <= 0) quantity = 1;
|
|
||||||
Owner.Queue(_shownRecipes[args.ItemIndex], quantity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PopulateMaterials()
|
|
||||||
{
|
|
||||||
_materials.Clear();
|
|
||||||
|
|
||||||
if (Owner.Storage == null) return;
|
|
||||||
|
|
||||||
foreach (var (id, amount) in Owner.Storage)
|
|
||||||
{
|
|
||||||
if (!_prototypeManager.TryIndex(id, out MaterialPrototype? materialPrototype)) continue;
|
|
||||||
var material = materialPrototype;
|
|
||||||
_materials.AddItem($"{material.Name} {amount} cm³", material.Icon.Frame0(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Disables or enables shown recipes depending on whether there are enough materials for it or not.
|
|
||||||
/// </summary>
|
|
||||||
public void PopulateDisabled()
|
|
||||||
{
|
|
||||||
int.TryParse(_amountLineEdit.Text, out var quantity);
|
|
||||||
if (quantity <= 0) quantity = 1;
|
|
||||||
for (var i = 0; i < _shownRecipes.Count; i++)
|
|
||||||
{
|
|
||||||
var prototype = _shownRecipes[i];
|
|
||||||
_items[i].Disabled = !Owner.Lathe?.CanProduce(prototype, quantity) ?? true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="PopulateDisabled()"/>
|
|
||||||
public void PopulateDisabled(LineEdit.LineEditEventArgs args)
|
|
||||||
{
|
|
||||||
PopulateDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds shown recipes to the ItemList control.
|
|
||||||
/// </summary>
|
|
||||||
public void PopulateList()
|
|
||||||
{
|
|
||||||
_items.Clear();
|
|
||||||
foreach (var prototype in _shownRecipes)
|
|
||||||
{
|
|
||||||
var item = _items.AddItem(prototype.Name, prototype.Icon.Frame0());
|
|
||||||
|
|
||||||
StringBuilder sb = new();
|
|
||||||
bool first = true;
|
|
||||||
foreach (var (id, quantity) in prototype.RequiredMaterials)
|
|
||||||
{
|
|
||||||
if (!_prototypeManager.TryIndex<MaterialPrototype>(id, out var proto))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (first)
|
|
||||||
first = false;
|
|
||||||
else
|
|
||||||
sb.Append("\n");
|
|
||||||
|
|
||||||
sb.Append(quantity.ToString());
|
|
||||||
sb.Append(" ");
|
|
||||||
sb.Append(proto.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
item.TooltipText = sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
PopulateDisabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Populates the list of recipes that will actually be shown, using the current filters.
|
|
||||||
/// </summary>
|
|
||||||
public void Populate()
|
|
||||||
{
|
|
||||||
_shownRecipes.Clear();
|
|
||||||
|
|
||||||
if (Owner.Database == null) return;
|
|
||||||
|
|
||||||
foreach (var prototype in Owner.Database)
|
|
||||||
{
|
|
||||||
if (_searchBar.Text.Trim().Length != 0)
|
|
||||||
{
|
|
||||||
if (prototype.Name.ToLowerInvariant().Contains(_searchBar.Text.Trim().ToLowerInvariant()))
|
|
||||||
_shownRecipes.Add(prototype);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_shownRecipes.Add(prototype);
|
|
||||||
}
|
|
||||||
|
|
||||||
PopulateList();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="Populate"/>
|
|
||||||
public void Populate(LineEdit.LineEditEventArgs args)
|
|
||||||
{
|
|
||||||
Populate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
82
Content.Client/Lathe/UI/LatheMenu.xaml
Normal file
82
Content.Client/Lathe/UI/LatheMenu.xaml
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<DefaultWindow
|
||||||
|
xmlns="https://spacestation14.io"
|
||||||
|
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||||
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
|
Title="{Loc 'lathe-menu-title'}"
|
||||||
|
MinSize="300 450"
|
||||||
|
SetSize="300 450">
|
||||||
|
<BoxContainer
|
||||||
|
Orientation="Vertical"
|
||||||
|
VerticalExpand="True"
|
||||||
|
SeparationOverride="5">
|
||||||
|
<BoxContainer
|
||||||
|
Orientation="Horizontal"
|
||||||
|
Align="End"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
VerticalExpand="True"
|
||||||
|
SizeFlagsStretchRatio="1">
|
||||||
|
<Button
|
||||||
|
Name="QueueButton"
|
||||||
|
Text="{Loc 'lathe-menu-queue'}"
|
||||||
|
TextAlign="Center"
|
||||||
|
Mode="Press"
|
||||||
|
SizeFlagsStretchRatio="1">
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
Name="ServerListButton"
|
||||||
|
Text="{Loc 'lathe-menu-server-list'}"
|
||||||
|
TextAlign="Center"
|
||||||
|
Mode="Press"
|
||||||
|
SizeFlagsStretchRatio="1">
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
Name="ServerSyncButton"
|
||||||
|
Text="{Loc 'lathe-menu-sync'}"
|
||||||
|
TextAlign="Center"
|
||||||
|
Mode="Press"
|
||||||
|
SizeFlagsStretchRatio="1">
|
||||||
|
</Button>
|
||||||
|
</BoxContainer>
|
||||||
|
<BoxContainer
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
VerticalExpand="True"
|
||||||
|
SizeFlagsStretchRatio="1">
|
||||||
|
<LineEdit
|
||||||
|
Name="SearchBar"
|
||||||
|
PlaceHolder="{Loc 'lathe-menu-search-designs'}"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
SizeFlagsStretchRatio="1">
|
||||||
|
</LineEdit>
|
||||||
|
<Button
|
||||||
|
Name="FilterButton"
|
||||||
|
Text="{Loc 'lathe-menu-search-filter'}"
|
||||||
|
TextAlign="Center"
|
||||||
|
SizeFlagsStretchRatio="1"
|
||||||
|
Disabled="True">
|
||||||
|
</Button>
|
||||||
|
</BoxContainer>
|
||||||
|
<ScrollContainer MinHeight="225">
|
||||||
|
<BoxContainer
|
||||||
|
Name="RecipeList"
|
||||||
|
Orientation="Vertical"
|
||||||
|
SizeFlagsStretchRatio="8"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
VerticalExpand="True">
|
||||||
|
</BoxContainer>
|
||||||
|
</ScrollContainer>
|
||||||
|
<LineEdit
|
||||||
|
Name="AmountLineEdit"
|
||||||
|
PlaceHolder="{Loc 'lathe-menu-search-amount'}"
|
||||||
|
Text="1"
|
||||||
|
HorizontalExpand="True">
|
||||||
|
</LineEdit>
|
||||||
|
<ItemList
|
||||||
|
Name="Materials"
|
||||||
|
VerticalExpand="True"
|
||||||
|
SizeFlagsStretchRatio="3">
|
||||||
|
</ItemList>
|
||||||
|
</BoxContainer>
|
||||||
|
</DefaultWindow>
|
||||||
|
|
||||||
|
|
||||||
142
Content.Client/Lathe/UI/LatheMenu.xaml.cs
Normal file
142
Content.Client/Lathe/UI/LatheMenu.xaml.cs
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Content.Shared.Lathe;
|
||||||
|
using Content.Shared.Materials;
|
||||||
|
using Content.Shared.Research.Prototypes;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Client.Lathe.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class LatheMenu : DefaultWindow
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||||
|
private readonly SpriteSystem _spriteSystem;
|
||||||
|
private readonly LatheSystem _lathe;
|
||||||
|
|
||||||
|
public event Action<BaseButton.ButtonEventArgs>? OnQueueButtonPressed;
|
||||||
|
public event Action<BaseButton.ButtonEventArgs>? OnServerListButtonPressed;
|
||||||
|
public event Action<BaseButton.ButtonEventArgs>? OnServerSyncButtonPressed;
|
||||||
|
public event Action<string, int>? RecipeQueueAction;
|
||||||
|
|
||||||
|
public List<string> Recipes = new();
|
||||||
|
private List<LatheRecipePrototype> _oldRecipesToShow = new();
|
||||||
|
|
||||||
|
public LatheMenu(LatheBoundUserInterface owner)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
_spriteSystem = _entityManager.EntitySysManager.GetEntitySystem<SpriteSystem>();
|
||||||
|
_lathe = _entityManager.EntitySysManager.GetEntitySystem<LatheSystem>();
|
||||||
|
|
||||||
|
Title = _entityManager.GetComponent<MetaDataComponent>(owner.Lathe).EntityName;
|
||||||
|
|
||||||
|
SearchBar.OnTextChanged += _ =>
|
||||||
|
{
|
||||||
|
PopulateRecipes(owner.Lathe);
|
||||||
|
};
|
||||||
|
AmountLineEdit.OnTextChanged += _ =>
|
||||||
|
{
|
||||||
|
PopulateRecipes(owner.Lathe);
|
||||||
|
};
|
||||||
|
|
||||||
|
QueueButton.OnPressed += a => OnQueueButtonPressed?.Invoke(a);
|
||||||
|
ServerListButton.OnPressed += a => OnServerListButtonPressed?.Invoke(a);
|
||||||
|
|
||||||
|
//refresh the bui state
|
||||||
|
ServerSyncButton.OnPressed += a => OnServerSyncButtonPressed?.Invoke(a);
|
||||||
|
|
||||||
|
if (_entityManager.TryGetComponent<LatheComponent>(owner.Lathe, out var latheComponent))
|
||||||
|
{
|
||||||
|
if (latheComponent.DynamicRecipes == null)
|
||||||
|
{
|
||||||
|
ServerListButton.Visible = false;
|
||||||
|
ServerSyncButton.Visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopulateMaterials(EntityUid lathe)
|
||||||
|
{
|
||||||
|
if (!_entityManager.TryGetComponent<MaterialStorageComponent>(lathe, out var materials))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Materials.Clear();
|
||||||
|
foreach (var (id, amount) in materials.Storage)
|
||||||
|
{
|
||||||
|
if (!_prototypeManager.TryIndex(id, out MaterialPrototype? material))
|
||||||
|
continue;
|
||||||
|
var mat = Loc.GetString("lathe-menu-material-display",
|
||||||
|
("material", material.Name), ("amount", amount));
|
||||||
|
Materials.AddItem(mat, _spriteSystem.Frame0(material.Icon), false);
|
||||||
|
}
|
||||||
|
PopulateRecipes(lathe);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populates the list of all the recipes
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="lathe"></param>
|
||||||
|
public void PopulateRecipes(EntityUid lathe)
|
||||||
|
{
|
||||||
|
var recipesToShow = new List<LatheRecipePrototype>();
|
||||||
|
foreach (var recipe in Recipes)
|
||||||
|
{
|
||||||
|
if (!_prototypeManager.TryIndex<LatheRecipePrototype>(recipe, out var proto))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (SearchBar.Text.Trim().Length != 0)
|
||||||
|
{
|
||||||
|
if (proto.Name.ToLowerInvariant().Contains(SearchBar.Text.Trim().ToLowerInvariant()))
|
||||||
|
recipesToShow.Add(proto);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
recipesToShow.Add(proto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!int.TryParse(AmountLineEdit.Text, out var quantity) || quantity <= 0)
|
||||||
|
quantity = 1;
|
||||||
|
|
||||||
|
RecipeList.Children.Clear();
|
||||||
|
_oldRecipesToShow = recipesToShow;
|
||||||
|
foreach (var prototype in recipesToShow)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new();
|
||||||
|
var first = true;
|
||||||
|
foreach (var (id, amount) in prototype.RequiredMaterials)
|
||||||
|
{
|
||||||
|
if (!_prototypeManager.TryIndex<MaterialPrototype>(id, out var proto))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (first)
|
||||||
|
first = false;
|
||||||
|
else
|
||||||
|
sb.Append('\n');
|
||||||
|
|
||||||
|
sb.Append(amount);
|
||||||
|
sb.Append(' ');
|
||||||
|
sb.Append(proto.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
var icon = _spriteSystem.Frame0(prototype.Icon);
|
||||||
|
var canProduce = _lathe.CanProduce(lathe, prototype, quantity);
|
||||||
|
|
||||||
|
var control = new RecipeControl(prototype, sb.ToString(), canProduce, icon);
|
||||||
|
control.OnButtonPressed += s =>
|
||||||
|
{
|
||||||
|
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount <= 0)
|
||||||
|
amount = 1;
|
||||||
|
RecipeQueueAction?.Invoke(s, amount);
|
||||||
|
};
|
||||||
|
RecipeList.AddChild(control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
using Content.Shared.Research.Prototypes;
|
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
|
||||||
using Robust.Client.Utility;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
|
||||||
|
|
||||||
namespace Content.Client.Lathe.UI
|
|
||||||
{
|
|
||||||
public sealed class LatheQueueMenu : DefaultWindow
|
|
||||||
{
|
|
||||||
public LatheBoundUserInterface Owner { get; set; }
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
private readonly ItemList _queueList;
|
|
||||||
private readonly Label _nameLabel;
|
|
||||||
private readonly Label _description;
|
|
||||||
private readonly TextureRect _icon;
|
|
||||||
|
|
||||||
public LatheQueueMenu(LatheBoundUserInterface owner)
|
|
||||||
{
|
|
||||||
Owner = owner;
|
|
||||||
SetSize = MinSize = (300, 450);
|
|
||||||
Title = Loc.GetString("lathe-queue-menu-title");
|
|
||||||
|
|
||||||
var vBox = new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Vertical
|
|
||||||
};
|
|
||||||
|
|
||||||
var hBox = new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Horizontal,
|
|
||||||
HorizontalExpand = true,
|
|
||||||
SizeFlagsStretchRatio = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
_icon = new TextureRect()
|
|
||||||
{
|
|
||||||
HorizontalExpand = true,
|
|
||||||
SizeFlagsStretchRatio = 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
var vBoxInfo = new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Vertical,
|
|
||||||
VerticalExpand = true,
|
|
||||||
SizeFlagsStretchRatio = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
_nameLabel = new Label()
|
|
||||||
{
|
|
||||||
RectClipContent = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
_description = new Label()
|
|
||||||
{
|
|
||||||
RectClipContent = true,
|
|
||||||
VerticalAlignment = VAlignment.Stretch,
|
|
||||||
VerticalExpand = true
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
_queueList = new ItemList()
|
|
||||||
{
|
|
||||||
VerticalExpand = true,
|
|
||||||
SizeFlagsStretchRatio = 3,
|
|
||||||
SelectMode = ItemList.ItemListSelectMode.None
|
|
||||||
};
|
|
||||||
|
|
||||||
vBoxInfo.AddChild(_nameLabel);
|
|
||||||
vBoxInfo.AddChild(_description);
|
|
||||||
|
|
||||||
hBox.AddChild(_icon);
|
|
||||||
hBox.AddChild(vBoxInfo);
|
|
||||||
|
|
||||||
vBox.AddChild(hBox);
|
|
||||||
vBox.AddChild(_queueList);
|
|
||||||
|
|
||||||
Contents.AddChild(vBox);
|
|
||||||
|
|
||||||
ClearInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetInfo(LatheRecipePrototype recipe)
|
|
||||||
{
|
|
||||||
_icon.Texture = recipe.Icon.Frame0();
|
|
||||||
if (recipe.Name != null)
|
|
||||||
_nameLabel.Text = recipe.Name;
|
|
||||||
if (recipe.Description != null)
|
|
||||||
_description.Text = recipe.Description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearInfo()
|
|
||||||
{
|
|
||||||
_icon.Texture = Texture.Transparent;
|
|
||||||
_nameLabel.Text = "-------";
|
|
||||||
_description.Text = Loc.GetString("lathe-queue-menu-not-producing-text");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PopulateList()
|
|
||||||
{
|
|
||||||
_queueList.Clear();
|
|
||||||
var idx = 1;
|
|
||||||
foreach (var recipe in Owner.QueuedRecipes)
|
|
||||||
{
|
|
||||||
_queueList.AddItem($"{idx}. {recipe.Name}", recipe.Icon.Frame0());
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
42
Content.Client/Lathe/UI/LatheQueueMenu.xaml
Normal file
42
Content.Client/Lathe/UI/LatheQueueMenu.xaml
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<DefaultWindow
|
||||||
|
xmlns="https://spacestation14.io"
|
||||||
|
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||||
|
Title="{Loc 'lathe-queue-menu-title'}"
|
||||||
|
MinSize="300 450"
|
||||||
|
SetSize="300 450">
|
||||||
|
<BoxContainer Orientation="Vertical">
|
||||||
|
<BoxContainer
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
SizeFlagsStretchRatio="2">
|
||||||
|
<TextureRect
|
||||||
|
Name="Icon"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
SizeFlagsStretchRatio="2">
|
||||||
|
</TextureRect>
|
||||||
|
<BoxContainer
|
||||||
|
Orientation="Vertical"
|
||||||
|
VerticalExpand="True"
|
||||||
|
SizeFlagsStretchRatio="3">
|
||||||
|
<Label
|
||||||
|
Name="NameLabel"
|
||||||
|
RectClipContent="True">
|
||||||
|
</Label>
|
||||||
|
<Label
|
||||||
|
Name="Description"
|
||||||
|
RectClipContent="True"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
VerticalExpand="True">
|
||||||
|
</Label>
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
<ItemList
|
||||||
|
Name="QueueList"
|
||||||
|
VerticalExpand="True"
|
||||||
|
SizeFlagsStretchRatio="3"
|
||||||
|
SelectMode="None">
|
||||||
|
</ItemList>
|
||||||
|
</BoxContainer>
|
||||||
|
</DefaultWindow>
|
||||||
|
|
||||||
|
|
||||||
52
Content.Client/Lathe/UI/LatheQueueMenu.xaml.cs
Normal file
52
Content.Client/Lathe/UI/LatheQueueMenu.xaml.cs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
using Content.Shared.Research.Prototypes;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.Lathe.UI
|
||||||
|
{
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class LatheQueueMenu : DefaultWindow
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||||
|
private readonly SpriteSystem _spriteSystem;
|
||||||
|
|
||||||
|
public LatheQueueMenu(LatheBoundUserInterface owner)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
_spriteSystem = _entityManager.EntitySysManager.GetEntitySystem<SpriteSystem>();
|
||||||
|
|
||||||
|
SetInfo(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetInfo(LatheRecipePrototype? recipe)
|
||||||
|
{
|
||||||
|
if (recipe != null)
|
||||||
|
{
|
||||||
|
Icon.Texture = _spriteSystem.Frame0(recipe.Icon);
|
||||||
|
NameLabel.Text = recipe.Name;
|
||||||
|
Description.Text = recipe.Description;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Icon.Texture = Texture.Transparent;
|
||||||
|
NameLabel.Text = string.Empty;
|
||||||
|
Description.Text = Loc.GetString("lathe-queue-menu-not-producing-text");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopulateList(List<LatheRecipePrototype> queue)
|
||||||
|
{
|
||||||
|
QueueList.Clear();
|
||||||
|
var idx = 1;
|
||||||
|
foreach (var recipe in queue)
|
||||||
|
{
|
||||||
|
QueueList.AddItem($"{idx}. {recipe.Name}", _spriteSystem.Frame0(recipe.Icon));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
Content.Client/Lathe/UI/RecipeControl.xaml
Normal file
15
Content.Client/Lathe/UI/RecipeControl.xaml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<Control xmlns="https://spacestation14.io">
|
||||||
|
<Button
|
||||||
|
Name="Button"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
TooltipDelay="0.5">
|
||||||
|
<BoxContainer Orientation="Horizontal">
|
||||||
|
<TextureRect
|
||||||
|
Name="RecipeTexture"
|
||||||
|
Margin="0,0,4,0"
|
||||||
|
MinSize="32 32"
|
||||||
|
Stretch="KeepAspectCentered" />
|
||||||
|
<Label Name="RecipeName" HorizontalExpand="True" />
|
||||||
|
</BoxContainer>
|
||||||
|
</Button>
|
||||||
|
</Control>
|
||||||
28
Content.Client/Lathe/UI/RecipeControl.xaml.cs
Normal file
28
Content.Client/Lathe/UI/RecipeControl.xaml.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using Content.Shared.Research.Prototypes;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.Lathe.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class RecipeControl : Control
|
||||||
|
{
|
||||||
|
public Action<string>? OnButtonPressed;
|
||||||
|
|
||||||
|
public RecipeControl(LatheRecipePrototype recipe, string tooltip, bool canProduce, Texture? texture = null)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
|
||||||
|
RecipeName.Text = recipe.Name;
|
||||||
|
RecipeTexture.Texture = texture;
|
||||||
|
Button.ToolTip = tooltip;
|
||||||
|
Button.Disabled = !canProduce;
|
||||||
|
|
||||||
|
Button.OnPressed += (_) =>
|
||||||
|
{
|
||||||
|
OnButtonPressed?.Invoke(recipe.ID);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Content.Client/Materials/MaterialStorageSystem.cs
Normal file
16
Content.Client/Materials/MaterialStorageSystem.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Content.Shared.Materials;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Client.Materials;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This handles...
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MaterialStorageSystem : SharedMaterialStorageSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly TransformSystem _transform = default!;
|
||||||
|
protected override void OnFinishInsertMaterialEntity(EntityUid toInsert, MaterialStorageComponent component)
|
||||||
|
{
|
||||||
|
_transform.DetachParentToNull(Transform(toInsert));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -109,13 +109,6 @@ public sealed class PrototypeSaveTest
|
|||||||
"LauncherCreamPie",
|
"LauncherCreamPie",
|
||||||
"GravityGenerator",
|
"GravityGenerator",
|
||||||
"GravityGeneratorMini",
|
"GravityGeneratorMini",
|
||||||
"Autolathe",
|
|
||||||
"Protolathe",
|
|
||||||
"CircuitImprinter",
|
|
||||||
"SecurityTechFab",
|
|
||||||
"MedicalTechFab",
|
|
||||||
"UniformPrinter",
|
|
||||||
"OreProcessor",
|
|
||||||
"MagazinePistolSubMachineGunTopMounted",
|
"MagazinePistolSubMachineGunTopMounted",
|
||||||
"EpinephrineChemistryBottle",
|
"EpinephrineChemistryBottle",
|
||||||
"RobustHarvestChemistryBottle",
|
"RobustHarvestChemistryBottle",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
using Content.Server.Administration;
|
using Content.Server.Administration;
|
||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Components;
|
||||||
using Content.Server.Cargo.Components;
|
using Content.Server.Cargo.Components;
|
||||||
using Content.Server.Materials;
|
using Content.Shared.Materials;
|
||||||
using Content.Server.Stack;
|
using Content.Server.Stack;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.MobState.Components;
|
using Content.Shared.MobState.Components;
|
||||||
|
|||||||
@@ -16,13 +16,14 @@ using Content.Server.EUI;
|
|||||||
using Content.Server.MachineLinking.System;
|
using Content.Server.MachineLinking.System;
|
||||||
using Content.Server.MachineLinking.Events;
|
using Content.Server.MachineLinking.Events;
|
||||||
using Content.Server.MobState;
|
using Content.Server.MobState;
|
||||||
using Content.Server.Lathe.Components;
|
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Content.Server.Fluids.EntitySystems;
|
using Content.Server.Fluids.EntitySystems;
|
||||||
using Content.Server.Chat.Systems;
|
using Content.Server.Chat.Systems;
|
||||||
using Content.Server.Construction.Components;
|
using Content.Server.Construction.Components;
|
||||||
|
using Content.Server.Materials;
|
||||||
using Content.Server.Stack;
|
using Content.Server.Stack;
|
||||||
using Content.Server.Jobs;
|
using Content.Server.Jobs;
|
||||||
|
using Content.Shared.Materials;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Server.Containers;
|
using Robust.Server.Containers;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
@@ -54,6 +55,7 @@ namespace Content.Server.Cloning.Systems
|
|||||||
[Dependency] private readonly SpillableSystem _spillableSystem = default!;
|
[Dependency] private readonly SpillableSystem _spillableSystem = default!;
|
||||||
[Dependency] private readonly ChatSystem _chatSystem = default!;
|
[Dependency] private readonly ChatSystem _chatSystem = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
[Dependency] private readonly IConfigurationManager _configManager = default!;
|
||||||
|
[Dependency] private readonly MaterialStorageSystem _material = default!;
|
||||||
|
|
||||||
public readonly Dictionary<Mind.Mind, EntityUid> ClonesWaitingForMind = new();
|
public readonly Dictionary<Mind.Mind, EntityUid> ClonesWaitingForMind = new();
|
||||||
public const float EasyModeCloningCost = 0.7f;
|
public const float EasyModeCloningCost = 0.7f;
|
||||||
@@ -79,10 +81,7 @@ namespace Content.Server.Cloning.Systems
|
|||||||
|
|
||||||
private void OnDeconstruct(EntityUid uid, CloningPodComponent component, MachineDeconstructedEvent args)
|
private void OnDeconstruct(EntityUid uid, CloningPodComponent component, MachineDeconstructedEvent args)
|
||||||
{
|
{
|
||||||
if (!TryComp<MaterialStorageComponent>(uid, out var storage))
|
_serverStackSystem.SpawnMultiple(_material.GetMaterialAmount(uid, "Biomass"), 100, "Biomass", Transform(uid).Coordinates);
|
||||||
return;
|
|
||||||
|
|
||||||
_serverStackSystem.SpawnMultiple(storage.GetMaterialAmount("Biomass"), 100, "Biomass", Transform(uid).Coordinates);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateAppearance(CloningPodComponent clonePod)
|
private void UpdateAppearance(CloningPodComponent clonePod)
|
||||||
@@ -140,8 +139,7 @@ namespace Content.Server.Cloning.Systems
|
|||||||
if (!args.IsInDetailsRange || !_powerReceiverSystem.IsPowered(uid))
|
if (!args.IsInDetailsRange || !_powerReceiverSystem.IsPowered(uid))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (TryComp<MaterialStorageComponent>(uid, out var storage))
|
args.PushMarkup(Loc.GetString("cloning-pod-biomass", ("number", _material.GetMaterialAmount(uid, "Biomass"))));
|
||||||
args.PushMarkup(Loc.GetString("cloning-pod-biomass", ("number", storage.GetMaterialAmount("Biomass"))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryCloning(EntityUid uid, EntityUid bodyToClone, Mind.Mind mind, CloningPodComponent? clonePod)
|
public bool TryCloning(EntityUid uid, EntityUid bodyToClone, Mind.Mind mind, CloningPodComponent? clonePod)
|
||||||
@@ -170,9 +168,6 @@ namespace Content.Server.Cloning.Systems
|
|||||||
if (mind.UserId == null || !_playerManager.TryGetSessionById(mind.UserId.Value, out var client))
|
if (mind.UserId == null || !_playerManager.TryGetSessionById(mind.UserId.Value, out var client))
|
||||||
return false; // If we can't track down the client, we can't offer transfer. That'd be quite bad.
|
return false; // If we can't track down the client, we can't offer transfer. That'd be quite bad.
|
||||||
|
|
||||||
if (!TryComp<MaterialStorageComponent>(clonePod.Owner, out var podStorage))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!TryComp<HumanoidAppearanceComponent>(bodyToClone, out var humanoid))
|
if (!TryComp<HumanoidAppearanceComponent>(bodyToClone, out var humanoid))
|
||||||
return false; // whatever body was to be cloned, was not a humanoid
|
return false; // whatever body was to be cloned, was not a humanoid
|
||||||
|
|
||||||
@@ -188,7 +183,7 @@ namespace Content.Server.Cloning.Systems
|
|||||||
cloningCost = (int) Math.Round(cloningCost * EasyModeCloningCost);
|
cloningCost = (int) Math.Round(cloningCost * EasyModeCloningCost);
|
||||||
|
|
||||||
// biomass checks
|
// biomass checks
|
||||||
var biomassAmount = podStorage.GetMaterialAmount("Biomass");
|
var biomassAmount = _material.GetMaterialAmount(uid, "Biomass");
|
||||||
|
|
||||||
if (biomassAmount < cloningCost)
|
if (biomassAmount < cloningCost)
|
||||||
{
|
{
|
||||||
@@ -197,7 +192,7 @@ namespace Content.Server.Cloning.Systems
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
podStorage.RemoveMaterial("Biomass", cloningCost);
|
_material.TryChangeMaterialAmount(uid, "Biomass", -cloningCost);
|
||||||
clonePod.UsedBiomass = cloningCost;
|
clonePod.UsedBiomass = cloningCost;
|
||||||
// end of biomass checks
|
// end of biomass checks
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
using Content.Shared.Lathe;
|
|
||||||
using Content.Shared.Research.Prototypes;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
|
|
||||||
namespace Content.Server.Lathe.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed class LatheComponent : SharedLatheComponent
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The lathe's construction queue
|
|
||||||
/// </summary>
|
|
||||||
[DataField("queue", customTypeSerializer: typeof(PrototypeIdListSerializer<LatheRecipePrototype>))]
|
|
||||||
public List<string> Queue { get; } = new();
|
|
||||||
// TODO queue serializer.
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// How long the inserting animation will play
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public float InsertionTime = 0.79f; // 0.01 off for animation timing
|
|
||||||
/// <summary>
|
|
||||||
/// Update accumulator for the insertion time
|
|
||||||
/// </suummary>
|
|
||||||
[DataField("insertionAccumulator")]
|
|
||||||
public float InsertionAccumulator = 0f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The sound that plays when the lathe is producing an item, if any
|
|
||||||
/// </summary>
|
|
||||||
[DataField("producingSound")]
|
|
||||||
public SoundSpecifier? ProducingSound;
|
|
||||||
|
|
||||||
/// <summmary>
|
|
||||||
/// The lathe's UI.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables] public BoundUserInterface? UserInterface;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using Content.Shared.Lathe;
|
|
||||||
using Content.Shared.Research.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Lathe.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
[ComponentReference(typeof(SharedLatheDatabaseComponent))]
|
|
||||||
public sealed class LatheDatabaseComponent : SharedLatheDatabaseComponent
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Whether new recipes can be added to this database or not.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("static")]
|
|
||||||
public bool Static { get; private set; } = false;
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState()
|
|
||||||
{
|
|
||||||
return new LatheDatabaseState(GetRecipeIdList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Clear()
|
|
||||||
{
|
|
||||||
if (Static) return;
|
|
||||||
base.Clear();
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void AddRecipe(LatheRecipePrototype recipe)
|
|
||||||
{
|
|
||||||
if (Static) return;
|
|
||||||
base.AddRecipe(recipe);
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool RemoveRecipe(LatheRecipePrototype recipe)
|
|
||||||
{
|
|
||||||
if (Static || !base.RemoveRecipe(recipe)) return false;
|
|
||||||
Dirty();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ namespace Content.Server.Lathe.Components
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For EntityQuery to keep track of which lathes are inserting
|
/// For EntityQuery to keep track of which lathes are inserting
|
||||||
/// <summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class LatheInsertingComponent : Component
|
public sealed class LatheInsertingComponent : Component
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,24 +1,15 @@
|
|||||||
using Content.Shared.Research.Prototypes;
|
namespace Content.Server.Lathe.Components;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
|
||||||
|
|
||||||
namespace Content.Server.Lathe.Components
|
/// <summary>
|
||||||
|
/// For EntityQuery to keep track of which lathes are producing
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class LatheProducingComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For EntityQuery to keep track of which lathes are producing
|
/// How much production time has passed, in seconds.
|
||||||
/// <summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed class LatheProducingComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The recipe the lathe is currently producing
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("recipe", required:true, customTypeSerializer:typeof(PrototypeIdSerializer<LatheRecipePrototype>))]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public string? Recipe;
|
public float AccumulatedTime;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Remaining production time, in seconds.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("timeRemaining", required: true)]
|
|
||||||
public float TimeRemaining;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
using Content.Shared.Lathe;
|
|
||||||
using Content.Shared.Whitelist;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
|
||||||
using Content.Shared.Materials;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
|
|
||||||
namespace Content.Server.Lathe.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
[ComponentReference(typeof(SharedMaterialStorageComponent))]
|
|
||||||
public sealed class MaterialStorageComponent : SharedMaterialStorageComponent
|
|
||||||
{
|
|
||||||
[ViewVariables]
|
|
||||||
protected override Dictionary<string, int> Storage { get; set; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// How much material the storage can store in total.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public int StorageLimit => _storageLimit;
|
|
||||||
[DataField("StorageLimit")]
|
|
||||||
private int _storageLimit = -1;
|
|
||||||
|
|
||||||
/// <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 = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The sound that plays when inserting an item into the storage
|
|
||||||
/// </summary>
|
|
||||||
[DataField("insertingSound")]
|
|
||||||
public SoundSpecifier? InsertingSound;
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState()
|
|
||||||
{
|
|
||||||
return new MaterialStorageState(Storage);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the storage can take a volume of material without surpassing its own limits.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="amount">The volume of material</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool CanTakeAmount(int amount)
|
|
||||||
{
|
|
||||||
return CurrentAmount + amount <= StorageLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if it can insert a material.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">Material ID</param>
|
|
||||||
/// <param name="amount">How much to insert</param>
|
|
||||||
/// <returns>Whether it can insert the material or not.</returns>
|
|
||||||
public bool CanInsertMaterial(string id, int amount)
|
|
||||||
{
|
|
||||||
return (CanTakeAmount(amount) || StorageLimit < 0) && (!Storage.ContainsKey(id) || Storage[id] + amount >= 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Inserts material into the storage.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">Material ID</param>
|
|
||||||
/// <param name="amount">How much to insert</param>
|
|
||||||
/// <returns>Whether it inserted it or not.</returns>
|
|
||||||
public bool InsertMaterial(string id, int amount)
|
|
||||||
{
|
|
||||||
if (!CanInsertMaterial(id, amount)) return false;
|
|
||||||
|
|
||||||
if (!Storage.ContainsKey(id))
|
|
||||||
Storage.Add(id, 0);
|
|
||||||
|
|
||||||
Storage[id] += amount;
|
|
||||||
|
|
||||||
Dirty();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Removes material from the storage.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">Material ID</param>
|
|
||||||
/// <param name="amount">How much to remove</param>
|
|
||||||
/// <returns>Whether it removed it or not.</returns>
|
|
||||||
public bool RemoveMaterial(string id, int amount)
|
|
||||||
{
|
|
||||||
return InsertMaterial(id, -amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// forgive me I needed to write a crumb of e/c code to not go fucking insane i swear i will ecs this entire shitty fucking system one day
|
|
||||||
public int GetMaterialAmount(string id)
|
|
||||||
{
|
|
||||||
if (!Storage.TryGetValue(id, out var amount))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return amount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using Content.Server.Research.Components;
|
|
||||||
using Content.Shared.Lathe;
|
|
||||||
using Content.Shared.Research.Prototypes;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Lathe.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
[ComponentReference(typeof(SharedLatheDatabaseComponent))]
|
|
||||||
public sealed class ProtolatheDatabaseComponent : SharedProtolatheDatabaseComponent
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState()
|
|
||||||
{
|
|
||||||
return new ProtolatheDatabaseState(GetRecipeIdList());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds unlocked recipes from technologies to the database.
|
|
||||||
/// </summary>
|
|
||||||
public void Sync()
|
|
||||||
{
|
|
||||||
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(Owner, out TechnologyDatabaseComponent? database)) return;
|
|
||||||
|
|
||||||
foreach (var technology in database.Technologies)
|
|
||||||
{
|
|
||||||
foreach (var id in technology.UnlockedRecipes)
|
|
||||||
{
|
|
||||||
var recipe = (LatheRecipePrototype) _prototypeManager.Index(typeof(LatheRecipePrototype), id);
|
|
||||||
UnlockRecipe(recipe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Unlocks a recipe but only if it's one of the allowed recipes on this protolathe.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="recipe">The recipe</param>
|
|
||||||
/// <returns>Whether it could add it or not.</returns>
|
|
||||||
public bool UnlockRecipe(LatheRecipePrototype recipe)
|
|
||||||
{
|
|
||||||
if (!ProtolatheRecipes.Contains(recipe)) return false;
|
|
||||||
|
|
||||||
AddRecipe(recipe);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +1,49 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Content.Server.Lathe.Components;
|
using Content.Server.Lathe.Components;
|
||||||
using Content.Shared.Lathe;
|
using Content.Shared.Lathe;
|
||||||
using Content.Shared.Materials;
|
using Content.Shared.Materials;
|
||||||
using Content.Shared.Research.Prototypes;
|
using Content.Shared.Research.Prototypes;
|
||||||
using Content.Server.Research.Components;
|
using Content.Server.Research.Components;
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Server.Materials;
|
|
||||||
using Content.Server.Popups;
|
|
||||||
using Content.Server.Power.EntitySystems;
|
|
||||||
using Content.Server.Research;
|
using Content.Server.Research;
|
||||||
using Content.Server.Stack;
|
|
||||||
using Content.Shared.Research.Components;
|
using Content.Shared.Research.Components;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Player;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Cargo.Systems;
|
using Content.Server.Materials;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Server.UserInterface;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
|
|
||||||
namespace Content.Server.Lathe
|
namespace Content.Server.Lathe
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public sealed class LatheSystem : EntitySystem
|
public sealed class LatheSystem : SharedLatheSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audioSys = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly UserInterfaceSystem _uiSys = default!;
|
[Dependency] private readonly UserInterfaceSystem _uiSys = default!;
|
||||||
[Dependency] private readonly ResearchSystem _researchSys = default!;
|
[Dependency] private readonly ResearchSystem _researchSys = default!;
|
||||||
|
[Dependency] private readonly MaterialStorageSystem _materialStorage = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeLocalEvent<MaterialStorageComponent, InteractUsingEvent>(OnInteractUsing);
|
SubscribeLocalEvent<LatheComponent, MaterialEntityInsertedEvent>(OnMaterialEntityInserted);
|
||||||
SubscribeLocalEvent<LatheComponent, ComponentInit>(OnComponentInit);
|
SubscribeLocalEvent<LatheComponent, GetMaterialWhitelistEvent>(OnGetWhitelist);
|
||||||
|
SubscribeLocalEvent<LatheComponent, MapInitEvent>(OnMapInit);
|
||||||
|
SubscribeLocalEvent<LatheComponent, PowerChangedEvent>(OnPowerChanged);
|
||||||
|
|
||||||
SubscribeLocalEvent<LatheComponent, LatheQueueRecipeMessage>(OnLatheQueueRecipeMessage);
|
SubscribeLocalEvent<LatheComponent, LatheQueueRecipeMessage>(OnLatheQueueRecipeMessage);
|
||||||
SubscribeLocalEvent<LatheComponent, LatheSyncRequestMessage>(OnLatheSyncRequestMessage);
|
SubscribeLocalEvent<LatheComponent, LatheSyncRequestMessage>(OnLatheSyncRequestMessage);
|
||||||
SubscribeLocalEvent<LatheComponent, LatheServerSelectionMessage>(OnLatheServerSelectionMessage);
|
SubscribeLocalEvent<LatheComponent, LatheServerSelectionMessage>(OnLatheServerSelectionMessage);
|
||||||
SubscribeLocalEvent<LatheComponent, PowerChangedEvent>(OnPowerChanged);
|
|
||||||
|
SubscribeLocalEvent<LatheComponent, BeforeActivatableUIOpenEvent>((u,c,_) => UpdateUserInterfaceState(u,c));
|
||||||
|
SubscribeLocalEvent<LatheComponent, MaterialAmountChangedEvent>((u,c,_) => UpdateUserInterfaceState(u,c));
|
||||||
|
|
||||||
|
SubscribeLocalEvent<TechnologyDatabaseComponent, LatheGetRecipesEvent>(OnGetRecipes);
|
||||||
SubscribeLocalEvent<TechnologyDatabaseComponent, LatheServerSyncMessage>(OnLatheServerSyncMessage);
|
SubscribeLocalEvent<TechnologyDatabaseComponent, LatheServerSyncMessage>(OnLatheServerSyncMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,217 +60,167 @@ namespace Content.Server.Lathe
|
|||||||
RemCompDeferred(comp.Owner, comp);
|
RemCompDeferred(comp.Owner, comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var comp in EntityQuery<LatheProducingComponent>())
|
foreach (var (comp, lathe) in EntityQuery<LatheProducingComponent, LatheComponent>())
|
||||||
{
|
{
|
||||||
comp.TimeRemaining -= frameTime;
|
if (lathe.CurrentRecipe == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (comp.TimeRemaining <= 0)
|
comp.AccumulatedTime += frameTime;
|
||||||
FinishProducing(comp.Owner, comp);
|
|
||||||
|
if (comp.AccumulatedTime >= (float) lathe.CurrentRecipe.CompleteTime.TotalSeconds)
|
||||||
|
FinishProducing(comp.Owner, lathe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnGetWhitelist(EntityUid uid, LatheComponent component, GetMaterialWhitelistEvent args)
|
||||||
|
{
|
||||||
|
if (args.Storage != uid)
|
||||||
|
return;
|
||||||
|
var materialWhitelist = new List<string>();
|
||||||
|
var recipes = GetAllBaseRecipes(component);
|
||||||
|
foreach (var id in recipes)
|
||||||
|
{
|
||||||
|
if (!_proto.TryIndex<LatheRecipePrototype>(id, out var proto))
|
||||||
|
continue;
|
||||||
|
foreach (var (mat, _) in proto.RequiredMaterials)
|
||||||
|
{
|
||||||
|
if (!materialWhitelist.Contains(mat))
|
||||||
|
{
|
||||||
|
materialWhitelist.Add(mat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var combined = args.Whitelist.Union(materialWhitelist).ToList();
|
||||||
|
args.Whitelist = combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
[PublicAPI]
|
||||||
|
public bool TryGetAvailableRecipes(EntityUid uid, [NotNullWhen(true)] out List<string>? recipes, LatheComponent? component = null)
|
||||||
|
{
|
||||||
|
recipes = null;
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return false;
|
||||||
|
recipes = GetAvailableRecipes(component);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> GetAvailableRecipes(LatheComponent component)
|
||||||
|
{
|
||||||
|
var ev = new LatheGetRecipesEvent(component.Owner)
|
||||||
|
{
|
||||||
|
Recipes = component.StaticRecipes
|
||||||
|
};
|
||||||
|
RaiseLocalEvent(component.Owner, ev);
|
||||||
|
return ev.Recipes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<string> GetAllBaseRecipes(LatheComponent component)
|
||||||
|
{
|
||||||
|
return component.DynamicRecipes == null
|
||||||
|
? component.StaticRecipes
|
||||||
|
: component.StaticRecipes.Union(component.DynamicRecipes).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryAddToQueue(EntityUid uid, LatheRecipePrototype recipe, LatheComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!CanProduce(uid, recipe, 1, component))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (var (mat, amount) in recipe.RequiredMaterials)
|
||||||
|
{
|
||||||
|
_materialStorage.TryChangeMaterialAmount(uid, mat, -amount);
|
||||||
|
}
|
||||||
|
component.Queue.Add(recipe);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryStartProducing(EntityUid uid, LatheComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return false;
|
||||||
|
if (component.CurrentRecipe != null || component.Queue.Count <= 0 || !this.IsPowered(uid, EntityManager))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var recipe = component.Queue.First();
|
||||||
|
component.Queue.RemoveAt(0);
|
||||||
|
|
||||||
|
EnsureComp<LatheProducingComponent>(uid);
|
||||||
|
component.CurrentRecipe = recipe;
|
||||||
|
|
||||||
|
_audio.PlayPvs(component.ProducingSound, component.Owner);
|
||||||
|
UpdateRunningAppearance(uid, true);
|
||||||
|
UpdateUserInterfaceState(uid, component);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FinishProducing(EntityUid uid, LatheComponent? comp = null, LatheProducingComponent? prodComp = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref comp, ref prodComp, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (comp.CurrentRecipe != null)
|
||||||
|
Spawn(comp.CurrentRecipe.Result, Transform(uid).Coordinates);
|
||||||
|
comp.CurrentRecipe = null;
|
||||||
|
prodComp.AccumulatedTime = 0;
|
||||||
|
|
||||||
|
if (!TryStartProducing(uid, comp))
|
||||||
|
{
|
||||||
|
RemCompDeferred(prodComp.Owner, prodComp);
|
||||||
|
UpdateUserInterfaceState(uid, comp);
|
||||||
|
UpdateRunningAppearance(uid, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateUserInterfaceState(EntityUid uid, LatheComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ui = _uiSys.GetUi(uid, LatheUiKey.Key);
|
||||||
|
var producing = component.CurrentRecipe ?? component.Queue.FirstOrDefault();
|
||||||
|
|
||||||
|
var state = new LatheUpdateState(GetAvailableRecipes(component), component.Queue, producing);
|
||||||
|
_uiSys.SetUiState(ui, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetRecipes(EntityUid uid, TechnologyDatabaseComponent component, LatheGetRecipesEvent args)
|
||||||
|
{
|
||||||
|
if (uid != args.Lathe || !TryComp<LatheComponent>(uid, out var latheComponent) || latheComponent.DynamicRecipes == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//gets all of the techs that are unlocked and also in the DynamicRecipes list
|
||||||
|
var allTechs = (from tech in component.Technologies
|
||||||
|
from recipe in tech.UnlockedRecipes
|
||||||
|
where latheComponent.DynamicRecipes.Contains(recipe)
|
||||||
|
select recipe).ToList();
|
||||||
|
|
||||||
|
args.Recipes = args.Recipes.Union(allTechs).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initialize the UI and appearance.
|
/// Initialize the UI and appearance.
|
||||||
/// Appearance requires initialization or the layers break
|
/// Appearance requires initialization or the layers break
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnComponentInit(EntityUid uid, LatheComponent component, ComponentInit args)
|
private void OnMapInit(EntityUid uid, LatheComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
if (TryComp<AppearanceComponent>(uid, out var appearance))
|
_appearance.SetData(uid, LatheVisuals.IsInserting, false);
|
||||||
{
|
_appearance.SetData(uid, LatheVisuals.IsRunning, false);
|
||||||
appearance.SetData(LatheVisuals.IsInserting, false);
|
|
||||||
appearance.SetData(LatheVisuals.IsRunning, false);
|
_materialStorage.UpdateMaterialWhitelist(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Fix this awful shit once Lathes get ECS'd.
|
private void OnMaterialEntityInserted(EntityUid uid, LatheComponent component, MaterialEntityInsertedEvent args)
|
||||||
List<LatheRecipePrototype>? recipes = null;
|
|
||||||
if (TryComp<ProtolatheDatabaseComponent>(uid, out var database))
|
|
||||||
recipes = database.ProtolatheRecipes.ToList();
|
|
||||||
else if (TryComp<LatheDatabaseComponent>(uid, out var database2))
|
|
||||||
recipes = database2._recipes;
|
|
||||||
|
|
||||||
if (recipes == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!TryComp<MaterialStorageComponent>(uid, out var storage))
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var recipe in recipes)
|
|
||||||
{
|
{
|
||||||
foreach (var mat in recipe.RequiredMaterials)
|
var lastMat = args.Materials.Keys.Last();
|
||||||
{
|
|
||||||
if (!storage.MaterialWhiteList.Contains(mat.Key))
|
|
||||||
storage.MaterialWhiteList.Add(mat.Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnInteractUsing(EntityUid uid, MaterialStorageComponent component, InteractUsingEvent args)
|
|
||||||
{
|
|
||||||
if (args.Handled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!TryComp<MaterialStorageComponent>(uid, out var storage)
|
|
||||||
|| !TryComp<MaterialComponent>(args.Used, out var material)
|
|
||||||
|| storage.EntityWhitelist?.IsValid(args.Used) == false)
|
|
||||||
return;
|
|
||||||
|
|
||||||
args.Handled = true;
|
|
||||||
|
|
||||||
var matUsed = false;
|
|
||||||
foreach (var mat in material.Materials)
|
|
||||||
if (storage.MaterialWhiteList.Contains(mat.ID))
|
|
||||||
matUsed = true;
|
|
||||||
|
|
||||||
if (!matUsed)
|
|
||||||
{
|
|
||||||
_popupSystem.PopupEntity(Loc.GetString("lathe-popup-material-not-used"), uid, Filter.Pvs(uid));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var multiplier = 1;
|
|
||||||
|
|
||||||
if (TryComp<StackComponent>(args.Used, out var stack))
|
|
||||||
multiplier = stack.Count;
|
|
||||||
|
|
||||||
var totalAmount = 0;
|
|
||||||
|
|
||||||
// Check if it can insert all materials.
|
|
||||||
foreach (var (mat, vol) in material._materials)
|
|
||||||
{
|
|
||||||
if (!storage.CanInsertMaterial(mat,
|
|
||||||
vol * multiplier)) return;
|
|
||||||
totalAmount += vol * multiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if it can take ALL of the material's volume.
|
|
||||||
if (storage.StorageLimit > 0 && !storage.CanTakeAmount(totalAmount))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var lastMat = string.Empty;
|
|
||||||
foreach (var (mat, vol) in material._materials)
|
|
||||||
{
|
|
||||||
storage.InsertMaterial(mat, vol * multiplier);
|
|
||||||
lastMat = mat;
|
|
||||||
}
|
|
||||||
|
|
||||||
EntityManager.QueueDeleteEntity(args.Used);
|
|
||||||
|
|
||||||
// Play a sound when inserting, if any
|
|
||||||
if (component.InsertingSound != null)
|
|
||||||
_audioSys.PlayPvs(component.InsertingSound, uid);
|
|
||||||
|
|
||||||
_popupSystem.PopupEntity(Loc.GetString("machine-insert-item", ("machine", uid),
|
|
||||||
("item", args.Used)), uid, Filter.Entities(args.User));
|
|
||||||
|
|
||||||
// TODO: You can probably split this part off of lathe component too
|
|
||||||
if (!TryComp<LatheComponent>(uid, out var lathe))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// We need the prototype to get the color
|
// We need the prototype to get the color
|
||||||
_prototypeManager.TryIndex(lastMat, out MaterialPrototype? matProto);
|
_proto.TryIndex(lastMat, out MaterialPrototype? matProto);
|
||||||
|
EnsureComp<LatheInsertingComponent>(uid).TimeRemaining = component.InsertionTime;
|
||||||
EntityManager.QueueDeleteEntity(args.Used);
|
UpdateInsertingAppearance(uid, true, matProto?.Color);
|
||||||
|
|
||||||
EnsureComp<LatheInsertingComponent>(uid).TimeRemaining = lathe.InsertionTime;
|
|
||||||
|
|
||||||
_popupSystem.PopupEntity(Loc.GetString("machine-insert-item", ("machine", uid),
|
|
||||||
("item", args.Used)), uid, Filter.Entities(args.User));
|
|
||||||
|
|
||||||
if (matProto != null)
|
|
||||||
{
|
|
||||||
UpdateInsertingAppearance(uid, true, matProto.Color);
|
|
||||||
}
|
|
||||||
UpdateInsertingAppearance(uid, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void OnPowerChanged(EntityUid uid, LatheComponent component, PowerChangedEvent args)
|
|
||||||
{
|
|
||||||
//if the power state changes, try to produce.
|
|
||||||
//aka, if you went from unpowered --> powered, resume lathe queue.
|
|
||||||
TryStartProducing(uid, component: component);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This handles the checks to start producing an item, and
|
|
||||||
/// starts up the sound and visuals
|
|
||||||
/// </summary>
|
|
||||||
private bool TryStartProducing(EntityUid uid, LatheProducingComponent? prodComp = null, LatheComponent? component = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref component) || component.Queue.Count == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!this.IsPowered(uid, EntityManager))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var recipeId = component.Queue[0];
|
|
||||||
|
|
||||||
if (!_prototypeManager.TryIndex<LatheRecipePrototype>(recipeId, out var recipe))
|
|
||||||
{
|
|
||||||
// recipie does not exist. Remove and try produce the next item.
|
|
||||||
component.Queue.RemoveAt(0);
|
|
||||||
return TryStartProducing(uid, prodComp, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!component.CanProduce(recipe) || !TryComp(uid, out MaterialStorageComponent? storage))
|
|
||||||
{
|
|
||||||
component.Queue.RemoveAt(0);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
prodComp ??= EnsureComp<LatheProducingComponent>(uid);
|
|
||||||
|
|
||||||
// Do nothing if the lathe is already producing something.
|
|
||||||
if (prodComp.Recipe != null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
component.Queue.RemoveAt(0);
|
|
||||||
prodComp.Recipe = recipeId;
|
|
||||||
prodComp.TimeRemaining = (float)recipe.CompleteTime.TotalSeconds;
|
|
||||||
|
|
||||||
foreach (var (material, amount) in recipe.RequiredMaterials)
|
|
||||||
{
|
|
||||||
// This should always return true, otherwise CanProduce fucked up.
|
|
||||||
// TODO just remove materials when first queuing, to avoid queuing more items than can actually be produced.
|
|
||||||
storage.RemoveMaterial(material, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Again, this should really just be a bui state instead of two separate messages.
|
|
||||||
_uiSys.TrySendUiMessage(uid, LatheUiKey.Key, new LatheProducingRecipeMessage(recipe.ID));
|
|
||||||
_uiSys.TrySendUiMessage(uid, LatheUiKey.Key, new LatheFullQueueMessage(component.Queue));
|
|
||||||
|
|
||||||
if (component.ProducingSound != null)
|
|
||||||
_audioSys.PlayPvs(component.ProducingSound, component.Owner);
|
|
||||||
|
|
||||||
UpdateRunningAppearance(uid, true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If we were able to produce the recipe,
|
|
||||||
/// spawn it and cleanup. If we weren't, just do cleanup.
|
|
||||||
/// </summary>
|
|
||||||
private void FinishProducing(EntityUid uid, LatheProducingComponent prodComp)
|
|
||||||
{
|
|
||||||
if (prodComp.Recipe == null || !_prototypeManager.TryIndex<LatheRecipePrototype>(prodComp.Recipe, out var recipe))
|
|
||||||
{
|
|
||||||
RemCompDeferred(prodComp.Owner, prodComp);
|
|
||||||
UpdateRunningAppearance(uid, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Spawn(recipe.Result, Transform(uid).Coordinates);
|
|
||||||
prodComp.Recipe = null;
|
|
||||||
|
|
||||||
// TODO this should probably just be a BUI state, not a special message.
|
|
||||||
_uiSys.TrySendUiMessage(uid, LatheUiKey.Key, new LatheStoppedProducingRecipeMessage());
|
|
||||||
|
|
||||||
// Continue to next in queue if there are items left
|
|
||||||
if (TryStartProducing(uid, prodComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
RemComp(prodComp.Owner, prodComp);
|
|
||||||
UpdateRunningAppearance(uid, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -274,10 +229,7 @@ namespace Content.Server.Lathe
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateRunningAppearance(EntityUid uid, bool isRunning)
|
private void UpdateRunningAppearance(EntityUid uid, bool isRunning)
|
||||||
{
|
{
|
||||||
if (!TryComp<AppearanceComponent>(uid, out var appearance))
|
_appearance.SetData(uid, LatheVisuals.IsRunning, isRunning);
|
||||||
return;
|
|
||||||
|
|
||||||
appearance.SetData(LatheVisuals.IsRunning, isRunning);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -286,56 +238,62 @@ namespace Content.Server.Lathe
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateInsertingAppearance(EntityUid uid, bool isInserting, Color? color = null)
|
private void UpdateInsertingAppearance(EntityUid uid, bool isInserting, Color? color = null)
|
||||||
{
|
{
|
||||||
if (!TryComp<AppearanceComponent>(uid, out var appearance))
|
_appearance.SetData(uid, LatheVisuals.IsInserting, isInserting);
|
||||||
return;
|
|
||||||
|
|
||||||
appearance.SetData(LatheVisuals.IsInserting, isInserting);
|
|
||||||
if (color != null)
|
if (color != null)
|
||||||
appearance.SetData(LatheVisuals.InsertingColor, color);
|
_appearance.SetData(uid, LatheVisuals.InsertingColor, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPowerChanged(EntityUid uid, LatheComponent component, PowerChangedEvent args)
|
||||||
|
{
|
||||||
|
if (!args.Powered)
|
||||||
|
{
|
||||||
|
RemComp<LatheProducingComponent>(uid);
|
||||||
|
UpdateRunningAppearance(uid, false);
|
||||||
|
}
|
||||||
|
else if (component.CurrentRecipe != null)
|
||||||
|
{
|
||||||
|
EnsureComp<LatheProducingComponent>(uid);
|
||||||
|
TryStartProducing(uid, component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool HasRecipe(EntityUid uid, LatheRecipePrototype recipe, LatheComponent component)
|
||||||
|
{
|
||||||
|
return GetAvailableRecipes(component).Contains(recipe.ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region UI Messages
|
#region UI Messages
|
||||||
|
|
||||||
private void OnLatheQueueRecipeMessage(EntityUid uid, LatheComponent component, LatheQueueRecipeMessage args)
|
private void OnLatheQueueRecipeMessage(EntityUid uid, LatheComponent component, LatheQueueRecipeMessage args)
|
||||||
{
|
{
|
||||||
if (_prototypeManager.TryIndex(args.ID, out LatheRecipePrototype? recipe))
|
if (_proto.TryIndex(args.ID, out LatheRecipePrototype? recipe))
|
||||||
{
|
{
|
||||||
for (var i = 0; i < args.Quantity; i++)
|
for (var i = 0; i < args.Quantity; i++)
|
||||||
{
|
{
|
||||||
// TODO check required materials exist and make materials unavailable.
|
TryAddToQueue(uid, recipe, component);
|
||||||
component.Queue.Add(recipe.ID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Again: TODO this should be handled by BUI states
|
|
||||||
_uiSys.TrySendUiMessage(uid, LatheUiKey.Key, new LatheFullQueueMessage(component.Queue));
|
|
||||||
}
|
}
|
||||||
|
TryStartProducing(uid, component);
|
||||||
TryStartProducing(component.Owner, null, component);
|
UpdateUserInterfaceState(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnLatheSyncRequestMessage(EntityUid uid, LatheComponent component, LatheSyncRequestMessage args)
|
private void OnLatheSyncRequestMessage(EntityUid uid, LatheComponent component, LatheSyncRequestMessage args)
|
||||||
{
|
{
|
||||||
if (!HasComp<MaterialStorageComponent>(uid)) return;
|
UpdateUserInterfaceState(uid, component);
|
||||||
|
|
||||||
// Again: TODO BUI states. Why TF was this was this ever two separate messages!?!?
|
|
||||||
_uiSys.TrySendUiMessage(uid, LatheUiKey.Key, new LatheFullQueueMessage(component.Queue));
|
|
||||||
if (TryComp(uid, out LatheProducingComponent? prodComp) && prodComp.Recipe != null)
|
|
||||||
_uiSys.TrySendUiMessage(uid, LatheUiKey.Key, new LatheProducingRecipeMessage(prodComp.Recipe));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnLatheServerSelectionMessage(EntityUid uid, LatheComponent component, LatheServerSelectionMessage args)
|
private void OnLatheServerSelectionMessage(EntityUid uid, LatheComponent component, LatheServerSelectionMessage args)
|
||||||
{
|
{
|
||||||
// TODO W.. b.. why?
|
// TODO: one day, when you can open BUIs clientside, do that. Until then, picture Electro seething.
|
||||||
// the client can just open the ui itself. why tf is it asking the server to open it for it.
|
if (component.DynamicRecipes != null)
|
||||||
_uiSys.TryOpen(uid, ResearchClientUiKey.Key, (IPlayerSession) args.Session);
|
_uiSys.TryOpen(uid, ResearchClientUiKey.Key, (IPlayerSession) args.Session);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnLatheServerSyncMessage(EntityUid uid, TechnologyDatabaseComponent component, LatheServerSyncMessage args)
|
private void OnLatheServerSyncMessage(EntityUid uid, TechnologyDatabaseComponent component, LatheServerSyncMessage args)
|
||||||
{
|
{
|
||||||
|
Logger.Debug("OnLatheServerSyncMessage");
|
||||||
_researchSys.SyncWithServer(component);
|
_researchSys.SyncWithServer(component);
|
||||||
|
UpdateUserInterfaceState(uid);
|
||||||
if (TryComp(uid, out ProtolatheDatabaseComponent? protoDatabase))
|
|
||||||
protoDatabase.Sync();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
23
Content.Server/Materials/MaterialStorageSystem.cs
Normal file
23
Content.Server/Materials/MaterialStorageSystem.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Content.Shared.Materials;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
|
||||||
|
namespace Content.Server.Materials;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This handles <see cref="SharedMaterialStorageSystem"/>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class MaterialStorageSystem : SharedMaterialStorageSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
|
||||||
|
protected override void OnFinishInsertMaterialEntity(EntityUid toInsert, MaterialStorageComponent component)
|
||||||
|
{
|
||||||
|
_audio.PlayPvs(component.InsertingSound, component.Owner);
|
||||||
|
_popup.PopupEntity(Loc.GetString("machine-insert-item", ("machine", component.Owner),
|
||||||
|
("item", toInsert)), component.Owner, Filter.Pvs(component.Owner));
|
||||||
|
|
||||||
|
QueueDel(toInsert);
|
||||||
|
}
|
||||||
|
}
|
||||||
73
Content.Shared/Lathe/LatheComponent.cs
Normal file
73
Content.Shared/Lathe/LatheComponent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,83 +1,49 @@
|
|||||||
|
using Content.Shared.Research.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Lathe;
|
namespace Content.Shared.Lathe;
|
||||||
|
|
||||||
/// <summary>
|
[Serializable, NetSerializable]
|
||||||
/// Sent to the server to sync material storage and the recipe queue.
|
public sealed class LatheUpdateState : BoundUserInterfaceState
|
||||||
/// </summary>
|
{
|
||||||
[Serializable, NetSerializable]
|
public List<string> Recipes;
|
||||||
public sealed class LatheSyncRequestMessage : BoundUserInterfaceMessage
|
|
||||||
{
|
|
||||||
public LatheSyncRequestMessage()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
public List<LatheRecipePrototype> Queue;
|
||||||
/// 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()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
public LatheRecipePrototype? CurrentlyProducing;
|
||||||
/// Sent to the server to open the ResearchClient UI.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class LatheServerSelectionMessage : BoundUserInterfaceMessage
|
|
||||||
{
|
|
||||||
public LatheServerSelectionMessage()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
public LatheUpdateState(List<string> recipes, List<LatheRecipePrototype> queue, LatheRecipePrototype? currentlyProducing = null)
|
||||||
/// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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 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;
|
Recipes = recipes;
|
||||||
|
Queue = queue;
|
||||||
|
CurrentlyProducing = currentlyProducing;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sent to the server when a client queues a new recipe.
|
/// Sent to the server to sync material storage and the recipe queue.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class LatheQueueRecipeMessage : BoundUserInterfaceMessage
|
public sealed class LatheSyncRequestMessage : BoundUserInterfaceMessage { }
|
||||||
{
|
|
||||||
|
/// <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 to open the ResearchClient UI.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class LatheServerSelectionMessage : 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 string ID;
|
||||||
public readonly int Quantity;
|
public readonly int Quantity;
|
||||||
public LatheQueueRecipeMessage(string id, int quantity)
|
public LatheQueueRecipeMessage(string id, int quantity)
|
||||||
@@ -85,10 +51,10 @@ namespace Content.Shared.Lathe;
|
|||||||
ID = id;
|
ID = id;
|
||||||
Quantity = quantity;
|
Quantity = quantity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[NetSerializable, Serializable]
|
[NetSerializable, Serializable]
|
||||||
public enum LatheUiKey
|
public enum LatheUiKey
|
||||||
{
|
{
|
||||||
Key,
|
Key,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ using Robust.Shared.Serialization;
|
|||||||
|
|
||||||
namespace Content.Shared.Lathe
|
namespace Content.Shared.Lathe
|
||||||
{
|
{
|
||||||
[Serializable, NetSerializable]
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stores bools for if the machine is on
|
/// Stores bools for if the machine is on
|
||||||
/// and if it's currently running and/or inserting.
|
/// and if it's currently running and/or inserting.
|
||||||
/// Used for the visualizer
|
/// Used for the visualizer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
public enum LatheVisuals : byte
|
public enum LatheVisuals : byte
|
||||||
{
|
{
|
||||||
IsRunning,
|
IsRunning,
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
38
Content.Shared/Lathe/SharedLatheSystem.cs
Normal file
38
Content.Shared/Lathe/SharedLatheSystem.cs
Normal 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);
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.Materials;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
||||||
|
|
||||||
namespace Content.Server.Materials
|
namespace Content.Shared.Materials
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Component to store data such as "this object is made out of steel".
|
/// Component to store data such as "this object is made out of steel".
|
||||||
/// This is not a storage system for say smelteries.
|
/// This is not a storage system for say smelteries.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent, NetworkedComponent]
|
||||||
public sealed class MaterialComponent : Component
|
public sealed class MaterialComponent : Component
|
||||||
{
|
{
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
88
Content.Shared/Materials/MaterialStorageComponent.cs
Normal file
88
Content.Shared/Materials/MaterialStorageComponent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
217
Content.Shared/Materials/SharedMaterialStorageSystem.cs
Normal file
217
Content.Shared/Materials/SharedMaterialStorageSystem.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,3 +5,4 @@ lathe-menu-sync = Sync
|
|||||||
lathe-menu-search-designs = Search designs
|
lathe-menu-search-designs = Search designs
|
||||||
lathe-menu-search-filter = Filter
|
lathe-menu-search-filter = Filter
|
||||||
lathe-menu-search-amount = Amount
|
lathe-menu-search-amount = Amount
|
||||||
|
lathe-menu-material-display = {$material} {$amount} cm³
|
||||||
@@ -19,9 +19,6 @@
|
|||||||
- state: panel
|
- state: panel
|
||||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
- type: LatheVisuals
|
|
||||||
idleState: icon
|
|
||||||
runningState: building
|
|
||||||
- type: WiresVisuals
|
- type: WiresVisuals
|
||||||
- type: Physics
|
- type: Physics
|
||||||
bodyType: Static
|
bodyType: Static
|
||||||
@@ -56,9 +53,20 @@
|
|||||||
- type: Wires
|
- type: Wires
|
||||||
BoardName: "Autolathe"
|
BoardName: "Autolathe"
|
||||||
LayoutId: Autolathe
|
LayoutId: Autolathe
|
||||||
- type: LatheDatabase
|
- type: ActivatableUI
|
||||||
static: true
|
key: enum.LatheUiKey.Key
|
||||||
recipes:
|
- type: ActivatableUIRequiresPower
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.LatheUiKey.Key
|
||||||
|
type: LatheBoundUserInterface
|
||||||
|
- type: Transform
|
||||||
|
anchored: true
|
||||||
|
- type: Pullable
|
||||||
|
- type: Lathe
|
||||||
|
idleState: icon
|
||||||
|
runningState: building
|
||||||
|
staticRecipes:
|
||||||
- Wirecutter
|
- Wirecutter
|
||||||
- Screwdriver
|
- Screwdriver
|
||||||
- Welder
|
- Welder
|
||||||
@@ -74,17 +82,6 @@
|
|||||||
- TRayScanner
|
- TRayScanner
|
||||||
- GasAnalyzer
|
- GasAnalyzer
|
||||||
- UtilityBelt
|
- UtilityBelt
|
||||||
- type: ActivatableUI
|
|
||||||
key: enum.LatheUiKey.Key
|
|
||||||
- type: ActivatableUIRequiresPower
|
|
||||||
- type: UserInterface
|
|
||||||
interfaces:
|
|
||||||
- key: enum.LatheUiKey.Key
|
|
||||||
type: LatheBoundUserInterface
|
|
||||||
- type: Transform
|
|
||||||
anchored: true
|
|
||||||
- type: Pullable
|
|
||||||
- type: Lathe
|
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 800
|
price: 800
|
||||||
|
|
||||||
@@ -109,9 +106,6 @@
|
|||||||
- state: panel
|
- state: panel
|
||||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
- type: LatheVisuals
|
|
||||||
idleState: icon
|
|
||||||
runningState: building
|
|
||||||
- type: WiresVisuals
|
- type: WiresVisuals
|
||||||
- type: Physics
|
- type: Physics
|
||||||
bodyType: Static
|
bodyType: Static
|
||||||
@@ -148,8 +142,22 @@
|
|||||||
- Sheet
|
- Sheet
|
||||||
- RawMaterial
|
- RawMaterial
|
||||||
- Ingot
|
- Ingot
|
||||||
- type: ProtolatheDatabase
|
- type: ActivatableUI
|
||||||
protolatherecipes:
|
key: enum.LatheUiKey.Key #Yes only having 1 of them here doesn't break anything
|
||||||
|
- type: ActivatableUIRequiresPower
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.LatheUiKey.Key
|
||||||
|
type: LatheBoundUserInterface
|
||||||
|
- key: enum.ResearchClientUiKey.Key
|
||||||
|
type: ResearchClientBoundUserInterface
|
||||||
|
- type: Transform
|
||||||
|
anchored: true
|
||||||
|
- type: Pullable
|
||||||
|
- type: Lathe
|
||||||
|
idleState: icon
|
||||||
|
runningState: building
|
||||||
|
dynamicRecipes:
|
||||||
- LightTube
|
- LightTube
|
||||||
- LightBulb
|
- LightBulb
|
||||||
- SheetSteel
|
- SheetSteel
|
||||||
@@ -210,19 +218,6 @@
|
|||||||
- LeftArmBorg
|
- LeftArmBorg
|
||||||
- RightArmBorg
|
- RightArmBorg
|
||||||
- HolofanProjector
|
- HolofanProjector
|
||||||
- type: ActivatableUI
|
|
||||||
key: enum.LatheUiKey.Key #Yes only having 1 of them here doesn't break anything
|
|
||||||
- type: ActivatableUIRequiresPower
|
|
||||||
- type: UserInterface
|
|
||||||
interfaces:
|
|
||||||
- key: enum.LatheUiKey.Key
|
|
||||||
type: LatheBoundUserInterface
|
|
||||||
- key: enum.ResearchClientUiKey.Key
|
|
||||||
type: ResearchClientBoundUserInterface
|
|
||||||
- type: Transform
|
|
||||||
anchored: true
|
|
||||||
- type: Pullable
|
|
||||||
- type: Lathe
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: Protolathe
|
parent: Protolathe
|
||||||
@@ -241,8 +236,13 @@
|
|||||||
map: ["enum.PowerDeviceVisualLayers.Powered"]
|
map: ["enum.PowerDeviceVisualLayers.Powered"]
|
||||||
- state: panel
|
- state: panel
|
||||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||||
- type: ProtolatheDatabase
|
- type: Machine
|
||||||
protolatherecipes:
|
board: CircuitImprinterMachineCircuitboard
|
||||||
|
- type: Lathe
|
||||||
|
producingSound: /Audio/Machines/circuitprinter.ogg
|
||||||
|
idleState: icon
|
||||||
|
runningState: building
|
||||||
|
dynamicRecipes:
|
||||||
- SMESMachineCircuitboard
|
- SMESMachineCircuitboard
|
||||||
- SubstationMachineCircuitboard
|
- SubstationMachineCircuitboard
|
||||||
- ThermomachineFreezerMachineCircuitBoard
|
- ThermomachineFreezerMachineCircuitBoard
|
||||||
@@ -282,10 +282,6 @@
|
|||||||
- WallmountSubstationElectronics
|
- WallmountSubstationElectronics
|
||||||
- EmitterCircuitboard
|
- EmitterCircuitboard
|
||||||
- GasRecyclerMachineCircuitboard
|
- GasRecyclerMachineCircuitboard
|
||||||
- type: Machine
|
|
||||||
board: CircuitImprinterMachineCircuitboard
|
|
||||||
- type: Lathe
|
|
||||||
producingSound: /Audio/Machines/circuitprinter.ogg
|
|
||||||
- type: MaterialStorage
|
- type: MaterialStorage
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
@@ -312,12 +308,12 @@
|
|||||||
map: ["enum.LatheVisualLayers.IsInserting"]
|
map: ["enum.LatheVisualLayers.IsInserting"]
|
||||||
- state: panel
|
- state: panel
|
||||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||||
- type: LatheVisuals
|
- type: Machine
|
||||||
|
board: SecurityTechFabCircuitboard
|
||||||
|
- type: Lathe
|
||||||
idleState: icon
|
idleState: icon
|
||||||
runningState: icon
|
runningState: icon
|
||||||
- type: LatheDatabase
|
dynamicRecipes:
|
||||||
static: true
|
|
||||||
recipes:
|
|
||||||
- Flash
|
- Flash
|
||||||
- FlashPayload
|
- FlashPayload
|
||||||
- Handcuffs
|
- Handcuffs
|
||||||
@@ -335,9 +331,6 @@
|
|||||||
- CartridgeCaselessRifleRubber
|
- CartridgeCaselessRifleRubber
|
||||||
- CartridgeLightRifleRubber
|
- CartridgeLightRifleRubber
|
||||||
- CartridgeRifleRubber
|
- CartridgeRifleRubber
|
||||||
- type: Machine
|
|
||||||
board: SecurityTechFabCircuitboard
|
|
||||||
- type: Lathe
|
|
||||||
- type: MaterialStorage
|
- type: MaterialStorage
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
@@ -365,11 +358,10 @@
|
|||||||
map: ["enum.LatheVisualLayers.IsInserting"]
|
map: ["enum.LatheVisualLayers.IsInserting"]
|
||||||
- state: panel
|
- state: panel
|
||||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||||
- type: LatheVisuals
|
- type: Lathe
|
||||||
idleState: icon
|
idleState: icon
|
||||||
runningState: icon
|
runningState: building
|
||||||
- type: ProtolatheDatabase
|
dynamicRecipes:
|
||||||
protolatherecipes:
|
|
||||||
- HandheldHealthAnalyzer
|
- HandheldHealthAnalyzer
|
||||||
- ClothingHandsGlovesLatex
|
- ClothingHandsGlovesLatex
|
||||||
- ClothingHandsGlovesNitrile
|
- ClothingHandsGlovesNitrile
|
||||||
@@ -401,9 +393,20 @@
|
|||||||
components:
|
components:
|
||||||
- type: Transform
|
- type: Transform
|
||||||
noRot: false
|
noRot: false
|
||||||
- type: LatheDatabase
|
- type: Sprite
|
||||||
static: true
|
sprite: Structures/Machines/uniform_printer.rsi
|
||||||
recipes:
|
netsync: false
|
||||||
|
snapCardinals: false
|
||||||
|
layers:
|
||||||
|
- state: icon
|
||||||
|
map: ["enum.LatheVisualLayers.IsRunning"]
|
||||||
|
- type: Machine
|
||||||
|
board: UniformPrinterMachineCircuitboard
|
||||||
|
- type: Lathe
|
||||||
|
producingSound: /Audio/Machines/uniformprinter.ogg
|
||||||
|
idleState: icon
|
||||||
|
runningState: building
|
||||||
|
staticRecipes:
|
||||||
- ClothingUniformJumpsuitColorGrey
|
- ClothingUniformJumpsuitColorGrey
|
||||||
- ClothingUniformJumpskirtColorGrey
|
- ClothingUniformJumpskirtColorGrey
|
||||||
- ClothingUniformJumpsuitBartender
|
- ClothingUniformJumpsuitBartender
|
||||||
@@ -468,17 +471,6 @@
|
|||||||
- ClothingOuterWinterMusician
|
- ClothingOuterWinterMusician
|
||||||
- ClothingOuterWinterClown
|
- ClothingOuterWinterClown
|
||||||
- ClothingOuterWinterMime
|
- ClothingOuterWinterMime
|
||||||
- type: Sprite
|
|
||||||
sprite: Structures/Machines/uniform_printer.rsi
|
|
||||||
netsync: false
|
|
||||||
snapCardinals: false
|
|
||||||
layers:
|
|
||||||
- state: icon
|
|
||||||
map: ["enum.LatheVisualLayers.IsRunning"]
|
|
||||||
- type: Machine
|
|
||||||
board: UniformPrinterMachineCircuitboard
|
|
||||||
- type: Lathe
|
|
||||||
producingSound: /Audio/Machines/uniformprinter.ogg
|
|
||||||
- type: MaterialStorage
|
- type: MaterialStorage
|
||||||
whitelist:
|
whitelist:
|
||||||
tags:
|
tags:
|
||||||
@@ -492,8 +484,6 @@
|
|||||||
name: ore processor
|
name: ore processor
|
||||||
description: It produces sheets and ingots using ores.
|
description: It produces sheets and ingots using ores.
|
||||||
components:
|
components:
|
||||||
- type: LatheVisuals
|
|
||||||
ignoreColor: true
|
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Machines/ore_processor.rsi
|
sprite: Structures/Machines/ore_processor.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
@@ -509,9 +499,13 @@
|
|||||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||||
- type: Machine
|
- type: Machine
|
||||||
board: OreProcessorMachineCircuitboard
|
board: OreProcessorMachineCircuitboard
|
||||||
- type: LatheDatabase
|
- type: MaterialStorage
|
||||||
static: true
|
whitelist:
|
||||||
recipes:
|
tags:
|
||||||
|
- Ore
|
||||||
|
- type: Lathe
|
||||||
|
ignoreColor: true
|
||||||
|
staticRecipes:
|
||||||
- SheetSteel30
|
- SheetSteel30
|
||||||
- SheetGlass30
|
- SheetGlass30
|
||||||
- SheetRGlass30
|
- SheetRGlass30
|
||||||
@@ -521,8 +515,3 @@
|
|||||||
- SheetUranium1
|
- SheetUranium1
|
||||||
- IngotGold1
|
- IngotGold1
|
||||||
- IngotSilver1
|
- IngotSilver1
|
||||||
- type: MaterialStorage
|
|
||||||
whitelist:
|
|
||||||
tags:
|
|
||||||
- Ore
|
|
||||||
- type: Lathe
|
|
||||||
|
|||||||
Reference in New Issue
Block a user