using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.Emag.Systems; using Content.Shared.Examine; using Content.Shared.Lathe.Prototypes; using Content.Shared.Localizations; using Content.Shared.Materials; using Content.Shared.Research.Prototypes; using JetBrains.Annotations; using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Shared.Lathe; /// /// This handles... /// public abstract class SharedLatheSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly SharedMaterialStorageSystem _materialStorage = default!; [Dependency] private readonly EmagSystem _emag = default!; public readonly Dictionary> InverseRecipes = new(); public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnPrototypesReloaded); BuildInverseRecipeDictionary(); } /// /// Add every recipe in the list of recipe packs to a single hashset. /// public void AddRecipesFromPacks(HashSet> recipes, IEnumerable> packs) { foreach (var id in packs) { var pack = _proto.Index(id); recipes.UnionWith(pack.Recipes); } } private void OnExamined(Entity 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) { return _proto.TryIndex(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.Materials) { var adjustedAmount = AdjustMaterial(needed, recipe.ApplyMaterialDiscount, component.MaterialUseMultiplier); if (_materialStorage.GetMaterialAmount(uid, material) < adjustedAmount * amount) return false; } return true; } private void OnEmagged(EntityUid uid, EmagLatheRecipesComponent component, ref GotEmaggedEvent args) { if (!_emag.CompareFlag(args.Type, EmagType.Interaction)) return; if (_emag.CheckFlag(uid, EmagType.Interaction)) return; args.Handled = true; } public static int AdjustMaterial(int original, bool reduce, float multiplier) => reduce ? (int) MathF.Ceiling(original * multiplier) : original; protected abstract bool HasRecipe(EntityUid uid, LatheRecipePrototype recipe, LatheComponent component); private void OnPrototypesReloaded(PrototypesReloadedEventArgs obj) { if (!obj.WasModified()) return; BuildInverseRecipeDictionary(); } private void BuildInverseRecipeDictionary() { InverseRecipes.Clear(); foreach (var latheRecipe in _proto.EnumeratePrototypes()) { if (latheRecipe.Result is not {} result) continue; InverseRecipes.GetOrNew(result).Add(latheRecipe); } } public bool TryGetRecipesFromEntity(string prototype, [NotNullWhen(true)] out List? recipes) { recipes = new(); if (InverseRecipes.TryGetValue(prototype, out var r)) recipes.AddRange(r); return recipes.Count != 0; } public string GetRecipeName(ProtoId 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 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; } }