Add support for printing reagents in lathes (#30476)

* Add support for reagents in lathes

* missing locale
This commit is contained in:
Nemanja
2024-08-01 00:15:05 -04:00
committed by GitHub
parent 4b7325098a
commit 2c26be606f
17 changed files with 239 additions and 139 deletions

View File

@@ -100,11 +100,9 @@
Margin="5 0 0 0" Margin="5 0 0 0"
Text="{Loc 'lathe-menu-fabricating-message'}"> Text="{Loc 'lathe-menu-fabricating-message'}">
</Label> </Label>
<EntityPrototypeView <BoxContainer Name="FabricatingDisplayContainer"
Name="FabricatingEntityProto"
HorizontalAlignment="Left" HorizontalAlignment="Left"
Margin="100 0 0 0" Margin="100 0 0 0"/>
/>
<Label <Label
Name="NameLabel" Name="NameLabel"
RectClipContent="True" RectClipContent="True"

View File

@@ -1,18 +1,15 @@
using System.Buffers;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Content.Client.Materials; using Content.Client.Materials;
using Content.Shared.Lathe; using Content.Shared.Lathe;
using Content.Shared.Lathe.Prototypes; using Content.Shared.Lathe.Prototypes;
using Content.Shared.Materials;
using Content.Shared.Research.Prototypes; using Content.Shared.Research.Prototypes;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Client.ResourceManagement;
using Robust.Client.Graphics;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Client.Lathe.UI; namespace Content.Client.Lathe.UI;
@@ -92,7 +89,7 @@ public sealed partial class LatheMenu : DefaultWindow
if (SearchBar.Text.Trim().Length != 0) if (SearchBar.Text.Trim().Length != 0)
{ {
if (proto.Name.ToLowerInvariant().Contains(SearchBar.Text.Trim().ToLowerInvariant())) if (_lathe.GetRecipeName(recipe).ToLowerInvariant().Contains(SearchBar.Text.Trim().ToLowerInvariant()))
recipesToShow.Add(proto); recipesToShow.Add(proto);
} }
else else
@@ -104,19 +101,15 @@ public sealed partial class LatheMenu : DefaultWindow
if (!int.TryParse(AmountLineEdit.Text, out var quantity) || quantity <= 0) if (!int.TryParse(AmountLineEdit.Text, out var quantity) || quantity <= 0)
quantity = 1; quantity = 1;
var sortedRecipesToShow = recipesToShow.OrderBy(p => p.Name); var sortedRecipesToShow = recipesToShow.OrderBy(_lathe.GetRecipeName);
RecipeList.Children.Clear(); RecipeList.Children.Clear();
_entityManager.TryGetComponent(Entity, out LatheComponent? lathe); _entityManager.TryGetComponent(Entity, out LatheComponent? lathe);
foreach (var prototype in sortedRecipesToShow) foreach (var prototype in sortedRecipesToShow)
{ {
EntityPrototype? recipeProto = null;
if (_prototypeManager.TryIndex(prototype.Result, out EntityPrototype? entityProto))
recipeProto = entityProto;
var canProduce = _lathe.CanProduce(Entity, prototype, quantity, component: lathe); var canProduce = _lathe.CanProduce(Entity, prototype, quantity, component: lathe);
var control = new RecipeControl(prototype, () => GenerateTooltipText(prototype), canProduce, recipeProto); var control = new RecipeControl(_lathe, prototype, () => GenerateTooltipText(prototype), canProduce, GetRecipeDisplayControl(prototype));
control.OnButtonPressed += s => control.OnButtonPressed += s =>
{ {
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount <= 0) if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount <= 0)
@@ -132,9 +125,9 @@ public sealed partial class LatheMenu : DefaultWindow
StringBuilder sb = new(); StringBuilder sb = new();
var multiplier = _entityManager.GetComponent<LatheComponent>(Entity).MaterialUseMultiplier; var multiplier = _entityManager.GetComponent<LatheComponent>(Entity).MaterialUseMultiplier;
foreach (var (id, amount) in prototype.RequiredMaterials) foreach (var (id, amount) in prototype.Materials)
{ {
if (!_prototypeManager.TryIndex<MaterialPrototype>(id, out var proto)) if (!_prototypeManager.TryIndex(id, out var proto))
continue; continue;
var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, multiplier); var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, multiplier);
@@ -163,8 +156,9 @@ public sealed partial class LatheMenu : DefaultWindow
sb.AppendLine(tooltipText); sb.AppendLine(tooltipText);
} }
if (!string.IsNullOrWhiteSpace(prototype.Description)) var desc = _lathe.GetRecipeDescription(prototype);
sb.AppendLine(Loc.GetString("lathe-menu-description-display", ("description", prototype.Description))); if (!string.IsNullOrWhiteSpace(desc))
sb.AppendLine(Loc.GetString("lathe-menu-description-display", ("description", desc)));
// Remove last newline // Remove last newline
if (sb.Length > 0) if (sb.Length > 0)
@@ -222,13 +216,10 @@ public sealed partial class LatheMenu : DefaultWindow
var queuedRecipeBox = new BoxContainer(); var queuedRecipeBox = new BoxContainer();
queuedRecipeBox.Orientation = BoxContainer.LayoutOrientation.Horizontal; queuedRecipeBox.Orientation = BoxContainer.LayoutOrientation.Horizontal;
var queuedRecipeProto = new EntityPrototypeView(); queuedRecipeBox.AddChild(GetRecipeDisplayControl(recipe));
queuedRecipeBox.AddChild(queuedRecipeProto);
if (_prototypeManager.TryIndex(recipe.Result, out EntityPrototype? entityProto) && entityProto != null)
queuedRecipeProto.SetPrototype(entityProto);
var queuedRecipeLabel = new Label(); var queuedRecipeLabel = new Label();
queuedRecipeLabel.Text = $"{idx}. {recipe.Name}"; queuedRecipeLabel.Text = $"{idx}. {_lathe.GetRecipeName(recipe)}";
queuedRecipeBox.AddChild(queuedRecipeLabel); queuedRecipeBox.AddChild(queuedRecipeLabel);
QueueList.AddChild(queuedRecipeBox); QueueList.AddChild(queuedRecipeBox);
idx++; idx++;
@@ -241,10 +232,29 @@ public sealed partial class LatheMenu : DefaultWindow
if (recipe == null) if (recipe == null)
return; return;
if (_prototypeManager.TryIndex(recipe.Result, out EntityPrototype? entityProto) && entityProto != null) FabricatingDisplayContainer.Children.Clear();
FabricatingEntityProto.SetPrototype(entityProto); FabricatingDisplayContainer.AddChild(GetRecipeDisplayControl(recipe));
NameLabel.Text = $"{recipe.Name}"; NameLabel.Text = _lathe.GetRecipeName(recipe);
}
public Control GetRecipeDisplayControl(LatheRecipePrototype recipe)
{
if (recipe.Icon != null)
{
var textRect = new TextureRect();
textRect.Texture = _spriteSystem.Frame0(recipe.Icon);
return textRect;
}
if (recipe.Result is { } result)
{
var entProtoView = new EntityPrototypeView();
entProtoView.SetPrototype(result);
return entProtoView;
}
return new Control();
} }
private void OnItemSelected(OptionButton.ItemSelectedEventArgs obj) private void OnItemSelected(OptionButton.ItemSelectedEventArgs obj)

