/* * This file is sublicensed under MIT License * https://github.com/space-wizards/space-station-14/blob/master/LICENSE.TXT */ using Content.Shared._CP14.Workbench; using Content.Shared._CP14.Workbench.Prototypes; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Prototypes; namespace Content.Client._CP14.Workbench; [GenerateTypedNameReferences] public sealed partial class CP14WorkbenchWindow : DefaultWindow { private const int AllCategoryId = -1; [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly ILogManager _log = default!; public event Action? OnCraft; private readonly Dictionary _categories = new(); private CP14WorkbenchUiRecipesState? _cachedState; private CP14WorkbenchUiRecipesEntry? _selectedEntry; private string _searchFilter = string.Empty; private ISawmill Sawmill { get; init; } public CP14WorkbenchWindow() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); Sawmill = _log.GetSawmill("cp14_workbench_window"); SearchBar.OnTextChanged += OnSearchChanged; CraftButton.OnPressed += OnCraftPressed; OptionCategories.OnItemSelected += OnCategoryItemSelected; } public void UpdateRecipesVisibility() { if (_cachedState is null) return; CraftsContainer.RemoveAllChildren(); var recipes = new List(); foreach (var entry in _cachedState.Recipes) { if (!_prototype.TryIndex(entry.ProtoId, out var indexedEntry)) { Sawmill.Error($"No recipe prototype {entry.ProtoId} retrieved from cache found"); continue; } if (!ProcessSearchFilter(entry, indexedEntry)) continue; if (!ProcessSearchCategoryFilter(indexedEntry)) continue; recipes.Add(entry); } recipes.Sort(CP14WorkbenchUiRecipesEntry.CompareTo); foreach (var recipe in recipes) { var control = new CP14WorkbenchRecipeControl(recipe); control.OnSelect += RecipeSelect; CraftsContainer.AddChild(control); } if (_selectedEntry is not null && !recipes.Contains(_selectedEntry.Value)) RecipeSelectNull(); } public void UpdateState(CP14WorkbenchUiRecipesState recipesState) { _cachedState = recipesState; _categories.Clear(); OptionCategories.Clear(); OptionCategories.AddItem(Loc.GetString("cp14-recipe-category-all"), AllCategoryId); var categories = new List(); var count = 0; foreach (var entry in recipesState.Recipes) { if (!_prototype.TryIndex(entry.ProtoId, out var indexedEntry)) continue; // Populate categories if (indexedEntry.Category is null) continue; if (!_prototype.TryIndex(indexedEntry.Category, out var indexedCategory)) continue; if (categories.Contains(indexedCategory.Name)) continue; categories.Add(indexedCategory.Name); } categories.Sort((a, b) => string.Compare(Loc.GetString(a), Loc.GetString(b), StringComparison.Ordinal)); foreach (var category in categories) { OptionCategories.AddItem(Loc.GetString(category), count); _categories.Add(count, category); count++; } UpdateRecipesVisibility(); } private void OnSearchChanged(LineEdit.LineEditEventArgs _) { _searchFilter = SearchBar.Text.Trim().ToLowerInvariant(); UpdateRecipesVisibility(); } private void OnCraftPressed(BaseButton.ButtonEventArgs _) { if (_selectedEntry is null) return; OnCraft?.Invoke(_selectedEntry.Value); } private void OnCategoryItemSelected(OptionButton.ItemSelectedEventArgs obj) { OptionCategories.SelectId(obj.Id); UpdateRecipesVisibility(); } private bool ProcessSearchFilter(CP14WorkbenchUiRecipesEntry entry, CP14WorkbenchRecipePrototype indexedEntry) { if (_searchFilter == string.Empty) return true; // Skip the iteration, because the desired result does not match the filter if (_prototype.TryIndex(indexedEntry.Result, out var indexedResult)) return indexedResult.Name.Contains(_searchFilter); Sawmill.Error($"No result entity prototype {entry.ProtoId} retrieved from cache found"); return false; } private bool ProcessSearchCategoryFilter(CP14WorkbenchRecipePrototype indexedEntry) { // If we are searching through all categories, we simply skip the current filter if (OptionCategories.SelectedId == AllCategoryId) return true; if (!_categories.TryGetValue(OptionCategories.SelectedId, out var selectedCategory)) { Sawmill.Error($"Non-existent {OptionCategories.SelectedId} category id selected. Filter skipped"); return true; } if (indexedEntry.Category is null) return false; if (!_prototype.TryIndex(indexedEntry.Category, out var indexedCategory)) { Sawmill.Error($"Non-existent {indexedEntry.Category} category prototype id. Filter skipped"); return true; } return indexedCategory.Name == selectedCategory; } private void RecipeSelect(CP14WorkbenchUiRecipesState recipesState) { foreach (var entry in recipesState.Recipes) { RecipeSelect(entry, _prototype.Index(entry.ProtoId)); break; } } private void RecipeSelect(CP14WorkbenchUiRecipesEntry cachedEntry) { if (_cachedState is null) return; if (_cachedState.Recipes.Contains(cachedEntry)) { Sawmill.Warning($"The selected cache option {cachedEntry} isn't found in recipes"); return; } RecipeSelect(cachedEntry, _prototype.Index(cachedEntry.ProtoId)); } private void RecipeSelect(CP14WorkbenchUiRecipesEntry entry, CP14WorkbenchRecipePrototype recipe) { _selectedEntry = entry; var result = _prototype.Index(recipe.Result); // TODO: Make it through the localization? var counter = recipe.ResultCount > 1 ? $" x{recipe.ResultCount}" : string.Empty; ItemView.SetPrototype(recipe.Result); ItemName.Text = result.Name + counter; ItemDescription.Text = result.Description; ItemRequirements.RemoveAllChildren(); foreach (var requirement in recipe.Requirements) { ItemRequirements.AddChild(new CP14WorkbenchRequirementControl(requirement)); } CraftButton.Disabled = !entry.Craftable; } private void RecipeSelectNull() { _selectedEntry = null; ItemView.SetPrototype(null); ItemName.Text = string.Empty; ItemDescription.Text = string.Empty; ItemRequirements.RemoveAllChildren(); CraftButton.Disabled = true; } }