From d501639756f7d056cc15f694e6f38ff3cd354c91 Mon Sep 17 00:00:00 2001 From: Samuel Bouchet Date: Fri, 13 Mar 2026 23:47:12 +0100 Subject: [PATCH] Polish localization, name truncation, adventure tokens, consume feedback, column headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Localize box names and rarity in PlaythroughCapture test events - Fix name truncation to account for 2-char prefix (► / ) in inventory table - Add AdventureToken detail panel showing linked adventure name - Show remaining quantity after consuming an item - Localize inventory column headers (Nom, Rareté, Qté in FR) --- content/strings/en.json | 5 ++ content/strings/fr.json | 5 ++ src/OpenTheBox/Program.cs | 6 +- .../Rendering/Panels/InventoryPanel.cs | 23 ++++-- suggestions_4.md | 78 +++++++++++++++++++ tests/OpenTheBox.Tests/UnitTest1.cs | 10 ++- 6 files changed, 118 insertions(+), 9 deletions(-) create mode 100644 suggestions_4.md diff --git a/content/strings/en.json b/content/strings/en.json index 4632fb2..fa7606f 100644 --- a/content/strings/en.json +++ b/content/strings/en.json @@ -478,8 +478,13 @@ "inventory.effect": "Effect", "inventory.press_enter_use": "Press Enter to use", "inventory.item_used": "{0} used!", + "inventory.item_used_qty": "{0} used! ({1} remaining)", "inventory.cosmetic_slot": "Slot", "inventory.lore_progress": "Collected", + "inventory.adventure": "Adventure", + "inventory.col.name": "Name", + "inventory.col.rarity": "Rarity", + "inventory.col.qty": "Qty", "rarity.common": "Common", "rarity.uncommon": "Uncommon", diff --git a/content/strings/fr.json b/content/strings/fr.json index f5ca25b..95be664 100644 --- a/content/strings/fr.json +++ b/content/strings/fr.json @@ -478,8 +478,13 @@ "inventory.effect": "Effet", "inventory.press_enter_use": "Appuyer sur Entrée pour utiliser", "inventory.item_used": "{0} utilisé !", + "inventory.item_used_qty": "{0} utilisé ! ({1} restant)", "inventory.cosmetic_slot": "Emplacement", "inventory.lore_progress": "Collectés", + "inventory.adventure": "Aventure", + "inventory.col.name": "Nom", + "inventory.col.rarity": "Rareté", + "inventory.col.qty": "Qté", "rarity.common": "Commun", "rarity.uncommon": "Peu commun", diff --git a/src/OpenTheBox/Program.cs b/src/OpenTheBox/Program.cs index 5d891aa..db68488 100644 --- a/src/OpenTheBox/Program.cs +++ b/src/OpenTheBox/Program.cs @@ -694,7 +694,11 @@ public static class Program case ResourceChangedEvent resEvt: var resName = _loc.Get($"resource.{resEvt.Type.ToString().ToLower()}"); string itemName = _loc.Get(item.Def.NameKey); - _renderer.ShowMessage(_loc.Get("inventory.item_used", itemName)); + int remaining = _state.Inventory.Where(i => i.DefinitionId == item.DefId).Sum(i => i.Quantity); + string usedMsg = remaining > 0 + ? _loc.Get("inventory.item_used_qty", itemName, remaining.ToString()) + : _loc.Get("inventory.item_used", itemName); + _renderer.ShowMessage(usedMsg); _renderer.ShowMessage($"{resName}: {resEvt.OldValue} → {resEvt.NewValue}"); AddEventLog($"🧪 {itemName} → {resName} {resEvt.OldValue}→{resEvt.NewValue}"); break; diff --git a/src/OpenTheBox/Rendering/Panels/InventoryPanel.cs b/src/OpenTheBox/Rendering/Panels/InventoryPanel.cs index ad19c42..18f3abe 100644 --- a/src/OpenTheBox/Rendering/Panels/InventoryPanel.cs +++ b/src/OpenTheBox/Rendering/Panels/InventoryPanel.cs @@ -156,12 +156,16 @@ public static class InventoryPanel { int maxRows = compact ? CompactVisibleRows : MaxVisibleRows; + string colName = loc?.Get("inventory.col.name") ?? "Name"; + string colRarity = loc?.Get("inventory.col.rarity") ?? "Rarity"; + string colQty = loc?.Get("inventory.col.qty") ?? "Qty"; + var table = new Table() .Border(TableBorder.Rounded) - .AddColumn(new TableColumn("[bold]Name[/]").Width(MaxNameWidth)) + .AddColumn(new TableColumn($"[bold]{Markup.Escape(colName)}[/]").Width(MaxNameWidth)) .AddColumn(new TableColumn("").Centered().Width(2)) - .AddColumn(new TableColumn("[bold]Rarity[/]").Centered()) - .AddColumn(new TableColumn("[bold]Qty[/]").RightAligned()); + .AddColumn(new TableColumn($"[bold]{Markup.Escape(colRarity)}[/]").Centered()) + .AddColumn(new TableColumn($"[bold]{Markup.Escape(colQty)}[/]").RightAligned()); var grouped = GetGroupedItems(state, registry); @@ -175,10 +179,11 @@ public static class InventoryPanel int globalIndex = clampedOffset + i; bool isSelected = globalIndex == selectedIndex; - // Resolve localized name, truncate if needed + // Resolve localized name, truncate to fit column with "► " or " " prefix string name = ResolveName(item, registry, loc); - if (name.Length > MaxNameWidth) - name = name[..(MaxNameWidth - 1)] + "…"; + int maxDisplayName = MaxNameWidth - 2; // account for prefix + if (name.Length > maxDisplayName) + name = name[..(maxDisplayName - 1)] + "…"; string catIcon = LocalizeCategory(item.Category, loc); string rarity = LocalizeRarity(item.Rarity, loc); @@ -322,6 +327,12 @@ public static class InventoryPanel string matForm = loc?.Get($"material.form.{(item.Def.MaterialForm ?? MaterialForm.Raw).ToString().ToLower()}") ?? item.Def.MaterialForm?.ToString() ?? "Raw"; rows.Add(new Markup($"[dim]{Markup.Escape(matType)} ({Markup.Escape(matForm)})[/]")); break; + + case ItemCategory.AdventureToken when item.Def?.AdventureTheme is not null: + string advName = loc?.Get($"adventure.name.{item.Def.AdventureTheme.Value}") ?? item.Def.AdventureTheme.Value.ToString(); + string advLabel = loc?.Get("inventory.adventure") ?? "Adventure"; + rows.Add(new Markup($"[dim]{Markup.Escape(advLabel)}: [bold]{Markup.Escape(advName)}[/][/]")); + break; } string title = loc?.Get("inventory.details") ?? "Details"; diff --git a/suggestions_4.md b/suggestions_4.md new file mode 100644 index 0000000..8321057 --- /dev/null +++ b/suggestions_4.md @@ -0,0 +1,78 @@ +# Suggestions d'amélioration #4 + +Basées sur l'analyse des rendus après suggestions_2 et suggestions_3. + +| # | Suggestion | Priorité | Statut | +|---|-----------|----------|--------| +| 1 | Localiser les noms de boîtes dans les événements du PlaythroughCapture | ★★★★★ | ✅ DONE | +| 2 | Tronquer les noms trop longs incluant le préfixe "► " (26 chars réels) | ★★★★★ | ✅ DONE | +| 3 | Panneau détails : ajouter un cas AdventureToken avec le nom d'aventure | ★★★★☆ | ✅ DONE | +| 4 | Afficher la quantité restante dans le message de consommation | ★★★★☆ | ✅ DONE | +| 5 | Localize the "Name" and "Qty" column headers in the inventory table | ★★★★☆ | ✅ DONE | +| 6 | Panneau "???" avec headers localisés dans FullLayout | ★★★☆☆ | | +| 7 | Afficher la progression globale dans le header de l'inventaire interactif | ★★★☆☆ | | +| 8 | Grouper les boîtes identiques dans le loot reveal (× N) | ★★☆☆☆ | | +| 9 | Ajouter un son de notification pour les lore fragments | ★★☆☆☆ | | +| 10 | Indicateur d'items actionnables dans la liste (★ à côté des consommables/lore) | ★★☆☆☆ | | + +--- + +## 1. Localiser les noms de boîtes dans les événements du PlaythroughCapture — ★★★★★ + +**Constat** : Le test PlaythroughCapture affiche `Received [Common] "box_of_boxes"` au lieu du nom localisé. Le code de test utilise `ire.Item.DefinitionId` directement pour les boîtes au lieu de résoudre via le registre. + +**Solution** : Utiliser le même pattern que `GetLocalizedName()` dans le test PlaythroughCapture. + +## 2. Tronquer les noms incluant le préfixe "► " — ★★★★★ + +**Constat** : Le nom sélectionné a un préfixe "► " (2 chars de plus), mais la troncature se fait à MaxNameWidth (24) sur le nom seul. Avec le préfixe, le nom effectif est 26 chars, ce qui peut déborder de la colonne. + +**Solution** : Ajuster la troncature pour tenir compte du préfixe dans la colonne, ou augmenter la largeur de la colonne Name. + +## 3. Panneau détails AdventureToken — ★★★★☆ + +**Constat** : Les items AdventureToken (badge spatial, boussole pirate, etc.) n'ont pas d'information contextuelle dans le panneau de détails. L'aventure associée n'est pas mentionnée. + +**Solution** : Ajouter un cas `AdventureToken` dans `RenderDetailPanel()` qui affiche le nom de l'aventure liée. + +## 4. Afficher la quantité restante après consommation — ★★★★☆ + +**Constat** : Quand on consomme un item, le message dit juste "Ration alimentaire utilisé !". On ne sait pas combien il en reste. + +**Solution** : Ajouter "× N restant(s)" au message de consommation dans HandleInventoryAction. + +## 5. Localiser les en-têtes de colonnes de l'inventaire — ★★★★☆ + +**Constat** : Les colonnes "Name", "Rarity" et "Qty" sont en anglais dans la table d'inventaire. + +**Solution** : Utiliser des clés de localisation `inventory.col.name`, `inventory.col.rarity`, `inventory.col.qty`. + +## 6. Panneau "???" localisé dans FullLayout — ★★★☆☆ + +**Constat** : Les panneaux pas encore débloqués dans le FullLayout affichent un header anglais. + +**Solution** : Localiser les headers "Stats", "Resources", "Inventory" dans les panels placeholder. + +## 7. Progression globale dans l'inventaire interactif — ★★★☆☆ + +**Constat** : L'inventaire interactif ne montre pas de contexte sur la progression globale du joueur. + +**Solution** : Optionnel — afficher le nombre de boîtes ouvertes ou le % de complétion dans le header. + +## 8. Grouper les boîtes dans le loot reveal — ★★☆☆☆ + +**Constat** : Quand plusieurs boîtes identiques sont obtenues, elles apparaissent chacune sur une ligne séparée. + +**Solution** : Grouper par nom et ajouter un "× N" pour les doublons. + +## 9. Son pour les lore fragments — ★★☆☆☆ + +**Constat** : Recevoir un fragment de lore est un moment clé de l'expérience narrative mais il passe inaperçu. + +**Solution** : Console.Beep sur Windows comme pour les Music items. + +## 10. Indicateur d'items actionnables — ★★☆☆☆ + +**Constat** : Dans l'inventaire, rien ne distingue visuellement les items sur lesquels on peut agir (consommables, lore) des items passifs. + +**Solution** : Ajouter un petit marqueur (★ ou ►) à côté des items actionnables. diff --git a/tests/OpenTheBox.Tests/UnitTest1.cs b/tests/OpenTheBox.Tests/UnitTest1.cs index e2c75a0..115d9c8 100644 --- a/tests/OpenTheBox.Tests/UnitTest1.cs +++ b/tests/OpenTheBox.Tests/UnitTest1.cs @@ -1431,8 +1431,14 @@ public class ContentValidationTests break; case ItemReceivedEvent ire: var idef = registry.GetItem(ire.Item.DefinitionId); - string iname = idef is not null ? loc.Get(idef.NameKey) : ire.Item.DefinitionId; - string rarity = (idef?.Rarity ?? ItemRarity.Common).ToString(); + var iboxDef = idef is null ? registry.GetBox(ire.Item.DefinitionId) : null; + string iname = idef is not null + ? loc.Get(idef.NameKey) + : iboxDef is not null + ? loc.Get(iboxDef.NameKey) + : ire.Item.DefinitionId; + var rarityEnum = idef?.Rarity ?? iboxDef?.Rarity ?? ItemRarity.Common; + string rarity = loc.Get($"rarity.{rarityEnum.ToString().ToLower()}"); report.AppendLine($"│ EVENT: Received [{rarity}] \"{iname}\""); break; case UIFeatureUnlockedEvent ufe: