6
StarHorizon_Public/Content.Shared/_NF/Lathe/SharedBlueprintLatheSystem.cs
2025-11-05 11:11:22 +03:00

176 lines
6.3 KiB
C#

using Content.Shared._NF.Research.Prototypes;
using Content.Shared.Materials;
using Content.Shared.Research.Prototypes;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Shared._NF.Lathe;
/// <summary>
/// This handles printing blueprints from all technologies known to a technology database.
/// </summary>
public abstract class SharedBlueprintLatheSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly SharedMaterialStorageSystem _materialStorage = default!;
/// <summary>
/// A lookup table of all printable recipes and the blueprint types they can be printed as.
/// </summary>
public readonly Dictionary<ProtoId<LatheRecipePrototype>, List<(ProtoId<BlueprintPrototype> blueprint, int index)>> PrintableRecipes = new();
/// <summary>
/// A lookup table of all printable blueprint types and each recipe that prints as that type.
/// Each list must be sorted alphabetically, and these indices are used as indices in a bitset in print requests.
/// </summary>
public readonly Dictionary<ProtoId<BlueprintPrototype>, List<ProtoId<LatheRecipePrototype>>> PrintableRecipesByType = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
BuildBlueprintRecipeList();
}
[PublicAPI]
public bool CanProduce(EntityUid uid, ProtoId<BlueprintPrototype> blueprintType, int[] recipe, int amount = 1, BlueprintLatheComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;
// TODO: should we reduce the set of recipes down to what we do have (and fail on empty) if this asks for things we don't have vs. failing?
if (!HasRecipes(uid, blueprintType, recipe, component))
return false;
return HasBlueprintMaterial(uid, amount, component);
}
[PublicAPI]
public bool HasBlueprintMaterial(EntityUid uid, int amount = 1, BlueprintLatheComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;
foreach (var (material, needed) in component.BlueprintPrintMaterials)
{
var adjustedAmount = AdjustMaterial(needed, component.ApplyMaterialDiscount, component.FinalMaterialUseMultiplier);
if (_materialStorage.GetMaterialAmount(uid, material) < adjustedAmount * amount)
return false;
}
return true;
}
[PublicAPI]
public bool CanProduceRecipe(EntityUid uid, ProtoId<BlueprintPrototype> blueprintType, ProtoId<LatheRecipePrototype> recipe, int amount = 1, BlueprintLatheComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;
if (!HasRecipe(uid, blueprintType, recipe, component))
return false;
foreach (var (material, needed) in component.BlueprintPrintMaterials)
{
var adjustedAmount = AdjustMaterial(needed, component.ApplyMaterialDiscount, component.FinalMaterialUseMultiplier);
if (_materialStorage.GetMaterialAmount(uid, material) < adjustedAmount * amount)
return false;
}
return true;
}
public static int AdjustMaterial(int original, bool reduce, float multiplier)
=> reduce ? (int)MathF.Ceiling(original * multiplier) : original;
protected abstract bool HasRecipes(EntityUid uid, ProtoId<BlueprintPrototype> blueprintType, int[] recipe, BlueprintLatheComponent component);
protected abstract bool HasRecipe(EntityUid uid, ProtoId<BlueprintPrototype> blueprintType, ProtoId<LatheRecipePrototype> recipe, BlueprintLatheComponent component);
private void OnPrototypesReloaded(PrototypesReloadedEventArgs obj)
{
if (!obj.WasModified<BlueprintPrototype>())
return;
BuildBlueprintRecipeList();
}
private void BuildBlueprintRecipeList()
{
PrintableRecipes.Clear();
PrintableRecipesByType.Clear();
// Set up collections
foreach (var blueprintProto in _proto.EnumeratePrototypes<BlueprintPrototype>())
{
List<ProtoId<LatheRecipePrototype>> recipeList = new();
// Fill in collections from packs
foreach (var pack in blueprintProto.Packs)
{
if (!_proto.TryIndex(pack, out var packProto))
continue;
foreach (var recipe in packProto.Recipes)
{
if (!_proto.HasIndex(recipe))
continue;
recipeList.Add(recipe);
}
}
PrintableRecipesByType.Add(blueprintProto.ID, recipeList);
}
// Associate each recipe with blueprint keys and indices
foreach (var (blueprintType, recipeList) in PrintableRecipesByType)
{
// Set up index values
int index = 0;
foreach (var recipe in recipeList)
{
if (!PrintableRecipes.TryGetValue(recipe, out var blueprintList))
{
PrintableRecipes.Add(recipe, new());
blueprintList = PrintableRecipes[recipe];
}
blueprintList.Add((blueprintType, index));
index++;
}
}
}
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 Loc.GetString("blueprint-lathe-name", ("name", _proto.Index(result).Name));
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 Loc.GetString("blueprint-lathe-description", ("name", _proto.Index(result).Name));
return string.Empty;
}
}