View File

@@ -5,8 +5,8 @@
Margin="0" Margin="0"
StyleClasses="ButtonSquare"> StyleClasses="ButtonSquare">
<BoxContainer Orientation="Horizontal"> <BoxContainer Orientation="Horizontal">
<EntityPrototypeView <BoxContainer
Name="RecipePrototype" Name="RecipeDisplayContainer"
Margin="0 0 4 0" Margin="0 0 4 0"
HorizontalAlignment="Center" HorizontalAlignment="Center"
VerticalAlignment="Center" VerticalAlignment="Center"

View File

@@ -1,10 +1,7 @@
using Content.Shared.Research.Prototypes; using Content.Shared.Research.Prototypes;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Lathe.UI; namespace Content.Client.Lathe.UI;
@@ -14,13 +11,12 @@ public sealed partial class RecipeControl : Control
public Action<string>? OnButtonPressed; public Action<string>? OnButtonPressed;
public Func<string> TooltipTextSupplier; public Func<string> TooltipTextSupplier;
public RecipeControl(LatheRecipePrototype recipe, Func<string> tooltipTextSupplier, bool canProduce, EntityPrototype? entityPrototype = null) public RecipeControl(LatheSystem latheSystem, LatheRecipePrototype recipe, Func<string> tooltipTextSupplier, bool canProduce, Control displayControl)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
RecipeName.Text = recipe.Name; RecipeName.Text = latheSystem.GetRecipeName(recipe);
if (entityPrototype != null) RecipeDisplayContainer.AddChild(displayControl);
RecipePrototype.SetPrototype(entityPrototype);
Button.Disabled = !canProduce; Button.Disabled = !canProduce;
TooltipTextSupplier = tooltipTextSupplier; TooltipTextSupplier = tooltipTextSupplier;
Button.TooltipSupplier = SupplyTooltip; Button.TooltipSupplier = SupplyTooltip;

