Files
tbd-station-14/Content.Client/Lathe/UI/LatheMenu.xaml.cs
deltanedas 1c2f200762 add mining points (#2419)
* add mining points

* add claim points button to oreproc

* funny

* its over

* :trollface:

* xml fail

Signed-off-by: deltanedas <39013340+deltanedas@users.noreply.github.com>

---------

Signed-off-by: deltanedas <39013340+deltanedas@users.noreply.github.com>
Co-authored-by: deltanedas <@deltanedas:kde.org>
2025-11-26 22:55:56 -05:00

416 lines
14 KiB
C#

using System.Linq;
using System.Text;
using Content.Client.Materials;
using Content.Shared.DeltaV.Salvage.Components; // DeltaV
using Content.Shared.DeltaV.Salvage.Systems; // DeltaV
using Content.Shared.Lathe;
using Content.Shared.Lathe.Prototypes;
using Content.Shared.Research.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Player; // DeltaV
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using Robust.Shared.Timing; // DeltaV
namespace Content.Client.Lathe.UI;
[GenerateTypedNameReferences]
public sealed partial class LatheMenu : DefaultWindow
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPlayerManager _player = default!; // DeltaV
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private readonly SpriteSystem _spriteSystem;
private readonly LatheSystem _lathe;
private readonly MaterialStorageSystem _materialStorage;
private readonly MiningPointsSystem _miningPoints; // DeltaV
public event Action<BaseButton.ButtonEventArgs>? OnServerListButtonPressed;
public event Action<string, int>? RecipeQueueAction;
public event Action<int>? QueueDeleteAction;
public event Action<int>? QueueMoveUpAction;
public event Action<int>? QueueMoveDownAction;
public event Action? DeleteFabricatingAction;
public event Action? OnClaimMiningPoints; // DeltaV
public List<ProtoId<LatheRecipePrototype>> Recipes = new();
public List<ProtoId<LatheCategoryPrototype>>? Categories;
public ProtoId<LatheCategoryPrototype>? CurrentCategory;
public EntityUid Entity;
private uint? _lastMiningPoints; // DeltaV: used to avoid Loc.GetString every frame
public LatheMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_spriteSystem = _entityManager.System<SpriteSystem>();
_lathe = _entityManager.System<LatheSystem>();
_materialStorage = _entityManager.System<MaterialStorageSystem>();
_miningPoints = _entityManager.System<MiningPointsSystem>(); // DeltaV
SearchBar.OnTextChanged += _ =>
{
PopulateRecipes();
};
AmountLineEdit.OnTextChanged += _ =>
{
if (int.TryParse(AmountLineEdit.Text, out var amount))
{
if (amount > LatheSystem.MaxItemsPerRequest)
AmountLineEdit.Text = LatheSystem.MaxItemsPerRequest.ToString();
else if (amount < 0)
AmountLineEdit.Text = "0";
}
PopulateRecipes();
};
FilterOption.OnItemSelected += OnItemSelected;
ServerListButton.OnPressed += a => OnServerListButtonPressed?.Invoke(a);
DeleteFabricating.OnPressed += _ => DeleteFabricatingAction?.Invoke();
}
public void SetEntity(EntityUid uid)
{
Entity = uid;
if (_entityManager.TryGetComponent<LatheComponent>(Entity, out var latheComponent))
{
if (!latheComponent.DynamicPacks.Any())
{
ServerListButton.Visible = false;
}
AmountLineEdit.SetText(latheComponent.DefaultProductionAmount.ToString());
}
// Begin DeltaV Additions: Mining points UI
MiningPointsContainer.Visible = _entityManager.TryGetComponent<MiningPointsComponent>(Entity, out var points);
MiningPointsClaimButton.OnPressed += _ => OnClaimMiningPoints?.Invoke();
if (points != null)
UpdateMiningPoints(points.Points);
// End DeltaV Additions
MaterialsList.SetOwner(Entity);
}
/// <summary>
/// DeltaV: Updates the UI elements for mining points.
/// </summary>
private void UpdateMiningPoints(uint points)
{
MiningPointsClaimButton.Disabled = points == 0 ||
_player.LocalSession?.AttachedEntity is not {} player ||
_miningPoints.TryFindIdCard(player) == null;
if (points == _lastMiningPoints)
return;
_lastMiningPoints = points;
MiningPointsLabel.Text = Loc.GetString("lathe-menu-mining-points", ("points", points));
}
protected override void Opened()
{
base.Opened();
if (_entityManager.TryGetComponent<LatheComponent>(Entity, out var latheComp))
{
AmountLineEdit.SetText(latheComp.DefaultProductionAmount.ToString());
}
}
/// <summary>
/// DeltaV: Update mining points UI whenever it changes.
/// </summary>
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
if (_entityManager.TryGetComponent<MiningPointsComponent>(Entity, out var points))
UpdateMiningPoints(points.Points);
}
/// <summary>
/// Populates the list of all the recipes
/// </summary>
public void PopulateRecipes()
{
var recipesToShow = new List<LatheRecipePrototype>();
foreach (var recipe in Recipes)
{
if (!_prototypeManager.Resolve(recipe, out var proto))
continue;
// Category filtering
if (CurrentCategory != null)
{
if (proto.Categories.Count <= 0)
continue;
var validRecipe = proto.Categories.Any(category => category == CurrentCategory);
if (!validRecipe)
continue;
}
if (SearchBar.Text.Trim().Length != 0)
{
if (_lathe.GetRecipeName(recipe).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;
RecipeCount.Text = Loc.GetString("lathe-menu-recipe-count", ("count", recipesToShow.Count));
var sortedRecipesToShow = recipesToShow.OrderBy(_lathe.GetRecipeName);
// Get the existing list of queue controls
var oldChildCount = RecipeList.ChildCount;
_entityManager.TryGetComponent(Entity, out LatheComponent? lathe);
int idx = 0;
foreach (var prototype in sortedRecipesToShow)
{
var canProduce = _lathe.CanProduce(Entity, prototype, quantity, component: lathe);
var tooltipFunction = () => GenerateTooltipText(prototype);
if (idx >= oldChildCount)
{
var control = new RecipeControl(_lathe, prototype, tooltipFunction, canProduce, GetRecipeDisplayControl(prototype));
control.OnButtonPressed += s =>
{
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount <= 0)
amount = 1;
RecipeQueueAction?.Invoke(s, amount);
};
RecipeList.AddChild(control);
}
else
{
var child = RecipeList.GetChild(idx) as RecipeControl;
if (child == null)
{
DebugTools.Assert($"Lathe menu recipe control at {idx} is not of type RecipeControl"); // Something's gone terribly wrong.
continue;
}
child.SetRecipe(prototype);
child.SetTooltipSupplier(tooltipFunction);
child.SetCanProduce(canProduce);
child.SetDisplayControl(GetRecipeDisplayControl(prototype));
}
idx++;
}
// Shrink list if new list is shorter than old list.
for (var childIdx = oldChildCount - 1; idx <= childIdx; childIdx--)
{
RecipeList.RemoveChild(childIdx);
}
}
private string GenerateTooltipText(LatheRecipePrototype prototype)
{
StringBuilder sb = new();
var multiplier = _entityManager.GetComponent<LatheComponent>(Entity).MaterialUseMultiplier;
foreach (var (id, amount) in prototype.Materials)
{
if (!_prototypeManager.Resolve(id, out var proto))
continue;
var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, multiplier);
var sheetVolume = _materialStorage.GetSheetVolume(proto);
var unit = Loc.GetString(proto.Unit);
var sheets = adjustedAmount / (float) sheetVolume;
var availableAmount = _materialStorage.GetMaterialAmount(Entity, 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);
}
var desc = _lathe.GetRecipeDescription(prototype);
if (!string.IsNullOrWhiteSpace(desc))
sb.AppendLine(Loc.GetString("lathe-menu-description-display", ("description", desc)));
// Remove last newline
if (sb.Length > 0)
sb.Remove(sb.Length - 1, 1);
return sb.ToString();
}
public void UpdateCategories()
{
// Get categories from recipes
var currentCategories = new List<ProtoId<LatheCategoryPrototype>>();
foreach (var recipeId in Recipes)
{
var recipe = _prototypeManager.Index(recipeId);
if (recipe.Categories.Count <= 0)
continue;
foreach (var category in recipe.Categories)
{
if (currentCategories.Contains(category))
continue;
currentCategories.Add(category);
}
}
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);
}
/// <summary>
/// Populates the build queue list with all queued items
/// </summary>
/// <param name="queue"></param>
public void PopulateQueueList(IReadOnlyCollection<LatheRecipeBatch> queue)
{
// Get the existing list of queue controls
var oldChildCount = QueueList.ChildCount;
var idx = 0;
foreach (var batch in queue)
{
var recipe = _prototypeManager.Index(batch.Recipe);
var itemName = _lathe.GetRecipeName(batch.Recipe);
string displayText;
if (batch.ItemsRequested > 1)
displayText = Loc.GetString("lathe-menu-item-batch", ("index", idx + 1), ("name", itemName), ("printed", batch.ItemsPrinted), ("total", batch.ItemsRequested));
else
displayText = Loc.GetString("lathe-menu-item-single", ("index", idx + 1), ("name", itemName));
if (idx >= oldChildCount)
{
var queuedRecipeBox = new QueuedRecipeControl(displayText, idx, GetRecipeDisplayControl(recipe));
queuedRecipeBox.OnDeletePressed += s => QueueDeleteAction?.Invoke(s);
queuedRecipeBox.OnMoveUpPressed += s => QueueMoveUpAction?.Invoke(s);
queuedRecipeBox.OnMoveDownPressed += s => QueueMoveDownAction?.Invoke(s);
QueueList.AddChild(queuedRecipeBox);
}
else
{
var child = QueueList.GetChild(idx) as QueuedRecipeControl;
if (child == null)
{
DebugTools.Assert($"Lathe menu queued recipe control at {idx} is not of type QueuedRecipeControl"); // Something's gone terribly wrong.
continue;
}
child.SetDisplayText(displayText);
child.SetIndex(idx);
child.SetDisplayControl(GetRecipeDisplayControl(recipe));
}
idx++;
}
// Shrink list if new list is shorter than old list.
for (var childIdx = oldChildCount - 1; idx <= childIdx; childIdx--)
{
QueueList.RemoveChild(childIdx);
}
}
public void SetQueueInfo(ProtoId<LatheRecipePrototype>? recipeProto)
{
FabricatingContainer.Visible = recipeProto != null;
if (recipeProto == null)
return;
var recipe = _prototypeManager.Index(recipeProto.Value);
FabricatingDisplayContainer.Children.Clear();
FabricatingDisplayContainer.AddChild(GetRecipeDisplayControl(recipe));
NameLabel.Text = _lathe.GetRecipeName(recipe);
}
public Control GetRecipeDisplayControl(LatheRecipePrototype recipe)
{
if (recipe.Icon != null)
{
var textRect = new TextureRect();
textRect.Texture = _spriteSystem.Frame0(recipe.Icon);
return textRect;
}
if (recipe.Result is { } result)
{
var entProtoView = new EntityPrototypeView();
entProtoView.SetPrototype(result);
return entProtoView;
}
return new Control();
}
private void OnItemSelected(OptionButton.ItemSelectedEventArgs obj)
{
FilterOption.SelectId(obj.Id);
if (obj.Id == -1)
{
CurrentCategory = null;
}
else
{
CurrentCategory = Categories?[obj.Id];
}
PopulateRecipes();
}
}