From 825c5d3c5c0d14d50f7d7593e4395d2d3efaa9c6 Mon Sep 17 00:00:00 2001 From: Jon Kazama Date: Thu, 28 May 2026 19:26:53 +0200 Subject: [PATCH] =?UTF-8?q?Modul=2001=20Hotfix=20A4:=20AnvilRecipe.IsColle?= =?UTF-8?q?ctable=20erg=C3=A4nzt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 01-RecipeData Rev 6 (A4) added the field as a Summarize-branch discriminator for module 02. Resolved as IsExpertRecipe OR the output item's AlwaysCollectable flag, the latter being the canonical FFXIV signal for collectable submissions (custom delivery, masterpiece supply, collectables shop). - AnvilRecipe.cs: new required bool property after CanHQ, with a why-comment pointing at module 02's Summarize branch and noting the OR-resolve. - LuminaRecipeAdapter.cs: resolve folded into the recipe walk via the same itemSheet.TryGetRow call that already fetches the display name - no bootstrap-order question, no 2-pass needed. hasOutputItem is hoisted so the OR-branch can guard against a missing item-sheet row without touching the default(Item) struct. - RecipeDataAdapterLoadStep.cs: new pass criterion #10 that asserts at least one recipe surfaces IsCollectable == true. Catches a silent resolve-path failure. Existing Cosmic-surface check shifts to #11 to keep numbering stable. Build clean (0/0), csharpier clean. --- Anvil/RecipeData/AnvilRecipe.cs | 7 +++++++ Anvil/RecipeData/Internal/LuminaRecipeAdapter.cs | 12 +++++++++--- Anvil/SelfTest/RecipeDataAdapterLoadStep.cs | 15 ++++++++++++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Anvil/RecipeData/AnvilRecipe.cs b/Anvil/RecipeData/AnvilRecipe.cs index 9c44c85..696e7f2 100644 --- a/Anvil/RecipeData/AnvilRecipe.cs +++ b/Anvil/RecipeData/AnvilRecipe.cs @@ -33,6 +33,13 @@ public sealed record AnvilRecipe public required bool IsExpertRecipe { get; init; } public required bool CanHQ { get; init; } + // True when the recipe is a collectable submission (custom delivery, + // masterpiece supply, collectables shop). Module 02's Summarize branch + // uses this discriminator. Resolved as IsExpertRecipe OR the output + // item's AlwaysCollectable flag - the latter is the canonical FFXIV + // signal for collectable submissions. + public required bool IsCollectable { get; init; } + // Cosmic Exploration (Patch 7.x). v0.1.0 ships with MissionHas* always // false (SH-15 option B) - the data schema stays in place so v0.2.0+ can // light up the surface by resolving the WKS mission sheet without diff --git a/Anvil/RecipeData/Internal/LuminaRecipeAdapter.cs b/Anvil/RecipeData/Internal/LuminaRecipeAdapter.cs index aa74530..dd5c274 100644 --- a/Anvil/RecipeData/Internal/LuminaRecipeAdapter.cs +++ b/Anvil/RecipeData/Internal/LuminaRecipeAdapter.cs @@ -557,9 +557,14 @@ internal sealed class LuminaRecipeAdapter var isCosmic = recipe.Number == 0; var ingredients = ResolveIngredients(recipe); - var displayName = itemSheet.TryGetRow(outputItemId, out var outputItem) - ? outputItem.Name.ExtractText() - : string.Empty; + // IsCollectable resolves off Item.AlwaysCollectable from the same + // sheet row we already fetch for the display name. Lumina is the + // canonical source here and folding it into the Recipe walk keeps + // the adapter single-pass (the Item walk runs later and rebuilds + // its own catalog from the same sheet). + var hasOutputItem = itemSheet.TryGetRow(outputItemId, out var outputItem); + var displayName = hasOutputItem ? outputItem.Name.ExtractText() : string.Empty; + var isCollectable = recipe.IsExpert || (hasOutputItem && outputItem.AlwaysCollectable); var anvilRecipe = new AnvilRecipe { @@ -577,6 +582,7 @@ internal sealed class LuminaRecipeAdapter QualityForHQ = recipe.CanHq ? qualityMax : 0, IsExpertRecipe = recipe.IsExpert, CanHQ = recipe.CanHq, + IsCollectable = isCollectable, IsCosmic = isCosmic, MissionHasMaterialMiracle = false, MissionHasSteadyHand = false, diff --git a/Anvil/SelfTest/RecipeDataAdapterLoadStep.cs b/Anvil/SelfTest/RecipeDataAdapterLoadStep.cs index 2763263..298cf84 100644 --- a/Anvil/SelfTest/RecipeDataAdapterLoadStep.cs +++ b/Anvil/SelfTest/RecipeDataAdapterLoadStep.cs @@ -157,7 +157,20 @@ internal sealed class RecipeDataAdapterLoadStep : ISelfTestStep failures++; } - // #10: Cosmic-surface sanity. v0.1.0 ships with SH-15 option B (all + // #10: IsCollectable sanity. FFXIV ships a non-zero number of + // collectable submission recipes (custom delivery, masterpiece + // supply, collectables shop), so the catalog must surface at + // least one. A flat false would mean the resolve path is broken. + if (!_catalog.RecipesById.Values.Any(r => r.IsCollectable)) + { + _logger.LogError( + "RecipeDataCatalog.RecipesById has no recipe with " + + "IsCollectable == true; collectable detection failed." + ); + failures++; + } + + // #11: Cosmic-surface sanity. v0.1.0 ships with SH-15 option B (all // MissionHas* flags forced false), so the WARN is expected whenever // any Cosmic recipe is in the catalog. The step still returns Pass // because the catalog itself is not broken - the Cosmic sub-surface