* backpacks translation * leather categories * linter fix * Linter please * Описания * priority in craft * priorotize * eng comment * Update Content.Client/_CP14/Workbench/CP14WorkbenchWindow.xaml.cs
325 lines
11 KiB
C#
325 lines
11 KiB
C#
/*
|
|
* This file is sublicensed under MIT License
|
|
* https://github.com/space-wizards/space-station-14/blob/master/LICENSE.TXT
|
|
*/
|
|
|
|
using System.Linq;
|
|
using Content.Client._CP14.Skill;
|
|
using Content.Shared._CP14.Workbench;
|
|
using Content.Shared._CP14.Workbench.Prototypes;
|
|
using Robust.Client.AutoGenerated;
|
|
using Robust.Client.Player;
|
|
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 IPlayerManager _player = default!;
|
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
|
[Dependency] private readonly ILogManager _log = default!;
|
|
private CP14ClientSkillSystem _skill;
|
|
|
|
public event Action<CP14WorkbenchUiRecipesEntry>? OnCraft;
|
|
|
|
/// <summary>
|
|
/// Used for category dropdown filtering.
|
|
/// </summary>
|
|
private readonly Dictionary<int, LocId> _categoryIndexes = new();
|
|
|
|
private Dictionary<LocId, List<CP14WorkbenchUiRecipesEntry>> _categories = new();
|
|
private List<CP14WorkbenchUiRecipesEntry> _uncategorized = 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);
|
|
|
|
_skill = _entManager.System<CP14ClientSkillSystem>();
|
|
|
|
Sawmill = _log.GetSawmill("cp14_workbench_window");
|
|
|
|
SearchBar.OnTextChanged += OnSearchChanged;
|
|
CraftButton.OnPressed += OnCraftPressed;
|
|
OptionCategories.OnItemSelected += OnCategoryItemSelected;
|
|
}
|
|
|
|
private void UpdateRecipesVisibility()
|
|
{
|
|
if (_cachedState is null)
|
|
return;
|
|
|
|
CraftsContainer.RemoveAllChildren();
|
|
|
|
if (_uncategorized.Count > 0 && OptionCategories.SelectedId == AllCategoryId)
|
|
{
|
|
var uncategorizedGridContainer = new GridContainer();
|
|
uncategorizedGridContainer.Columns = 5;
|
|
uncategorizedGridContainer.VerticalExpand = true;
|
|
|
|
CraftsContainer.AddChild(uncategorizedGridContainer);
|
|
AddRecipeListToGrid(_uncategorized, uncategorizedGridContainer);
|
|
}
|
|
|
|
foreach (var category in _categories)
|
|
{
|
|
if (_categoryIndexes.TryGetValue(OptionCategories.SelectedId, out var selectedCategory) &&
|
|
category.Key != selectedCategory)
|
|
continue;
|
|
|
|
var categoryLabel = new RichTextLabel();
|
|
categoryLabel.Margin = new Thickness(5);
|
|
categoryLabel.Text = Loc.GetString(category.Key);
|
|
CraftsContainer.AddChild(categoryLabel);
|
|
|
|
var gridContainer = new GridContainer();
|
|
gridContainer.Columns = 5;
|
|
gridContainer.VerticalExpand = true;
|
|
CraftsContainer.AddChild(gridContainer);
|
|
|
|
AddRecipeListToGrid(category.Value, gridContainer);
|
|
}
|
|
|
|
if (_selectedEntry is not null && !_cachedState.Recipes.Contains(_selectedEntry.Value))
|
|
RecipeSelectNull();
|
|
}
|
|
|
|
private void AddRecipeListToGrid(List<CP14WorkbenchUiRecipesEntry> category, GridContainer gridContainer)
|
|
{
|
|
foreach (var entry in category)
|
|
{
|
|
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;
|
|
|
|
if (_player.LocalEntity is not null)
|
|
{
|
|
var skilled = true;
|
|
foreach (var skill in indexedEntry.RequiredSkills)
|
|
{
|
|
if (!_skill.HaveSkill(_player.LocalEntity.Value, skill))
|
|
{
|
|
skilled = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!skilled)
|
|
continue;
|
|
}
|
|
|
|
var control = new CP14WorkbenchRecipeControl(entry);
|
|
control.OnSelect += RecipeSelect;
|
|
|
|
gridContainer.AddChild(control);
|
|
}
|
|
}
|
|
|
|
public void UpdateState(CP14WorkbenchUiRecipesState recipesState)
|
|
{
|
|
if (_player.LocalEntity is null)
|
|
return;
|
|
|
|
_cachedState = recipesState;
|
|
|
|
_categoryIndexes.Clear();
|
|
_categories.Clear();
|
|
_uncategorized.Clear();
|
|
OptionCategories.Clear();
|
|
OptionCategories.AddItem(Loc.GetString("cp14-recipe-category-all"), AllCategoryId);
|
|
|
|
// First, we sort all the recipes by priority and category.
|
|
var sortedRecipes = recipesState.Recipes
|
|
.OrderByDescending(e => e.Craftable)
|
|
.ThenByDescending(e =>
|
|
{
|
|
if (!_prototype.TryIndex(e.ProtoId, out CP14WorkbenchRecipePrototype? recipe))
|
|
return 0;
|
|
return recipe.Priority;
|
|
})
|
|
.ThenBy(e =>
|
|
{
|
|
if (!_prototype.TryIndex(e.ProtoId, out CP14WorkbenchRecipePrototype? recipe) ||
|
|
!_prototype.TryIndex(recipe.Category, out CP14WorkbenchRecipeCategoryPrototype? category))
|
|
return string.Empty;
|
|
return category.ID;
|
|
});
|
|
|
|
foreach (var entry in sortedRecipes)
|
|
{
|
|
if (!_prototype.TryIndex(entry.ProtoId, out var indexedEntry))
|
|
continue;
|
|
|
|
if (!_prototype.TryIndex(indexedEntry.Category, out var indexedCategory))
|
|
{
|
|
_uncategorized.Add(entry);
|
|
continue;
|
|
}
|
|
|
|
if (!_categories.TryGetValue(indexedCategory.Name, out var entries))
|
|
{
|
|
entries = new List<CP14WorkbenchUiRecipesEntry>();
|
|
_categories[indexedCategory.Name] = entries;
|
|
}
|
|
|
|
entries.Add(entry);
|
|
}
|
|
|
|
// Sort categories by priority
|
|
var sortedCategories = _categories
|
|
.OrderByDescending(c =>
|
|
{
|
|
var categoryProto = _prototype.EnumeratePrototypes<CP14WorkbenchRecipeCategoryPrototype>()
|
|
.FirstOrDefault(p => p.Name == c.Key);
|
|
return categoryProto?.Priority ?? 0;
|
|
})
|
|
.ToList();
|
|
|
|
_categories = sortedCategories.ToDictionary(pair => pair.Key, pair => pair.Value);
|
|
|
|
var count = 0;
|
|
foreach (var category in _categories)
|
|
{
|
|
OptionCategories.AddItem(Loc.GetString(category.Key), count);
|
|
_categoryIndexes.Add(count, category.Key);
|
|
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 (!_categoryIndexes.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;
|
|
}
|
|
}
|