View File

@@ -193,7 +193,7 @@ public sealed class MaterialArbitrageTest
{ {
foreach (var recipe in recipes) foreach (var recipe in recipes)
{ {
foreach (var (matId, amount) in recipe.RequiredMaterials) foreach (var (matId, amount) in recipe.Materials)
{ {
var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier); var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier);
if (spawnedMats.TryGetValue(matId, out var numSpawned)) if (spawnedMats.TryGetValue(matId, out var numSpawned))
@@ -273,7 +273,7 @@ public sealed class MaterialArbitrageTest
{ {
foreach (var recipe in recipes) foreach (var recipe in recipes)
{ {
foreach (var (matId, amount) in recipe.RequiredMaterials) foreach (var (matId, amount) in recipe.Materials)
{ {
var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier); var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier);
if (deconstructedMats.TryGetValue(matId, out var numSpawned)) if (deconstructedMats.TryGetValue(matId, out var numSpawned))
@@ -328,7 +328,7 @@ public sealed class MaterialArbitrageTest
{ {
foreach (var recipe in recipes) foreach (var recipe in recipes)
{ {
foreach (var (matId, amount) in recipe.RequiredMaterials) foreach (var (matId, amount) in recipe.Materials)
{ {
var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier); var actualAmount = SharedLatheSystem.AdjustMaterial(amount, recipe.ApplyMaterialDiscount, multiplier);
if (compositionComponent.MaterialComposition.TryGetValue(matId, out var numSpawned)) if (compositionComponent.MaterialComposition.TryGetValue(matId, out var numSpawned))

View File

@@ -16,6 +16,7 @@ using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Linq; using System.Linq;
using Content.Shared.Research.Prototypes;
namespace Content.Server.Cargo.Systems; namespace Content.Server.Cargo.Systems;
@@ -158,6 +159,26 @@ public sealed class PricingSystem : EntitySystem
return price; return price;
} }
public double GetLatheRecipePrice(LatheRecipePrototype recipe)
{
var price = 0.0;
if (recipe.Result is { } result)
{
price += GetEstimatedPrice(_prototypeManager.Index(result));
}
if (recipe.ResultReagents is { } resultReagents)
{
foreach (var (reagent, amount) in resultReagents)
{
price += (_prototypeManager.Index(reagent).PricePerUnit * amount).Double();
}
}
return price;
}
/// <summary> /// <summary>
/// Get a rough price for an entityprototype. Does not consider contained entities. /// Get a rough price for an entityprototype. Does not consider contained entities.
/// </summary> /// </summary>

View File

@@ -1,23 +1,29 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Atmos;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Fluids.EntitySystems;
using Content.Server.Lathe.Components; using Content.Server.Lathe.Components;
using Content.Server.Materials; using Content.Server.Materials;
using Content.Server.Popups;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Server.Stack; using Content.Server.Stack;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.UserInterface; using Content.Shared.UserInterface;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Emag.Components; using Content.Shared.Emag.Components;
using Content.Shared.Examine;
using Content.Shared.Lathe; using Content.Shared.Lathe;
using Content.Shared.Materials; using Content.Shared.Materials;
using Content.Shared.ReagentSpeed; using Content.Shared.ReagentSpeed;
using Content.Shared.Research.Components; using Content.Shared.Research.Components;
using Content.Shared.Research.Prototypes; using Content.Shared.Research.Prototypes;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.Containers;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -34,9 +40,13 @@ namespace Content.Server.Lathe
[Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly AtmosphereSystem _atmosphere = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly UserInterfaceSystem _uiSys = default!; [Dependency] private readonly UserInterfaceSystem _uiSys = default!;
[Dependency] private readonly MaterialStorageSystem _materialStorage = default!; [Dependency] private readonly MaterialStorageSystem _materialStorage = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly PuddleSystem _puddle = default!;
[Dependency] private readonly ReagentSpeedSystem _reagentSpeed = default!; [Dependency] private readonly ReagentSpeedSystem _reagentSpeed = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solution = default!;
[Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly StackSystem _stack = default!;
[Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly TransformSystem _transform = default!;
@@ -63,7 +73,6 @@ namespace Content.Server.Lathe
SubscribeLocalEvent<EmagLatheRecipesComponent, LatheGetRecipesEvent>(GetEmagLatheRecipes); SubscribeLocalEvent<EmagLatheRecipesComponent, LatheGetRecipesEvent>(GetEmagLatheRecipes);
SubscribeLocalEvent<LatheHeatProducingComponent, LatheStartPrintingEvent>(OnHeatStartPrinting); SubscribeLocalEvent<LatheHeatProducingComponent, LatheStartPrintingEvent>(OnHeatStartPrinting);
} }
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
var query = EntityQueryEnumerator<LatheProducingComponent, LatheComponent>(); var query = EntityQueryEnumerator<LatheProducingComponent, LatheComponent>();
@@ -119,7 +128,7 @@ namespace Content.Server.Lathe
{ {
if (!_proto.TryIndex(id, out var proto)) if (!_proto.TryIndex(id, out var proto))
continue; continue;
foreach (var (mat, _) in proto.RequiredMaterials) foreach (var (mat, _) in proto.Materials)
{ {
if (!materialWhitelist.Contains(mat)) if (!materialWhitelist.Contains(mat))
{ {
@@ -165,7 +174,7 @@ namespace Content.Server.Lathe
if (!CanProduce(uid, recipe, 1, component)) if (!CanProduce(uid, recipe, 1, component))
return false; return false;
foreach (var (mat, amount) in recipe.RequiredMaterials) foreach (var (mat, amount) in recipe.Materials)
{ {
var adjustedAmount = recipe.ApplyMaterialDiscount var adjustedAmount = recipe.ApplyMaterialDiscount
? (int) (-amount * component.MaterialUseMultiplier) ? (int) (-amount * component.MaterialUseMultiplier)
@@ -211,10 +220,33 @@ namespace Content.Server.Lathe
if (comp.CurrentRecipe != null) if (comp.CurrentRecipe != null)
{ {
var result = Spawn(comp.CurrentRecipe.Result, Transform(uid).Coordinates); if (comp.CurrentRecipe.Result is { } resultProto)
{
var result = Spawn(resultProto, Transform(uid).Coordinates);
_stack.TryMergeToContacts(result); _stack.TryMergeToContacts(result);
} }
if (comp.CurrentRecipe.ResultReagents is { } resultReagents &&
comp.ReagentOutputSlotId is { } slotId)
{
var toAdd = new Solution(
resultReagents.Select(p => new ReagentQuantity(p.Key.Id, p.Value, null)));
// dispense it in the container if we have it and dump it if we don't
if (_container.TryGetContainer(uid, slotId, out var container) &&
container.ContainedEntities.Count == 1 &&
_solution.TryGetFitsInDispenser(container.ContainedEntities.First(), out var solution, out _))
{
_solution.AddSolution(solution.Value, toAdd);
}
else
{
_popup.PopupEntity(Loc.GetString("lathe-reagent-dispense-no-container", ("name", uid)), uid);
_puddle.TrySpillAt(uid, toAdd, out _);
}
}
}
comp.CurrentRecipe = null; comp.CurrentRecipe = null;
prodComp.StartTime = _timing.CurTime; prodComp.StartTime = _timing.CurTime;
@@ -327,6 +359,7 @@ namespace Content.Server.Lathe
{ {
return GetAvailableRecipes(uid, component).Contains(recipe.ID); return GetAvailableRecipes(uid, 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)
@@ -343,8 +376,9 @@ namespace Content.Server.Lathe
} }
if (count > 0) if (count > 0)
{ {
_adminLogger.Add(LogType.Action, LogImpact.Low, _adminLogger.Add(LogType.Action,
$"{ToPrettyString(args.Actor):player} queued {count} {recipe.Name} at {ToPrettyString(uid):lathe}"); LogImpact.Low,
$"{ToPrettyString(args.Actor):player} queued {count} {GetRecipeName(recipe)} at {ToPrettyString(uid):lathe}");
} }
} }
TryStartProducing(uid, component); TryStartProducing(uid, component);

View File

@@ -7,7 +7,6 @@ using Content.Server.Item;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Item; using Content.Shared.Item;
using Content.Shared.Materials;
using Content.Shared.Research.Prototypes; using Content.Shared.Research.Prototypes;
using Content.Shared.UserInterface; using Content.Shared.UserInterface;
using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Melee;
@@ -224,13 +223,13 @@ public sealed class StatValuesCommand : IConsoleCommand
{ {
var cost = 0.0; var cost = 0.0;
foreach (var (material, count) in proto.RequiredMaterials) foreach (var (material, count) in proto.Materials)
{ {
var materialPrice = _proto.Index<MaterialPrototype>(material).Price; var materialPrice = _proto.Index(material).Price;
cost += materialPrice * count; cost += materialPrice * count;
} }
var sell = priceSystem.GetEstimatedPrice(_proto.Index<EntityPrototype>(proto.Result)); var sell = priceSystem.GetLatheRecipePrice(proto);
values.Add(new[] values.Add(new[]
{ {

View File

@@ -81,9 +81,9 @@ namespace Content.Shared.Construction
{ {
var partRecipe = recipes[0]; var partRecipe = recipes[0];
if (recipes.Count > 1) if (recipes.Count > 1)
partRecipe = recipes.MinBy(p => p.RequiredMaterials.Values.Sum()); partRecipe = recipes.MinBy(p => p.Materials.Values.Sum());
foreach (var (mat, matAmount) in partRecipe!.RequiredMaterials) foreach (var (mat, matAmount) in partRecipe!.Materials)
{ {
materials.TryAdd(mat, 0); materials.TryAdd(mat, 0);
materials[mat] += matAmount * amount * coefficient; materials[mat] += matAmount * amount * coefficient;
@@ -101,9 +101,9 @@ namespace Content.Shared.Construction
{ {
var partRecipe = recipes[0]; var partRecipe = recipes[0];
if (recipes.Count > 1) if (recipes.Count > 1)
partRecipe = recipes.MinBy(p => p.RequiredMaterials.Values.Sum()); partRecipe = recipes.MinBy(p => p.Materials.Values.Sum());
foreach (var (mat, matAmount) in partRecipe!.RequiredMaterials) foreach (var (mat, matAmount) in partRecipe!.Materials)
{ {
materials.TryAdd(mat, 0); materials.TryAdd(mat, 0);
materials[mat] += matAmount * amount * coefficient; materials[mat] += matAmount * amount * coefficient;

View File

@@ -33,6 +33,9 @@ namespace Content.Shared.Lathe
[DataField] [DataField]
public SoundSpecifier? ProducingSound; public SoundSpecifier? ProducingSound;
[DataField]
public string? ReagentOutputSlotId;
#region Visualizer info #region Visualizer info
[DataField] [DataField]
public string? IdleState; public string? IdleState;

View File

@@ -1,5 +1,8 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.Emag.Systems; using Content.Shared.Emag.Systems;
using Content.Shared.Examine;
using Content.Shared.Localizations;
using Content.Shared.Materials; using Content.Shared.Materials;
using Content.Shared.Research.Prototypes; using Content.Shared.Research.Prototypes;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -23,10 +26,21 @@ public abstract class SharedLatheSystem : EntitySystem
base.Initialize(); base.Initialize();
SubscribeLocalEvent<EmagLatheRecipesComponent, GotEmaggedEvent>(OnEmagged); SubscribeLocalEvent<EmagLatheRecipesComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<LatheComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded); SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
BuildInverseRecipeDictionary(); BuildInverseRecipeDictionary();
} }
private void OnExamined(Entity<LatheComponent> ent, ref ExaminedEvent args)
{
if (!args.IsInDetailsRange)
return;
if (ent.Comp.ReagentOutputSlotId != null)
args.PushMarkup(Loc.GetString("lathe-menu-reagent-slot-examine"));
}
[PublicAPI] [PublicAPI]
public bool CanProduce(EntityUid uid, string recipe, int amount = 1, LatheComponent? component = null) public bool CanProduce(EntityUid uid, string recipe, int amount = 1, LatheComponent? component = null)
{ {
@@ -40,7 +54,7 @@ public abstract class SharedLatheSystem : EntitySystem
if (!HasRecipe(uid, recipe, component)) if (!HasRecipe(uid, recipe, component))
return false; return false;
foreach (var (material, needed) in recipe.RequiredMaterials) foreach (var (material, needed) in recipe.Materials)
{ {
var adjustedAmount = AdjustMaterial(needed, recipe.ApplyMaterialDiscount, component.MaterialUseMultiplier); var adjustedAmount = AdjustMaterial(needed, recipe.ApplyMaterialDiscount, component.MaterialUseMultiplier);
@@ -72,6 +86,9 @@ public abstract class SharedLatheSystem : EntitySystem
_inverseRecipeDictionary.Clear(); _inverseRecipeDictionary.Clear();
foreach (var latheRecipe in _proto.EnumeratePrototypes<LatheRecipePrototype>()) foreach (var latheRecipe in _proto.EnumeratePrototypes<LatheRecipePrototype>())
{ {
if (latheRecipe.Result == null)
continue;
_inverseRecipeDictionary.GetOrNew(latheRecipe.Result).Add(latheRecipe); _inverseRecipeDictionary.GetOrNew(latheRecipe.Result).Add(latheRecipe);
} }
} }
@@ -83,4 +100,55 @@ public abstract class SharedLatheSystem : EntitySystem
recipes.AddRange(r); recipes.AddRange(r);
return recipes.Count != 0; return recipes.Count != 0;
} }
public string GetRecipeName(ProtoId<LatheRecipePrototype> proto)
{
return GetRecipeName(_proto.Index(proto));
}
public string GetRecipeName(LatheRecipePrototype proto)
{
if (!string.IsNullOrWhiteSpace(proto.Name))
return Loc.GetString(proto.Name);
if (proto.Result is { } result)
{
return _proto.Index(result).Name;
}
if (proto.ResultReagents is { } resultReagents)
{
return ContentLocalizationManager.FormatList(resultReagents
.Select(p => Loc.GetString("lathe-menu-result-reagent-display", ("reagent", _proto.Index(p.Key).LocalizedName), ("amount", p.Value)))
.ToList());
}
return string.Empty;
}
[PublicAPI]
public string GetRecipeDescription(ProtoId<LatheRecipePrototype> proto)
{
return GetRecipeDescription(_proto.Index(proto));
}
public string GetRecipeDescription(LatheRecipePrototype proto)
{
if (!string.IsNullOrWhiteSpace(proto.Description))
return Loc.GetString(proto.Description);
if (proto.Result is { } result)
{
return _proto.Index(result).Description;
}
if (proto.ResultReagents is { } resultReagents)
{
// We only use the first one for the description since these descriptions don't combine very well.
var reagent = resultReagents.First().Key;
return _proto.Index(reagent).LocalizedDescription;
}
return string.Empty;
}
} }

View File

@@ -1,101 +1,58 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
using Content.Shared.Lathe.Prototypes; using Content.Shared.Lathe.Prototypes;
using Content.Shared.Materials; using Content.Shared.Materials;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Shared.Research.Prototypes namespace Content.Shared.Research.Prototypes
{ {
[NetSerializable, Serializable, Prototype("latheRecipe")] [NetSerializable, Serializable, Prototype]
public sealed partial class LatheRecipePrototype : IPrototype public sealed partial class LatheRecipePrototype : IPrototype
{ {
[ViewVariables] [ViewVariables]
[IdDataField] [IdDataField]
public string ID { get; private set; } = default!; public string ID { get; private set; } = default!;
[DataField("name")]
private string _name = string.Empty;
[DataField("description")]
private string _description = string.Empty;
/// <summary>
/// The prototype name of the resulting entity when the recipe is printed.
/// </summary>
[DataField("result", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
public string Result = string.Empty;
/// <summary>
/// An entity whose sprite is displayed in the ui in place of the actual recipe result.
/// </summary>
[DataField("icon")]
public SpriteSpecifier? Icon;
[DataField("completetime")]
private TimeSpan _completeTime = TimeSpan.FromSeconds(5);
[DataField("materials", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<int, MaterialPrototype>))]
private Dictionary<string, int> _requiredMaterials = new();
//todo make these function calls because we're eating tons of resolves here.
/// <summary> /// <summary>
/// Name displayed in the lathe GUI. /// Name displayed in the lathe GUI.
/// </summary> /// </summary>
[ViewVariables] [DataField]
public string Name public LocId? Name;
{
get
{
if (_name.Trim().Length != 0)
return _name;
var protoMan = IoCManager.Resolve<IPrototypeManager>();
protoMan.TryIndex(Result, out EntityPrototype? prototype);
if (prototype?.Name != null)
_name = prototype.Name;
return _name;
}
}
/// <summary> /// <summary>
/// Short description displayed in the lathe GUI. /// Short description displayed in the lathe GUI.
/// </summary> /// </summary>
[ViewVariables] [DataField]
public string Description public LocId? Description;
{
get /// <summary>
{ /// The prototype name of the resulting entity when the recipe is printed.
if (_description.Trim().Length != 0) /// </summary>
return _description; [DataField]
var protoMan = IoCManager.Resolve<IPrototypeManager>(); public EntProtoId? Result;
protoMan.TryIndex(Result, out EntityPrototype? prototype);
if (prototype?.Description != null) [DataField]
_description = prototype.Description; public Dictionary<ProtoId<ReagentPrototype>, FixedPoint2>? ResultReagents;
return _description;
} /// <summary>
} /// An entity whose sprite is displayed in the ui in place of the actual recipe result.
/// </summary>
[DataField]
public SpriteSpecifier? Icon;
[DataField("completetime")]
public TimeSpan CompleteTime = TimeSpan.FromSeconds(5);
/// <summary> /// <summary>
/// The materials required to produce this recipe. /// The materials required to produce this recipe.
/// Takes a material ID as string. /// Takes a material ID as string.
/// </summary> /// </summary>
[ViewVariables] [DataField]
public Dictionary<string, int> RequiredMaterials public Dictionary<ProtoId<MaterialPrototype>, int> Materials = new();
{
get => _requiredMaterials;
private set => _requiredMaterials = value;
}
[DataField]
/// <summary>
/// How many milliseconds it'll take for the lathe to finish this recipe.
/// Might lower depending on the lathe's upgrade level.
/// </summary>
[ViewVariables]
public TimeSpan CompleteTime => _completeTime;
[DataField("applyMaterialDiscount")]
public bool ApplyMaterialDiscount = true; public bool ApplyMaterialDiscount = true;
/// <summary> /// <summary>

View File

@@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using Content.Shared.Lathe;
using Content.Shared.Research.Components; using Content.Shared.Research.Components;
using Content.Shared.Research.Prototypes; using Content.Shared.Research.Prototypes;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -12,6 +13,7 @@ public abstract class SharedResearchSystem : EntitySystem
{ {
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!; [Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedLatheSystem _lathe = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -185,7 +187,7 @@ public abstract class SharedResearchSystem : EntitySystem
var recipeProto = PrototypeManager.Index(recipe); var recipeProto = PrototypeManager.Index(recipe);
description.PushNewline(); description.PushNewline();
description.AddMarkup(Loc.GetString("research-console-unlocks-list-entry", description.AddMarkup(Loc.GetString("research-console-unlocks-list-entry",
("name",recipeProto.Name))); ("name", _lathe.GetRecipeName(recipeProto))));
} }
foreach (var generic in technology.GenericUnlocks) foreach (var generic in technology.GenericUnlocks)
{ {

View File

@@ -1,5 +1,6 @@
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Lathe;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Random.Helpers; using Content.Shared.Random.Helpers;
using Content.Shared.Research.Components; using Content.Shared.Research.Components;
@@ -19,6 +20,7 @@ public sealed class TechnologyDiskSystem : EntitySystem
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedResearchSystem _research = default!; [Dependency] private readonly SharedResearchSystem _research = default!;
[Dependency] private readonly SharedLatheSystem _lathe = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -83,8 +85,7 @@ public sealed class TechnologyDiskSystem : EntitySystem
if (ent.Comp.Recipes != null && ent.Comp.Recipes.Count > 0) if (ent.Comp.Recipes != null && ent.Comp.Recipes.Count > 0)
{ {
var prototype = _protoMan.Index(ent.Comp.Recipes[0]); var prototype = _protoMan.Index(ent.Comp.Recipes[0]);
var resultPrototype = _protoMan.Index<EntityPrototype>(prototype.Result); message = Loc.GetString("tech-disk-examine", ("result", _lathe.GetRecipeName(prototype)));
message = Loc.GetString("tech-disk-examine", ("result", resultPrototype.Name));
if (ent.Comp.Recipes.Count > 1) //idk how to do this well. sue me. if (ent.Comp.Recipes.Count > 1) //idk how to do this well. sue me.
message += " " + Loc.GetString("tech-disk-examine-more"); message += " " + Loc.GetString("tech-disk-examine-more");

View File

@@ -0,0 +1,8 @@
lathe-recipe-Medkit-name = first aid kit (empty)
lathe-recipe-MedkitBurn-name = burn treatment kit (empty)
lathe-recipe-MedkitToxin-name = toxin treatment kit (empty)
lathe-recipe-MedkitO2-name = oxygen deprivation treatment kit (empty)
lathe-recipe-MedkitBrute-name = brute trauma treatment kit (empty)
lathe-recipe-MedkitAdvanced-name = advanced first aid kit (empty)
lathe-recipe-MedkitRadiation-name = radiation treatment kit (empty)
lathe-recipe-MedkitCombat-name = combat medical kit (empty)

View File

@@ -6,6 +6,9 @@ lathe-menu-search-designs = Search designs
lathe-menu-category-all = All lathe-menu-category-all = All
lathe-menu-search-filter = Filter: lathe-menu-search-filter = Filter:
lathe-menu-amount = Amount: lathe-menu-amount = Amount:
lathe-menu-reagent-slot-examine = It has a slot for a beaker on the side.
lathe-reagent-dispense-no-container = Liquid pours out of {THE($name)} onto the floor!
lathe-menu-result-reagent-display = {$reagent} ({$amount}u)
lathe-menu-material-display = {$material} ({$amount}) lathe-menu-material-display = {$material} ({$amount})
lathe-menu-tooltip-display = {$amount} of {$material} lathe-menu-tooltip-display = {$amount} of {$material}
lathe-menu-description-display = [italic]{$description}[/italic] lathe-menu-description-display = [italic]{$description}[/italic]

View File

@@ -134,7 +134,7 @@
- type: latheRecipe - type: latheRecipe
id: Medkit id: Medkit
result: Medkit result: Medkit
name: first aid kit (empty) name: lathe-recipe-Medkit-name
completetime: 2 completetime: 2
materials: materials:
Plastic: 300 Plastic: 300
@@ -142,7 +142,7 @@
- type: latheRecipe - type: latheRecipe
id: MedkitBurn id: MedkitBurn
result: MedkitBurn result: MedkitBurn
name: burn treatment kit (empty) name: lathe-recipe-MedkitBurn-name
completetime: 2 completetime: 2
materials: materials:
Plastic: 300 Plastic: 300
@@ -150,7 +150,7 @@
- type: latheRecipe - type: latheRecipe
id: MedkitToxin id: MedkitToxin
result: MedkitToxin result: MedkitToxin
name: toxin treatment kit (empty) name: lathe-recipe-MedkitToxin-name
completetime: 2 completetime: 2
materials: materials:
Plastic: 300 Plastic: 300
@@ -158,7 +158,7 @@
- type: latheRecipe - type: latheRecipe
id: MedkitO2 id: MedkitO2
result: MedkitO2 result: MedkitO2
name: oxygen deprivation treatment kit (empty) name: lathe-recipe-MedkitO2-name
completetime: 2 completetime: 2
materials: materials:
Plastic: 300 Plastic: 300
@@ -166,7 +166,7 @@
- type: latheRecipe - type: latheRecipe
id: MedkitBrute id: MedkitBrute
result: MedkitBrute result: MedkitBrute
name: brute trauma treatment kit (empty) name: lathe-recipe-MedkitBrute-name
completetime: 2 completetime: 2
materials: materials:
Plastic: 300 Plastic: 300
@@ -174,7 +174,7 @@
- type: latheRecipe - type: latheRecipe
id: MedkitAdvanced id: MedkitAdvanced
result: MedkitAdvanced result: MedkitAdvanced
name: advanced first aid kit (empty) name: lathe-recipe-MedkitAdvanced-name
completetime: 2 completetime: 2
materials: materials:
Plastic: 300 Plastic: 300
@@ -182,7 +182,7 @@
- type: latheRecipe - type: latheRecipe
id: MedkitRadiation id: MedkitRadiation
result: MedkitRadiation result: MedkitRadiation
name: radiation treatment kit (empty) name: lathe-recipe-MedkitRadiation-name
completetime: 2 completetime: 2
materials: materials:
Plastic: 300 Plastic: 300
@@ -190,7 +190,7 @@
- type: latheRecipe - type: latheRecipe
id: MedkitCombat id: MedkitCombat
result: MedkitCombat result: MedkitCombat
name: combat medical kit (empty) name: lathe-recipe-MedkitCombat-name
completetime: 2 completetime: 2
materials: materials:
Plastic: 300 Plastic: 300