diff --git a/tests/OpenTheBox.Tests/UnitTest1.cs b/tests/OpenTheBox.Tests/UnitTest1.cs index 54e6171..c6b53ed 100644 --- a/tests/OpenTheBox.Tests/UnitTest1.cs +++ b/tests/OpenTheBox.Tests/UnitTest1.cs @@ -1778,10 +1778,24 @@ public class ContentValidationTests } } + // Build workstation transformation map: itemId → [(recipeId, workstation), ...] + var craftedAt = new Dictionary>(); + foreach (var recipe in registry.Recipes.Values) + { + foreach (var ingredient in recipe.Ingredients) + { + if (!craftedAt.ContainsKey(ingredient.ItemDefinitionId)) + craftedAt[ingredient.ItemDefinitionId] = []; + craftedAt[ingredient.ItemDefinitionId].Add((recipe.Id, recipe.Workstation)); + } + if (!craftedAt.ContainsKey(recipe.Result.ItemDefinitionId)) + craftedAt[recipe.Result.ItemDefinitionId] = []; + craftedAt[recipe.Result.ItemDefinitionId].Add((recipe.Id, recipe.Workstation)); + } + // Build report var report = new System.Text.StringBuilder(); report.AppendLine("# Item Utility Report"); - report.AppendLine($"# Generated: {DateTime.UtcNow:yyyy-MM-dd HH:mm} UTC"); report.AppendLine($"# Total items: {registry.Items.Count}"); report.AppendLine($"# Total boxes: {registry.Boxes.Count}"); report.AppendLine($"# Total recipes: {registry.Recipes.Count}"); @@ -1831,13 +1845,22 @@ public class ContentValidationTests if (item.CosmeticSlot.HasValue) usages.Add($"Equip: {item.CosmeticSlot}={item.CosmeticValue}"); - // Crafting ingredient + // Crafting ingredient (with workstation) if (craftIngredientOf.TryGetValue(item.Id, out var recipes)) - usages.Add($"Craft ingredient: {string.Join(", ", recipes)}"); + { + foreach (var recipeId in recipes) + { + var recipe = registry.Recipes[recipeId]; + usages.Add($"Craft ingredient: {recipeId} @ {recipe.Workstation}"); + } + } - // Crafting output + // Crafting output (with workstation) if (craftOutputOf.TryGetValue(item.Id, out var outputRecipe)) - usages.Add($"Craft output: {outputRecipe}"); + { + var recipe = registry.Recipes[outputRecipe]; + usages.Add($"Craft output: {outputRecipe} @ {recipe.Workstation}"); + } // Interaction if (interactionItems.TryGetValue(item.Id, out var inters)) @@ -1880,8 +1903,9 @@ public class ContentValidationTests string reportText = report.ToString(); - // Write snapshot - var snapshotDir = Path.Combine("tests", "snapshots"); + // Write snapshot to repo root (tests/snapshots/) so it can be committed + var repoRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..")); + var snapshotDir = Path.Combine(repoRoot, "tests", "snapshots"); Directory.CreateDirectory(snapshotDir); var snapshotPath = Path.Combine(snapshotDir, "item_utility_report.txt"); diff --git a/tests/snapshots/item_utility_report.txt b/tests/snapshots/item_utility_report.txt new file mode 100644 index 0000000..26083e2 --- /dev/null +++ b/tests/snapshots/item_utility_report.txt @@ -0,0 +1,615 @@ +# Item Utility Report +# Total items: 146 +# Total boxes: 31 +# Total recipes: 18 + +## AdventureToken (31 items) +──────────────────────────────────────────────────────────────────────────────── + [****] space_coordinates (Epic) — Coordonnées mystérieuses + Loot: box_adventure_space + Adventure: Space + Craft ingredient: chart_star_navigation @ DrawingTable + Interaction: coordinates_map_combine + + [***] space_phone (Rare) — Numéro de téléphone alien + Loot: box_adventure_space + Adventure: Space + Interaction: phone_character_encounter + + [***] medieval_crest (Rare) — Blason de chevalier + Loot: box_adventure_medieval + Adventure: Medieval + Craft ingredient: engrave_royal_seal @ EngravingBench + + [***] medieval_scroll (Rare) — Parchemin ancien + Loot: box_adventure_medieval + Adventure: Medieval + Craft ingredient: engrave_royal_seal @ EngravingBench + + [***] medieval_seal (Epic) — Sceau royal + Loot: box_adventure_medieval + Adventure: Medieval + Craft output: engrave_royal_seal @ EngravingBench + + [***] pirate_compass (Rare) — Boussole enchantée + Loot: box_adventure_pirate + Adventure: Pirate + Interaction: pirate_map_compass + + [***] contemporary_phone (Common) — Smartphone + Loot: box_adventure_contemporary + Adventure: Contemporary + Interaction: phone_character_encounter + + [***] sentimental_phone (Rare) — Numéro de l'ex + Loot: box_adventure_sentimental + Adventure: Sentimental + Interaction: phone_character_encounter + + [***] prehistoric_tooth (Uncommon) — Dent de dinosaure + Loot: box_adventure_prehistoric + Adventure: Prehistoric + Craft ingredient: preserve_amber @ StasisChamber + + [***] prehistoric_amber (Rare) — Pierre d'ambre + Loot: box_adventure_prehistoric + Adventure: Prehistoric + Craft output: preserve_amber @ StasisChamber + + [****] cosmic_shard (Rare) — Éclat de nébuleuse + Loot: box_adventure_cosmic + Adventure: Cosmic + Craft ingredient: engineer_rocket_boots @ EngineerDesk + Craft ingredient: fuse_cosmic_crystal @ MatterSynthesizer + + [***] cosmic_crystal (Epic) — Cristal de quasar + Loot: box_adventure_cosmic + Adventure: Cosmic + Craft output: fuse_cosmic_crystal @ MatterSynthesizer + + [***] microscopic_bacteria (Uncommon) — Échantillon de bactérie sentiente + Loot: box_adventure_microscopic + Adventure: Microscopic + Craft ingredient: splice_glowing_dna @ GeneticModStation + + [***] microscopic_dna (Rare) — Brin d'ADN luminescent + Loot: box_adventure_microscopic + Adventure: Microscopic + Craft output: splice_glowing_dna @ GeneticModStation + + [***] microscopic_prion (Epic) — Prion amical (probablement) + Loot: box_adventure_microscopic + Adventure: Microscopic + Craft ingredient: splice_glowing_dna @ GeneticModStation + + [***] darkfantasy_ring (Rare) — Anneau maudit + Loot: box_adventure_darkfantasy + Adventure: DarkFantasy + Craft ingredient: enchant_dark_grimoire @ TransformationPentacle + + [***] darkfantasy_grimoire (Epic) — Grimoire du nécromancien + Loot: box_adventure_darkfantasy + Adventure: DarkFantasy + Craft output: enchant_dark_grimoire @ TransformationPentacle + + [**] space_badge (Rare) — Badge d'astronaute + Loot: box_adventure_space + Adventure: Space + + [**] pirate_feather (Common) — Plume de perroquet + Loot: box_adventure_pirate + Adventure: Pirate + + [**] pirate_rum (Uncommon) — Bouteille de rhum + Loot: box_adventure_pirate + Adventure: Pirate + + [**] contemporary_card (Uncommon) — Carte de crédit + Loot: box_adventure_contemporary + Adventure: Contemporary + + [**] contemporary_usb (Rare) — Clé USB suspecte + Loot: box_adventure_contemporary + Adventure: Contemporary + + [**] contemporary_badge (Rare) — Badge d'entreprise + Loot: box_adventure_contemporary + Adventure: Contemporary + + [**] sentimental_letter (Rare) — Lettre d'amour + Loot: box_adventure_sentimental + Adventure: Sentimental + + [**] sentimental_flower (Common) — Fleur séchée + Loot: box_adventure_sentimental + Adventure: Sentimental + + [**] sentimental_teddy (Uncommon) — Vieil ours en peluche + Loot: box_adventure_sentimental + Adventure: Sentimental + + [**] prehistoric_fossil (Epic) — Fossile de trilobite + Loot: box_adventure_prehistoric + Adventure: Prehistoric + + [**] cosmic_core (Legendary) — Cœur d'étoile + Loot: box_adventure_cosmic, box_black + Adventure: Cosmic + + [**] darkfantasy_gem (Legendary) — Gemme d'âme + Loot: box_adventure_darkfantasy, box_black + Adventure: DarkFantasy + + [**] destiny_token (Mythic) — Jeton du Destin + Loot: box_endgame(G) + Adventure: Destiny + + [*] medieval_sword (Epic) — Réplique d'Excalibur + Adventure: Medieval + +## Consumable (6 items) +──────────────────────────────────────────────────────────────────────────────── + [**] blood_vial (Rare) — Fiole de sang + Loot: box_epic, box_adventure_darkfantasy, box_supply + Consume: +5 Blood + + [**] gold_pouch (Common) — Bourse d'or + Loot: box_ok_tier, box_adventure_pirate, box_supply + Consume: +50 Gold + + [**] resource_max_gold (Rare) — Amélioration Capacité Or + Loot: box_improvement + Upgrade: max Gold + + [**] resource_max_blood (Rare) — Amélioration Capacité Sang + Loot: box_improvement + Upgrade: max Blood + + [**] music_melody (Rare) — Mélodie de boîte + Loot: box_music(G) + Ephemeral: plays melody + + [**] cookie_fortune (Common) — Fortune Cookie + Loot: box_cookie(G) + Ephemeral: fortune message + +## Cosmetic (42 items) +──────────────────────────────────────────────────────────────────────────────── + [***] cosmetic_eyes_sunglasses (Uncommon) — Lunettes de soleil + Loot: box_cool, box_style + Equip: Eyes=Sunglasses + Craft ingredient: craft_pilot_glasses @ Workbench + + [***] cosmetic_eyes_pilotglasses (Rare) — Lunettes d'aviateur + Loot: box_epic + Equip: Eyes=PilotGlasses + Craft output: craft_pilot_glasses @ Workbench + + [***] cosmetic_body_armored (Epic) — Armure + Loot: box_legendary + Equip: Body=Armored + Craft output: forge_armored_plate @ Forge + + [***] cosmetic_legs_short (Common) — Short + Loot: box_not_great, box_style + Equip: Legs=Short + Craft ingredient: engineer_rocket_boots @ EngineerDesk + + [***] cosmetic_legs_rocketboots (Epic) — Bottes à réaction + Loot: box_legendary + Equip: Legs=RocketBoots + Craft output: engineer_rocket_boots @ EngineerDesk + + [**] cosmetic_hair_short (Common) — Cheveux courts + Loot: box_not_great, box_style + Equip: Hair=Short + + [**] cosmetic_hair_long (Common) — Cheveux longs + Loot: box_ok_tier, box_style + Equip: Hair=Long + + [**] cosmetic_hair_ponytail (Uncommon) — Queue de cheval + Loot: box_cool, box_style + Equip: Hair=Ponytail + + [**] cosmetic_hair_braided (Uncommon) — Tresses + Loot: box_style + Equip: Hair=Braided + + [**] cosmetic_hair_cyberpunk (Rare) — Cheveux néon cyberpunk + Loot: box_epic, box_legendhair + Equip: Hair=Cyberpunk + + [**] cosmetic_hair_fire (Epic) — Cheveux en feu + Loot: box_legendhair + Equip: Hair=Fire + + [**] cosmetic_hair_stardust (Legendary) — Coiffure Poussière d'Étoile légendaire + Loot: box_legendhair(G) + Equip: Hair=StardustLegendary + + [**] cosmetic_eyes_blue (Common) — Yeux bleus + Loot: box_ok_tier, box_style + Equip: Eyes=Blue + + [**] cosmetic_eyes_green (Common) — Yeux verts + Loot: box_ok_tier, box_style + Equip: Eyes=Green + + [**] cosmetic_eyes_redorange (Uncommon) — Yeux rouge-orange + Loot: box_style + Equip: Eyes=RedOrange + + [**] cosmetic_eyes_brown (Common) — Yeux marron + Loot: box_not_great + Equip: Eyes=Brown + + [**] cosmetic_eyes_cybernetic (Epic) — Yeux cybernétiques + Loot: box_legendary + Equip: Eyes=CyberneticEyes + + [**] cosmetic_eyes_magician (Rare) — Lunettes de magicien + Loot: box_cool + Equip: Eyes=MagicianGlasses + + [**] cosmetic_body_tshirt (Common) — T-shirt basique + Loot: box_not_great, box_style + Equip: Body=RegularTShirt + + [**] cosmetic_body_sexy (Uncommon) — T-shirt sexy + Loot: box_cool, box_style + Equip: Body=SexyTShirt + + [**] cosmetic_body_suit (Rare) — Costume + Loot: box_epic + Equip: Body=Suit + + [**] cosmetic_body_robotic (Legendary) — Châssis robotique + Loot: box_legendary + Equip: Body=Robotic + + [**] cosmetic_legs_panty (Uncommon) — Culotte + Loot: box_style + Equip: Legs=Panty + + [**] cosmetic_legs_pegleg (Rare) — Jambe de bois + Loot: box_epic + Equip: Legs=PegLeg + + [**] cosmetic_legs_tentacles (Legendary) — Tentacules + Loot: box_legendary + Equip: Legs=Tentacles + + [**] cosmetic_arms_regular (Common) — Bras normaux + Loot: box_not_great, box_style + Equip: Arms=Regular + + [**] cosmetic_arms_mechanical (Epic) — Bras mécaniques + Loot: box_legendary + Equip: Arms=Mechanical + + [**] cosmetic_arms_wings (Legendary) — Ailes + Loot: box_legendary + Equip: Arms=Wings + + [**] cosmetic_arms_extrapair (Epic) — Quatre bras + Loot: box_epic + Equip: Arms=ExtraPair + + [**] tint_neon (Rare) — Néon + Loot: box_epic + Craft ingredient: craft_box_cool @ Forge + + [**] tint_silver (Rare) — Argent + Loot: box_epic + Craft ingredient: craft_pilot_glasses @ Workbench + + [**] tint_gold (Epic) — Or + Loot: box_legendhair + Craft ingredient: craft_box_epic @ Printer3D + + [**] endgame_crown (Mythic) — Couronne d'Accomplissement + Loot: box_endgame(G) + Equip: Hair=crown + + [*] cosmetic_gender_error (Mythic) — Nouveau genre (ERREUR : les boîtes n'ont pas de genre. La boîte s'excuse pour la confusion.) + Loot: box_style, box_black + + [*] tint_cyan (Common) — Cyan + Loot: box_ok_tier, box_style + + [*] tint_orange (Common) — Orange + Loot: box_ok_tier, box_style + + [*] tint_purple (Uncommon) — Violet + Loot: box_cool, box_style + + [*] tint_warmpink (Uncommon) — Rose chaud + Loot: box_style + + [*] tint_light (Common) — Clair + Loot: box_not_great + + [*] tint_dark (Common) — Sombre + Loot: box_not_great + + [*] tint_rainbow (Legendary) — Arc-en-ciel + Loot: box_legendhair, box_legendary + + [*] tint_void (Legendary) — Néant + Loot: box_legendary, box_adventure_cosmic, box_black + +## Key (6 items) +──────────────────────────────────────────────────────────────────────────────── + [****] space_key (Rare) — Clé d'accès au sas + Loot: box_adventure_space + Adventure: Space + Craft output: chart_star_navigation @ DrawingTable + Interaction: key_chest_auto + + [***] medieval_key (Rare) — Clé du donjon + Loot: box_adventure_medieval + Adventure: Medieval + Interaction: key_chest_auto + + [***] pirate_key (Rare) — Clé du coffre + Loot: box_adventure_pirate + Adventure: Pirate + Interaction: key_chest_auto + + [***] contemporary_key (Uncommon) — Clé d'appartement + Loot: box_adventure_contemporary + Adventure: Contemporary + Interaction: key_chest_auto + + [***] darkfantasy_key (Rare) — Clé en os + Loot: box_adventure_darkfantasy + Adventure: DarkFantasy + Interaction: key_chest_auto + + [**] mysterious_key (Rare) — Clé mystérieuse + Loot: box_adventure_medieval, box_adventure_pirate, box_adventure_contemporary, box_adventure_darkfantasy, box_black + Interaction: key_chest_auto + +## LoreFragment (10 items) +──────────────────────────────────────────────────────────────────────────────── + [*] lore_1 (Uncommon) — Fragment : Genèse + Loot: box_cool, box_story + + [*] lore_2 (Uncommon) — Fragment : L'Ordre + Loot: box_cool, box_story + + [*] lore_3 (Rare) — Fragment : L'Univers + Loot: box_epic, box_story + + [*] lore_4 (Rare) — Fragment : Première Ouverture + Loot: box_epic, box_story + + [*] lore_5 (Rare) — Fragment : Le Son + Loot: box_epic, box_story + + [*] lore_6 (Epic) — Fragment : Le Paradoxe + Loot: box_legendary, box_story + + [*] lore_7 (Rare) — Fragment : La Grève + Loot: box_story + + [*] lore_8 (Rare) — Fragment : La Machine + Loot: box_story + + [*] lore_9 (Rare) — Fragment : Le Manuel + Loot: box_story + + [*] lore_10 (Epic) — Fragment : Schrödinger + Loot: box_legendary, box_black, box_story + +## Map (2 items) +──────────────────────────────────────────────────────────────────────────────── + [****] space_map (Epic) — Carte stellaire + Loot: box_adventure_space + Adventure: Space + Craft ingredient: chart_star_navigation @ DrawingTable + Interaction: coordinates_map_combine + + [***] pirate_map (Epic) — Carte au trésor + Loot: box_adventure_pirate(G) + Adventure: Pirate + Interaction: pirate_map_compass + +## Material (15 items) +──────────────────────────────────────────────────────────────────────────────── + [****] material_bronze_ingot (Uncommon) — Bronze + Loot: box_ok_tier + Craft ingredient: craft_pilot_glasses @ Workbench + Craft ingredient: craft_box_ok_tier @ Workbench + Craft output: smelt_bronze_ingot @ Furnace + + [****] material_steel_ingot (Rare) — Acier + Loot: box_epic + Craft ingredient: forge_armored_plate @ Forge + Craft ingredient: craft_box_cool @ Forge + Craft output: smelt_steel_ingot @ Furnace + + [****] material_titanium_ingot (Epic) — Titane + Loot: box_legendary + Craft ingredient: engineer_rocket_boots @ EngineerDesk + Craft ingredient: craft_box_epic @ Printer3D + Craft output: smelt_titanium_ingot @ Furnace + + [**] material_wood_raw (Common) — Bois + Loot: box_not_great, box_adventure_prehistoric + Craft ingredient: refine_wood @ Foundry + + [**] material_wood_refined (Common) — Bois + Craft ingredient: craft_box_ok_tier @ Workbench + Craft output: refine_wood @ Foundry + + [**] material_bronze_raw (Common) — Bronze + Loot: box_not_great + Craft ingredient: smelt_bronze_ingot @ Furnace + + [**] material_iron_raw (Common) — Fer + Loot: box_ok_tier + Craft ingredient: smelt_iron_ingot @ Furnace + + [**] material_iron_ingot (Uncommon) — Fer + Loot: box_cool + Craft output: smelt_iron_ingot @ Furnace + + [**] material_steel_raw (Uncommon) — Acier + Loot: box_cool + Craft ingredient: smelt_steel_ingot @ Furnace + + [**] material_titanium_raw (Rare) — Titane + Loot: box_epic + Craft ingredient: smelt_titanium_ingot @ Furnace + + [**] material_carbonfiber_raw (Rare) — Fibre de carbone + Loot: box_legendary + Craft ingredient: forge_carbonfiber_sheet @ Forge + + [***] material_carbonfiber_sheet (Epic) — Fibre de carbone + Craft ingredient: forge_armored_plate @ Forge + Craft ingredient: craft_box_epic @ Printer3D + Craft output: forge_carbonfiber_sheet @ Forge + + [*] material_diamond_raw (Epic) — Diamant + Loot: box_legendary, box_black + + [*] material_diamond_gem (Legendary) — Diamant + Loot: box_black + + [NO USE] material_wood_nail (Common) — Bois + +## Meta (34 items) +──────────────────────────────────────────────────────────────────────────────── + [**] meta_colors (Rare) — Couleurs de texte + Loot: box_meta_basics + Unlock: TextColors + + [**] meta_extended_colors (Rare) — Palette de couleurs étendue + Loot: box_meta_deep + Unlock: ExtendedColors + + [**] meta_arrows (Epic) — Navigation avec les flèches + Loot: box_meta_interface + Unlock: ArrowKeySelection + + [**] meta_inventory (Rare) — Panneau d'inventaire + Loot: box_meta_basics + Unlock: InventoryPanel + + [**] meta_resources (Rare) — Panneau de caractéristiques + Loot: box_meta_basics + Unlock: ResourcePanel + + [**] meta_stats (Rare) — Panneau de statistiques + Loot: box_meta_basics + Unlock: StatsPanel + + [**] meta_portrait (Epic) — Panneau portrait + Loot: box_meta_basics + Unlock: PortraitPanel + + [**] meta_chat (Epic) — Panneau de discussion + Loot: box_meta_deep + Unlock: ChatPanel + + [**] meta_layout (Legendary) — Mise en page complète + Loot: box_meta_interface + Unlock: FullLayout + + [**] meta_animation (Uncommon) — Animation d'ouverture de boîte + Loot: box_meta_mastery + Unlock: BoxAnimation + + [**] meta_crafting (Epic) — Panneau de fabrication + Loot: box_meta_deep + Unlock: CraftingPanel + + [**] meta_autosave (Uncommon) — Sauvegarde automatique + Loot: box_meta_mastery + Unlock: AutoSave + + [**] meta_completion (Rare) — Suivi de complétion + Loot: box_meta_deep + Unlock: CompletionTracker + + [**] blueprint_foundry (Uncommon) — Plan de Fonderie + Loot: box_improvement + Workstation: Foundry + + [**] blueprint_workbench (Uncommon) — Plan d'Établi + Loot: box_improvement + Workstation: Workbench + + [**] blueprint_furnace (Uncommon) — Plan de Fourneau + Loot: box_improvement + Workstation: Furnace + + [**] blueprint_forge (Rare) — Plan de Forge + Loot: box_legendary + Workstation: Forge + + [**] blueprint_engineer (Rare) — Plan de Bureau d'Ingénieur + Loot: box_improvement + Workstation: EngineerDesk + + [**] blueprint_drawing (Uncommon) — Plan de Table à Dessin + Loot: box_improvement + Workstation: DrawingTable + + [**] blueprint_engraving (Rare) — Plan de Banc de Gravure + Loot: box_legendary + Workstation: EngravingBench + + [**] blueprint_pentacle (Epic) — Plan de Pentacle de Transformation + Loot: box_legendary + Workstation: TransformationPentacle + + [**] blueprint_printer (Epic) — Plan d'Imprimante 3D + Loot: box_legendary + Workstation: Printer3D + + [**] blueprint_synthesizer (Epic) — Plan de Synthétiseur de Matière + Loot: box_legendary + Workstation: MatterSynthesizer + + [**] blueprint_genetic (Epic) — Plan de Station de Modification Génétique + Loot: box_legendary + Workstation: GeneticModStation + + [**] blueprint_stasis (Epic) — Plan de Chambre de Stase + Loot: box_legendary + Workstation: StasisChamber + + [*] meta_shortcuts (Rare) — Raccourcis clavier + Unlock: KeyboardShortcuts + + [*] meta_resource_blood (Rare) — Sang + Loot: box_meta_resources + + [*] meta_resource_gold (Uncommon) — Or + Loot: box_meta_resources + + [*] meta_stat_strength (Rare) — Force + Loot: box_meta_mastery + + [*] meta_stat_intelligence (Rare) — Intelligence + Loot: box_meta_mastery + + [*] meta_stat_luck (Rare) — Chance + Loot: box_meta_mastery + + [*] meta_stat_charisma (Rare) — Charisme + Loot: box_meta_mastery + + [*] meta_stat_dexterity (Rare) — Dextérité + Loot: box_meta_mastery + + [*] meta_stat_wisdom (Rare) — Sagesse + Loot: box_meta_mastery + +## Orphan Items (no usage context) +──────────────────────────────────────────────────────────────────────────────── + material_wood_nail (Material, Common)