Files
crystall-punk-14/Content.Client/_CP14/Workbench/CP14WorkbenchWindow.xaml.cs
Red 6570b1b4b6 Public market + Solutions requests (#1503)
* Add platform markup to trading platform prices

Introduces a PlatformMarkupProcent field to CP14TradingPlatformComponent and applies it to price calculations in both client and server logic. Adds a new public trading platform entity with a higher markup and updates related sprites and metadata.

* Add platform markup percent to selling platforms

Introduces a PlatformMarkupProcent field to CP14SellingPlatformComponent, allowing selling platforms to apply a markup or markdown to item prices. Updates server logic to use this value when calculating payouts and UI state. Adds a new public selling platform entity with a lower markup in the trade_platform.yml prototype.

* Add Brad Potions trading requests prototype

Introduces a new YAML prototype defining multiple cp14TradingRequest entries for the BradPotions faction, each specifying requirements for various potion effects with minimum purity and amount.

* Apply platform markup to selling prices and payouts

Updated the selling request control and platform window to factor in the platform's markup percentage when displaying prices. Adjusted the server-side payout logic to multiply the payout by the platform markup percentage, ensuring consistency between client display and server rewards.

* replace mapping to public platforms

* bug + remove eepy potions request

* Clarify purity requirement in workbench reagent text

Updated the reagent requirement string in both English and Russian locale files to indicate that the required purity is '{$purity}%+' instead of just '{$purity}%'. This clarifies that the purity must be at least the specified percentage.
2025-07-06 00:09:03 +03:00

265 lines
8.1 KiB
C#

/*
* This file is sublicensed under MIT License
* https://github.com/space-wizards/space-station-14/blob/master/LICENSE.TXT
*/
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;
private readonly Dictionary<int, LocId> _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);
_skill = _entManager.System<CP14ClientSkillSystem>();
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<CP14WorkbenchUiRecipesEntry>();
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;
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;
}
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)
{
if (_player.LocalEntity is null)
return;
_cachedState = recipesState;
_categories.Clear();
OptionCategories.Clear();
OptionCategories.AddItem(Loc.GetString("cp14-recipe-category-all"), AllCategoryId);
var categories = new List<LocId>();
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;
}
}