Polish localization, name truncation, adventure tokens, consume feedback, column headers

- 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)
This commit is contained in:
Samuel Bouchet 2026-03-13 23:47:12 +01:00
parent 04ee449f74
commit d501639756
6 changed files with 118 additions and 9 deletions

View file

@ -478,8 +478,13 @@
"inventory.effect": "Effect", "inventory.effect": "Effect",
"inventory.press_enter_use": "Press Enter to use", "inventory.press_enter_use": "Press Enter to use",
"inventory.item_used": "{0} used!", "inventory.item_used": "{0} used!",
"inventory.item_used_qty": "{0} used! ({1} remaining)",
"inventory.cosmetic_slot": "Slot", "inventory.cosmetic_slot": "Slot",
"inventory.lore_progress": "Collected", "inventory.lore_progress": "Collected",
"inventory.adventure": "Adventure",
"inventory.col.name": "Name",
"inventory.col.rarity": "Rarity",
"inventory.col.qty": "Qty",
"rarity.common": "Common", "rarity.common": "Common",
"rarity.uncommon": "Uncommon", "rarity.uncommon": "Uncommon",

View file

@ -478,8 +478,13 @@
"inventory.effect": "Effet", "inventory.effect": "Effet",
"inventory.press_enter_use": "Appuyer sur Entrée pour utiliser", "inventory.press_enter_use": "Appuyer sur Entrée pour utiliser",
"inventory.item_used": "{0} utilisé !", "inventory.item_used": "{0} utilisé !",
"inventory.item_used_qty": "{0} utilisé ! ({1} restant)",
"inventory.cosmetic_slot": "Emplacement", "inventory.cosmetic_slot": "Emplacement",
"inventory.lore_progress": "Collectés", "inventory.lore_progress": "Collectés",
"inventory.adventure": "Aventure",
"inventory.col.name": "Nom",
"inventory.col.rarity": "Rareté",
"inventory.col.qty": "Qté",
"rarity.common": "Commun", "rarity.common": "Commun",
"rarity.uncommon": "Peu commun", "rarity.uncommon": "Peu commun",

View file

@ -694,7 +694,11 @@ public static class Program
case ResourceChangedEvent resEvt: case ResourceChangedEvent resEvt:
var resName = _loc.Get($"resource.{resEvt.Type.ToString().ToLower()}"); var resName = _loc.Get($"resource.{resEvt.Type.ToString().ToLower()}");
string itemName = _loc.Get(item.Def.NameKey); 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}"); _renderer.ShowMessage($"{resName}: {resEvt.OldValue} → {resEvt.NewValue}");
AddEventLog($"🧪 {itemName} → {resName} {resEvt.OldValue}→{resEvt.NewValue}"); AddEventLog($"🧪 {itemName} → {resName} {resEvt.OldValue}→{resEvt.NewValue}");
break; break;

View file

@ -156,12 +156,16 @@ public static class InventoryPanel
{ {
int maxRows = compact ? CompactVisibleRows : MaxVisibleRows; 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() var table = new Table()
.Border(TableBorder.Rounded) .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("").Centered().Width(2))
.AddColumn(new TableColumn("[bold]Rarity[/]").Centered()) .AddColumn(new TableColumn($"[bold]{Markup.Escape(colRarity)}[/]").Centered())
.AddColumn(new TableColumn("[bold]Qty[/]").RightAligned()); .AddColumn(new TableColumn($"[bold]{Markup.Escape(colQty)}[/]").RightAligned());
var grouped = GetGroupedItems(state, registry); var grouped = GetGroupedItems(state, registry);
@ -175,10 +179,11 @@ public static class InventoryPanel
int globalIndex = clampedOffset + i; int globalIndex = clampedOffset + i;
bool isSelected = globalIndex == selectedIndex; 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); string name = ResolveName(item, registry, loc);
if (name.Length > MaxNameWidth) int maxDisplayName = MaxNameWidth - 2; // account for prefix
name = name[..(MaxNameWidth - 1)] + "…"; if (name.Length > maxDisplayName)
name = name[..(maxDisplayName - 1)] + "…";
string catIcon = LocalizeCategory(item.Category, loc); string catIcon = LocalizeCategory(item.Category, loc);
string rarity = LocalizeRarity(item.Rarity, 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"; 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)})[/]")); rows.Add(new Markup($"[dim]{Markup.Escape(matType)} ({Markup.Escape(matForm)})[/]"));
break; 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"; string title = loc?.Get("inventory.details") ?? "Details";

78
suggestions_4.md Normal file
View file

@ -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.

View file

@ -1431,8 +1431,14 @@ public class ContentValidationTests
break; break;
case ItemReceivedEvent ire: case ItemReceivedEvent ire:
var idef = registry.GetItem(ire.Item.DefinitionId); var idef = registry.GetItem(ire.Item.DefinitionId);
string iname = idef is not null ? loc.Get(idef.NameKey) : ire.Item.DefinitionId; var iboxDef = idef is null ? registry.GetBox(ire.Item.DefinitionId) : null;
string rarity = (idef?.Rarity ?? ItemRarity.Common).ToString(); 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}\""); report.AppendLine($"│ EVENT: Received [{rarity}] \"{iname}\"");
break; break;
case UIFeatureUnlockedEvent ufe: case UIFeatureUnlockedEvent ufe: