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

@@ -81,9 +81,9 @@ namespace Content.Shared.Construction
{
var partRecipe = recipes[0];
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[mat] += matAmount * amount * coefficient;
@@ -101,9 +101,9 @@ namespace Content.Shared.Construction
{
var partRecipe = recipes[0];
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[mat] += matAmount * amount * coefficient;

View File

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

View File

@@ -1,5 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.Emag.Systems;
using Content.Shared.Examine;
using Content.Shared.Localizations;
using Content.Shared.Materials;
using Content.Shared.Research.Prototypes;
using JetBrains.Annotations;
@@ -23,10 +26,21 @@ public abstract class SharedLatheSystem : EntitySystem
base.Initialize();
SubscribeLocalEvent<EmagLatheRecipesComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<LatheComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
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]
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))
return false;
foreach (var (material, needed) in recipe.RequiredMaterials)
foreach (var (material, needed) in recipe.Materials)
{
var adjustedAmount = AdjustMaterial(needed, recipe.ApplyMaterialDiscount, component.MaterialUseMultiplier);
@@ -72,6 +86,9 @@ public abstract class SharedLatheSystem : EntitySystem
_inverseRecipeDictionary.Clear();
foreach (var latheRecipe in _proto.EnumeratePrototypes<LatheRecipePrototype>())
{
if (latheRecipe.Result == null)
continue;
_inverseRecipeDictionary.GetOrNew(latheRecipe.Result).Add(latheRecipe);
}
}
@@ -83,4 +100,55 @@ public abstract class SharedLatheSystem : EntitySystem
recipes.AddRange(r);
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.Materials;
using Robust.Shared.Prototypes;
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;
namespace Content.Shared.Research.Prototypes
{
[NetSerializable, Serializable, Prototype("latheRecipe")]
[NetSerializable, Serializable, Prototype]
public sealed partial class LatheRecipePrototype : IPrototype
{
[ViewVariables]
[IdDataField]
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>
/// Name displayed in the lathe GUI.
/// </summary>
[ViewVariables]
public string 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;
}
}
[DataField]
public LocId? Name;
/// <summary>
/// Short description displayed in the lathe GUI.
/// </summary>
[ViewVariables]
public string Description
{
get
{
if (_description.Trim().Length != 0)
return _description;
var protoMan = IoCManager.Resolve<IPrototypeManager>();
protoMan.TryIndex(Result, out EntityPrototype? prototype);
if (prototype?.Description != null)
_description = prototype.Description;
return _description;
}
}
[DataField]
public LocId? Description;
/// <summary>
/// The prototype name of the resulting entity when the recipe is printed.
/// </summary>
[DataField]
public EntProtoId? Result;
[DataField]
public Dictionary<ProtoId<ReagentPrototype>, FixedPoint2>? ResultReagents;
/// <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>
/// The materials required to produce this recipe.
/// Takes a material ID as string.
/// </summary>
[ViewVariables]
public Dictionary<string, int> RequiredMaterials
{
get => _requiredMaterials;
private set => _requiredMaterials = value;
}
[DataField]
public Dictionary<ProtoId<MaterialPrototype>, int> Materials = new();
/// <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")]
[DataField]
public bool ApplyMaterialDiscount = true;
/// <summary>

View File

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

View File

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