using System.Linq; using System.Text; using Content.Client.Materials; using Content.Shared.Lathe; using Content.Shared.Lathe.Prototypes; 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 EntityUid _owner; private readonly SpriteSystem _spriteSystem; private readonly LatheSystem _lathe; private readonly MaterialStorageSystem _materialStorage; public event Action? OnServerListButtonPressed; public event Action? RecipeQueueAction; public List> Recipes = new(); public List>? Categories; public ProtoId? CurrentCategory; public LatheMenu(LatheBoundUserInterface owner) { _owner = owner.Owner; RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); _spriteSystem = _entityManager.System(); _lathe = _entityManager.System(); _materialStorage = _entityManager.System(); Title = _entityManager.GetComponent(owner.Owner).EntityName; SearchBar.OnTextChanged += _ => { PopulateRecipes(); }; AmountLineEdit.OnTextChanged += _ => { PopulateRecipes(); }; FilterOption.OnItemSelected += OnItemSelected; ServerListButton.OnPressed += a => OnServerListButtonPressed?.Invoke(a); if (_entityManager.TryGetComponent(owner.Owner, out var latheComponent)) { if (!latheComponent.DynamicRecipes.Any()) { ServerListButton.Visible = false; } } MaterialsList.SetOwner(owner.Owner); } /// /// Populates the list of all the recipes /// public void PopulateRecipes() { if (!_entityManager.TryGetComponent(_owner, out var component)) return; var recipesToShow = new List(); foreach (var recipe in Recipes) { if (!_prototypeManager.TryIndex(recipe, out var proto)) continue; if (CurrentCategory != null && proto.Category != CurrentCategory) 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; var sortedRecipesToShow = recipesToShow.OrderBy(p => p.Name); RecipeList.Children.Clear(); foreach (var prototype in sortedRecipesToShow) { var icon = prototype.Icon == null ? _spriteSystem.GetPrototypeIcon(prototype.Result).Default : _spriteSystem.Frame0(prototype.Icon); var canProduce = _lathe.CanProduce(_owner, prototype, quantity); var control = new RecipeControl(prototype, () => GenerateTooltipText(prototype), canProduce, icon); control.OnButtonPressed += s => { if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount <= 0) amount = 1; RecipeQueueAction?.Invoke(s, amount); }; RecipeList.AddChild(control); } } private string GenerateTooltipText(LatheRecipePrototype prototype) { StringBuilder sb = new(); foreach (var (id, amount) in prototype.RequiredMaterials) { if (!_prototypeManager.TryIndex(id, out var proto)) continue; var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, _entityManager.GetComponent(_owner).MaterialUseMultiplier); var sheetVolume = _materialStorage.GetSheetVolume(proto); var unit = Loc.GetString(proto.Unit); var sheets = adjustedAmount / (float) sheetVolume; var availableAmount = _materialStorage.GetMaterialAmount(_owner, id); var missingAmount = Math.Max(0, adjustedAmount - availableAmount); var missingSheets = missingAmount / (float) sheetVolume; var name = Loc.GetString(proto.Name); string tooltipText; if (missingSheets > 0) { tooltipText = Loc.GetString("lathe-menu-material-amount-missing", ("amount", sheets), ("missingAmount", missingSheets), ("unit", unit), ("material", name)); } else { var amountText = Loc.GetString("lathe-menu-material-amount", ("amount", sheets), ("unit", unit)); tooltipText = Loc.GetString("lathe-menu-tooltip-display", ("material", name), ("amount", amountText)); } sb.AppendLine(tooltipText); } if (!string.IsNullOrWhiteSpace(prototype.Description)) sb.AppendLine(Loc.GetString("lathe-menu-description-display", ("description", prototype.Description))); // Remove last newline if (sb.Length > 0) sb.Remove(sb.Length - 1, 1); return sb.ToString(); } public void UpdateCategories() { var currentCategories = new List>(); foreach (var recipeId in Recipes) { var recipe = _prototypeManager.Index(recipeId); if (recipe.Category == null) continue; if (currentCategories.Contains(recipe.Category.Value)) continue; currentCategories.Add(recipe.Category.Value); } if (Categories != null && (Categories.Count == currentCategories.Count || !Categories.All(currentCategories.Contains))) return; Categories = currentCategories; var sortedCategories = currentCategories .Select(p => _prototypeManager.Index(p)) .OrderBy(p => Loc.GetString(p.Name)) .ToList(); FilterOption.Clear(); FilterOption.AddItem(Loc.GetString("lathe-menu-category-all"), -1); foreach (var category in sortedCategories) { FilterOption.AddItem(Loc.GetString(category.Name), Categories.IndexOf(category.ID)); } FilterOption.SelectId(-1); } /// /// 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 void OnItemSelected(OptionButton.ItemSelectedEventArgs obj) { FilterOption.SelectId(obj.Id); if (obj.Id == -1) { CurrentCategory = null; } else { CurrentCategory = Categories?[obj.Id]; } PopulateRecipes(); } }