using System.Linq; 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 IEntityManager _entityManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private readonly SpriteSystem _spriteSystem; private readonly LatheSystem _lathe; public event Action? OnServerListButtonPressed; public event Action? RecipeQueueAction; public event Action? OnEjectPressed; public List> Recipes = new(); /// /// Default volume for a sheet if the material's entity prototype has no material composition. /// private const int DEFAULT_SHEET_VOLUME = 100; public LatheMenu(LatheBoundUserInterface owner) { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); _spriteSystem = _entityManager.System(); _lathe = _entityManager.System(); Title = _entityManager.GetComponent(owner.Owner).EntityName; SearchBar.OnTextChanged += _ => { PopulateRecipes(owner.Owner); }; AmountLineEdit.OnTextChanged += _ => { PopulateRecipes(owner.Owner); }; ServerListButton.OnPressed += a => OnServerListButtonPressed?.Invoke(a); if (_entityManager.TryGetComponent(owner.Owner, out var latheComponent)) { if (!latheComponent.DynamicRecipes.Any()) { ServerListButton.Visible = false; } } } public void PopulateMaterials(EntityUid lathe) { if (!_entityManager.TryGetComponent(lathe, out var materials)) return; MaterialsList.DisposeAllChildren(); foreach (var (materialId, volume) in materials.Storage) { if (volume <= 0) continue; if (!_prototypeManager.TryIndex(materialId, out MaterialPrototype? material)) continue; var sheetVolume = SheetVolume(material); var sheets = (float) volume / sheetVolume; var maxEjectableSheets = (int) MathF.Floor(sheets); var unit = Loc.GetString(material.Unit); var amountText = Loc.GetString("lathe-menu-material-amount", ("amount", sheets), ("unit", unit)); var name = Loc.GetString(material.Name); var mat = Loc.GetString("lathe-menu-material-display", ("material", name), ("amount", amountText)); var row = new LatheMaterialEjector(materialId, OnEjectPressed, sheetVolume, maxEjectableSheets) { Icon = { Texture = _spriteSystem.Frame0(material.Icon) }, ProductName = { Text = mat } }; MaterialsList.AddChild(row); } if (MaterialsList.ChildCount == 0) { var noMaterialsMsg = Loc.GetString("lathe-menu-no-materials-message"); var noItemRow = new Label(); noItemRow.Text = noMaterialsMsg; noItemRow.Align = Label.AlignMode.Center; MaterialsList.AddChild(noItemRow); } } /// /// Populates the list of all the recipes /// /// public void PopulateRecipes(EntityUid lathe) { if (!_entityManager.TryGetComponent(lathe, out var component)) return; var recipesToShow = new List(); foreach (var recipe in Recipes) { if (!_prototypeManager.TryIndex(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(); foreach (var prototype in recipesToShow) { StringBuilder sb = new(); var first = true; foreach (var (id, amount) in prototype.RequiredMaterials) { if (!_prototypeManager.TryIndex(id, out var proto)) continue; if (first) first = false; else sb.Append('\n'); var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, component.MaterialUseMultiplier); var sheetVolume = SheetVolume(proto); var unit = Loc.GetString(proto.Unit); // rounded in locale not here var sheets = adjustedAmount / (float) sheetVolume; var amountText = Loc.GetString("lathe-menu-material-amount", ("amount", sheets), ("unit", unit)); var name = Loc.GetString(proto.Name); sb.Append(Loc.GetString("lathe-menu-tooltip-display", ("material", name), ("amount", amountText))); } var icon = prototype.Icon == null ? _spriteSystem.GetPrototypeIcon(prototype.Result).Default : _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); } } /// /// Populates the build queue list with all queued items /// /// public void PopulateQueueList(List queue) { QueueList.Clear(); var idx = 1; foreach (var recipe in queue) { var icon = recipe.Icon == null ? _spriteSystem.GetPrototypeIcon(recipe.Result).Default : _spriteSystem.Frame0(recipe.Icon); QueueList.AddItem($"{idx}. {recipe.Name}", icon); idx++; } } public void SetQueueInfo(LatheRecipePrototype? recipe) { FabricatingContainer.Visible = recipe != null; if (recipe == null) return; Icon.Texture = recipe.Icon == null ? _spriteSystem.GetPrototypeIcon(recipe.Result).Default : _spriteSystem.Frame0(recipe.Icon); NameLabel.Text = $"{recipe.Name}"; } private int SheetVolume(MaterialPrototype material) { if (material.StackEntity == null) return DEFAULT_SHEET_VOLUME; var proto = _prototypeManager.Index(material.StackEntity); if (!proto.TryGetComponent(out var composition)) return DEFAULT_SHEET_VOLUME; return composition.MaterialComposition.FirstOrDefault(kvp => kvp.Key == material.ID).Value; } }