Show unlocked workstations permanently in Atelier panel with idle indicator
Workstations now appear as soon as they are unlocked, not only when a job
is active. Idle stations display a waiting-for-ingredients status (⏳/~).
Add craft.idle localization key and update empty message to "no workshops
unlocked".
This commit is contained in:
parent
e3aa4cbfe7
commit
001f682320
4 changed files with 46 additions and 24 deletions
|
|
@ -395,7 +395,8 @@
|
|||
"craft.completed": "{0} finished crafting!",
|
||||
"craft.done": "Done",
|
||||
"craft.panel.title": "Workshops",
|
||||
"craft.panel.empty": "No active workshops.",
|
||||
"craft.idle": "Waiting for ingredients",
|
||||
"craft.panel.empty": "No workshops unlocked.",
|
||||
|
||||
"item.blueprint.foundry": "Foundry Blueprint",
|
||||
"item.blueprint.workbench": "Workbench Blueprint",
|
||||
|
|
|
|||
|
|
@ -395,7 +395,8 @@
|
|||
"craft.completed": "{0} a terminé la fabrication !",
|
||||
"craft.done": "Terminé",
|
||||
"craft.panel.title": "Ateliers",
|
||||
"craft.panel.empty": "Aucun atelier en activité.",
|
||||
"craft.idle": "En attente d'ingrédients",
|
||||
"craft.panel.empty": "Aucun atelier débloqué.",
|
||||
|
||||
"item.blueprint.foundry": "Plan de Fonderie",
|
||||
"item.blueprint.workbench": "Plan d'Établi",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using OpenTheBox.Core;
|
||||
using OpenTheBox.Core.Crafting;
|
||||
using OpenTheBox.Core.Enums;
|
||||
using OpenTheBox.Data;
|
||||
using OpenTheBox.Localization;
|
||||
using Spectre.Console;
|
||||
|
|
@ -8,46 +9,60 @@ using Spectre.Console.Rendering;
|
|||
namespace OpenTheBox.Rendering.Panels;
|
||||
|
||||
/// <summary>
|
||||
/// Renders active crafting workstations showing progress bars and completion status.
|
||||
/// Renders all unlocked crafting workstations: active jobs show progress,
|
||||
/// idle stations show a waiting indicator.
|
||||
/// </summary>
|
||||
public static class CraftingPanel
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds a renderable panel showing all active crafting jobs with their progress.
|
||||
/// Builds a renderable panel showing all unlocked workstations and their status.
|
||||
/// </summary>
|
||||
public static IRenderable Render(GameState state, ContentRegistry? registry = null, LocalizationManager? loc = null)
|
||||
{
|
||||
var rows = new List<IRenderable>();
|
||||
|
||||
foreach (var job in state.ActiveCraftingJobs.OrderBy(j => j.StartedAt))
|
||||
// Index active jobs by workstation
|
||||
var jobsByStation = state.ActiveCraftingJobs
|
||||
.GroupBy(j => j.Workstation)
|
||||
.ToDictionary(g => g.Key, g => g.OrderBy(j => j.StartedAt).First());
|
||||
|
||||
// Show all unlocked workstations in stable order
|
||||
foreach (var station in state.UnlockedWorkstations.OrderBy(w => w.ToString()))
|
||||
{
|
||||
string name = job.RecipeId;
|
||||
if (registry is not null && registry.Recipes.TryGetValue(job.RecipeId, out var recipe))
|
||||
{
|
||||
name = loc is not null ? loc.Get(recipe.NameKey) : recipe.NameKey;
|
||||
}
|
||||
string stationName = station.ToString();
|
||||
|
||||
string station = job.Workstation.ToString();
|
||||
|
||||
if (job.IsComplete)
|
||||
if (jobsByStation.TryGetValue(station, out var job))
|
||||
{
|
||||
// Completed: show checkmark
|
||||
rows.Add(new Markup($" [bold green]✓[/] [yellow]{Markup.Escape(station)}[/]: {Markup.Escape(name)} — [bold green]{Markup.Escape(loc?.Get("craft.done") ?? "Done")}[/]"));
|
||||
string recipeName = job.RecipeId;
|
||||
if (registry is not null && registry.Recipes.TryGetValue(job.RecipeId, out var recipe))
|
||||
recipeName = loc is not null ? loc.Get(recipe.NameKey) : recipe.NameKey;
|
||||
|
||||
if (job.IsComplete)
|
||||
{
|
||||
string check = UnicodeSupport.IsUtf8 ? "✓" : ">";
|
||||
rows.Add(new Markup($" [bold green]{check}[/] [yellow]{Markup.Escape(stationName)}[/]: {Markup.Escape(recipeName)} — [bold green]{Markup.Escape(loc?.Get("craft.done") ?? "Done")}[/]"));
|
||||
}
|
||||
else
|
||||
{
|
||||
int pct = (int)job.ProgressPercent;
|
||||
int barWidth = 20;
|
||||
int filled = barWidth * pct / 100;
|
||||
string bar = new string('#', filled) + new string('-', barWidth - filled);
|
||||
rows.Add(new Markup($" [yellow]{Markup.Escape(stationName)}[/]: {Markup.Escape(recipeName)} [[{bar}]] {pct}%"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// In progress: show progress bar
|
||||
int pct = (int)job.ProgressPercent;
|
||||
int barWidth = 20;
|
||||
int filled = barWidth * pct / 100;
|
||||
string bar = new string('#', filled) + new string('-', barWidth - filled);
|
||||
rows.Add(new Markup($" [yellow]{Markup.Escape(station)}[/]: {Markup.Escape(name)} [[{bar}]] {pct}%"));
|
||||
// Idle workstation
|
||||
string idle = UnicodeSupport.IsUtf8 ? "⏳" : "~";
|
||||
string idleText = loc?.Get("craft.idle") ?? "Waiting for ingredients";
|
||||
rows.Add(new Markup($" [dim]{idle} {Markup.Escape(stationName)}: {Markup.Escape(idleText)}[/]"));
|
||||
}
|
||||
}
|
||||
|
||||
if (rows.Count == 0)
|
||||
{
|
||||
rows.Add(new Markup($"[dim]{Markup.Escape(loc?.Get("craft.panel.empty") ?? "No active workshops.")}[/]"));
|
||||
rows.Add(new Markup($"[dim]{Markup.Escape(loc?.Get("craft.panel.empty") ?? "No workshops unlocked.")}[/]"));
|
||||
}
|
||||
|
||||
return new Panel(new Rows(rows))
|
||||
|
|
|
|||
|
|
@ -437,14 +437,15 @@ public class CraftingPanelTests
|
|||
var output = RenderHelper.RenderToString(CraftingPanel.Render(state));
|
||||
|
||||
Assert.NotEmpty(output);
|
||||
// Should contain the empty workshop message
|
||||
Assert.Contains("active", output, StringComparison.OrdinalIgnoreCase);
|
||||
// Should contain the empty workshop message (no workstations unlocked)
|
||||
Assert.Contains("unlocked", output, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Render_InProgressJob_ShowsProgressBar()
|
||||
{
|
||||
var state = GameState.Create("Test", Locale.EN);
|
||||
state.UnlockedWorkstations.Add(WorkstationType.Foundry);
|
||||
state.ActiveCraftingJobs.Add(new CraftingJob
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
|
|
@ -466,6 +467,7 @@ public class CraftingPanelTests
|
|||
public void Render_CompletedJob_ShowsDone()
|
||||
{
|
||||
var state = GameState.Create("Test", Locale.EN);
|
||||
state.UnlockedWorkstations.Add(WorkstationType.Foundry);
|
||||
state.ActiveCraftingJobs.Add(new CraftingJob
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
|
|
@ -492,6 +494,7 @@ public class CraftingPanelTests
|
|||
new RecipeResult("material_wood_refined", 1)));
|
||||
|
||||
var state = GameState.Create("Test", Locale.EN);
|
||||
state.UnlockedWorkstations.Add(WorkstationType.Foundry);
|
||||
state.ActiveCraftingJobs.Add(new CraftingJob
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
|
|
@ -513,6 +516,8 @@ public class CraftingPanelTests
|
|||
public void Render_MixedJobs_DoesNotThrow()
|
||||
{
|
||||
var state = GameState.Create("Test", Locale.EN);
|
||||
state.UnlockedWorkstations.Add(WorkstationType.Foundry);
|
||||
state.UnlockedWorkstations.Add(WorkstationType.Furnace);
|
||||
state.ActiveCraftingJobs.Add(new CraftingJob
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue