mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-10-01 00:27:59 +00:00
* Initial port of goobstation factorio, missing disposals integration and faxing. Also ports impstations modification for robotic arms to have static power draw. Also adds automation slots to silos and advanced microwave. * Ports goobstation factorio fax automation, adds to the guidebook entry info about gas canisters. * Ported Goob Disposals. Removed part about taking mats out of storage silo cuz it ain't implemented yet. Seems to work. * Adds constructor circuitboard to research cuz I forgor
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);
|
|
}
|
|
}
|
|
}
|