194 lines
6.6 KiB
C#
194 lines
6.6 KiB
C#
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
|
|
// SPDX-FileCopyrightText: 2025 deltanedas <39013340+deltanedas@users.noreply.github.com>
|
|
// SPDX-FileCopyrightText: 2025 deltanedas <@deltanedas:kde.org>
|
|
// SPDX-FileCopyrightText: 2025 gluesniffler <159397573+gluesniffler@users.noreply.github.com>
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
using Content.Client.Construction;
|
|
using Content.Client.Construction.UI;
|
|
using Content.Shared._Goobstation.Factory;
|
|
using Content.Shared.Construction.Prototypes;
|
|
using Content.Shared.Whitelist;
|
|
using Robust.Client.GameObjects;
|
|
using Robust.Client.Graphics;
|
|
using Robust.Client.UserInterface;
|
|
using Robust.Client.UserInterface.Controls;
|
|
using Robust.Shared.Prototypes;
|
|
using System.Linq;
|
|
|
|
namespace Content.Client._Goobstation.Factory.UI;
|
|
|
|
public sealed class ConstructorBUI : BoundUserInterface
|
|
{
|
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
|
private readonly ConstructionSystem _construction;
|
|
private readonly EntityWhitelistSystem _whitelist;
|
|
private readonly SpriteSystem _sprite;
|
|
|
|
private ConstructionMenu? _menu;
|
|
private string? _id;
|
|
private List<ConstructionMenu.ConstructionMenuListData> _recipes = new();
|
|
private readonly LocId _favoriteCatName = "construction-category-favorites";
|
|
private readonly LocId _forAllCategoryName = "construction-category-all";
|
|
|
|
public ConstructorBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
|
{
|
|
_construction = EntMan.System<ConstructionSystem>();
|
|
_whitelist = EntMan.System<EntityWhitelistSystem>();
|
|
_sprite = EntMan.System<SpriteSystem>();
|
|
|
|
_id = EntMan.GetComponentOrNull<ConstructorComponent>(owner)?.Construction;
|
|
}
|
|
|
|
protected override void Open()
|
|
{
|
|
base.Open();
|
|
|
|
// god BLESS whoever made construction ui for having it so decoupled <3
|
|
_menu = this.CreateWindow<ConstructionMenu>();
|
|
PopulateCategories();
|
|
PopulateRecipes(string.Empty, string.Empty);
|
|
_menu.PopulateRecipes += (_, args) => PopulateRecipes(args.Item1, args.Item2);
|
|
_menu.RecipeSelected += (_, item) =>
|
|
{
|
|
_menu.ClearRecipeInfo();
|
|
if (item != null && item.Prototype != null)
|
|
{
|
|
_id = item.Prototype.ID;
|
|
_menu.SetRecipeInfo(item.Prototype.Name ?? "", item.Prototype.Description ?? "", item?.TargetPrototype,
|
|
item!.Prototype.Type != ConstructionType.Item, true); // TODO: favourites
|
|
|
|
GenerateStepList(item.Prototype);
|
|
}
|
|
else
|
|
{
|
|
_id = null;
|
|
}
|
|
};
|
|
_menu.BuildButtonToggled += (_, _) =>
|
|
{
|
|
SendPredictedMessage(new ConstructorSetProtoMessage(_id));
|
|
_menu.Close();
|
|
};
|
|
}
|
|
|
|
private void PopulateCategories(string? selected = null)
|
|
{
|
|
if (_menu is not {} menu)
|
|
return;
|
|
|
|
var categories = new HashSet<string>();
|
|
|
|
foreach (var prototype in _proto.EnumeratePrototypes<ConstructionPrototype>())
|
|
{
|
|
var category = prototype.Category;
|
|
|
|
if (!string.IsNullOrEmpty(category))
|
|
categories.Add(category);
|
|
}
|
|
|
|
var categoriesArray = new string[categories.Count + 1];
|
|
|
|
// hard-coded to show all recipes
|
|
var idx = 0;
|
|
categoriesArray[idx++] = _forAllCategoryName;
|
|
|
|
foreach (var cat in categories.OrderBy(Loc.GetString))
|
|
{
|
|
categoriesArray[idx++] = cat;
|
|
}
|
|
|
|
menu.OptionCategories.Clear();
|
|
|
|
for (var i = 0; i < categoriesArray.Length; i++)
|
|
{
|
|
menu.OptionCategories.AddItem(Loc.GetString(categoriesArray[i]), i);
|
|
|
|
if (!string.IsNullOrEmpty(selected) && selected == categoriesArray[i])
|
|
menu.OptionCategories.SelectId(i);
|
|
}
|
|
|
|
menu.Categories = categoriesArray;
|
|
}
|
|
|
|
// copypasted and optimised from ConstructionMenuPresenter
|
|
private void PopulateRecipes(string search, string category)
|
|
{
|
|
if (PlayerManager.LocalEntity is not { } user
|
|
|| _menu is not { } menu)
|
|
return;
|
|
|
|
search = search.Trim().ToLowerInvariant();
|
|
var searching = !string.IsNullOrEmpty(search);
|
|
var isEmptyCategory = string.IsNullOrEmpty(category) || category == _forAllCategoryName;
|
|
|
|
_recipes.Clear();
|
|
foreach (var recipe in _proto.EnumeratePrototypes<ConstructionPrototype>())
|
|
{
|
|
if (recipe.Hide)
|
|
continue;
|
|
|
|
if (_whitelist.IsWhitelistFail(recipe.EntityWhitelist, user))
|
|
continue;
|
|
|
|
if (searching
|
|
&& recipe.Name != null
|
|
&& !recipe.Name.ToLowerInvariant().Contains(search))
|
|
continue;
|
|
|
|
if (!isEmptyCategory)
|
|
{
|
|
// TODO: when favourites get sent from server do this
|
|
// currently its specific to the G menu
|
|
//if (!_favoritedRecipes.Contains(recipe))
|
|
if (category == _favoriteCatName)
|
|
continue;
|
|
else if (recipe.Category != category)
|
|
continue;
|
|
}
|
|
|
|
if (!_construction!.TryGetRecipePrototype(recipe.ID, out var targetProtoId))
|
|
continue;
|
|
|
|
if (!_proto.TryIndex(targetProtoId, out EntityPrototype? proto))
|
|
continue;
|
|
|
|
_recipes.Add(new(recipe, proto));
|
|
}
|
|
|
|
_recipes.Sort((a, b) => string.Compare(a.Prototype.Name, b.Prototype.Name, StringComparison.InvariantCulture));
|
|
|
|
var recipesList = menu.Recipes;
|
|
recipesList.PopulateList(_recipes);
|
|
|
|
menu.RecipesGridScrollContainer.Visible = false;
|
|
menu.Recipes.Visible = true;
|
|
}
|
|
|
|
private void GenerateStepList(ConstructionPrototype proto)
|
|
{
|
|
if (_construction.GetGuide(proto) is not { } guide
|
|
|| _menu is not { } menu)
|
|
return;
|
|
|
|
var list = menu.RecipeStepList;
|
|
foreach (var entry in guide.Entries)
|
|
{
|
|
var text = entry.Arguments != null
|
|
? Loc.GetString(entry.Localization, entry.Arguments)
|
|
: Loc.GetString(entry.Localization);
|
|
|
|
if (entry.EntryNumber is { } number)
|
|
text = Loc.GetString("construction-presenter-step-wrapper",
|
|
("step-number", number), ("text", text));
|
|
|
|
// The padding needs to be applied regardless of text length... (See PadLeft documentation)
|
|
text = text.PadLeft(text.Length + entry.Padding);
|
|
|
|
var icon = entry.Icon != null ? _sprite.Frame0(entry.Icon) : Texture.Transparent;
|
|
list.AddItem(text, icon, false);
|
|
}
|
|
}
|
|
}
|