Files
Anvil/Anvil/RecipeData/AnvilRecipe.cs
T
JonKazama-Hellion 12013ba6e6 Modul 01 Hotfix A1: AnvilRecipe.RecipeLevelTableClassJobLevel ergaenzt
01-RecipeData Rev 6 (A1) added the field as the character-level
equivalent of a recipe's RecipeLevelTable, so module 02 can branch the
mod-penalty in BaseProgress and BaseQuality. The adapter resolves it
from the existing RecipeLevelTable.Value in the recipe walk.

- AnvilRecipe.cs: new required byte property after RecipeLevel, with
  a why-comment that points consumers at module 02 and warns against
  confusing the field with the row-id one above it.
- LuminaRecipeAdapter.cs: levelTable.ClassJobLevel assignment in the
  Recipe walk - levelTable was already resolved earlier in the loop.
- RecipeDataAdapterLoadStep.cs: new pass criterion #9 that asserts at
  least one recipe carries a positive RecipeLevelTableClassJobLevel.
  Catches a silent RecipeLevelTable-walk failure that would otherwise
  only surface inside the simulator. Existing Cosmic-surface check
  shifted to #10 to keep the spec numbering stable.

Build clean (0/0), csharpier clean.
2026-05-28 18:58:56 +02:00

67 lines
3.3 KiB
C#

// Plain-data record describing a single FFXIV crafting recipe. Public surface
// is intentionally BCL-only (no Lumina or Dalamud types) so the simulator and
// solver can be tested in xUnit without loading Dalamud.dll into the test
// AppDomain. The adapter (Internal/LuminaRecipeAdapter) builds these from the
// Recipe + RecipeLevelTable sheet pair and flattens both sheets into one
// record so consumers never have to do a two-sheet walk.
using System.Collections.Generic;
namespace Anvil.RecipeData;
public sealed record AnvilRecipe
{
public required uint RecipeId { get; init; }
public required uint OutputItemId { get; init; }
public required byte OutputAmount { get; init; }
public required uint ClassJobId { get; init; }
public required ushort RecipeLevel { get; init; }
// Character-level equivalent of the recipe's difficulty (e.g. 90 for an
// Endwalker 3-star, 100 for a Dawntrail endgame recipe). Not the same as
// RecipeLevel above - that field is the RecipeLevelTable row id, this one
// is its ClassJobLevel column. Module 02 uses it for the mod-penalty
// branch in BaseProgress and BaseQuality.
public required byte RecipeLevelTableClassJobLevel { get; init; }
public required int Difficulty { get; init; }
public required int QualityMax { get; init; }
public required byte Durability { get; init; }
public required int RequiredCraftsmanship { get; init; }
public required int RequiredControl { get; init; }
public required int QualityForHQ { get; init; }
public required bool IsExpertRecipe { get; init; }
public required bool CanHQ { 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
// touching the type. IsCosmic is still set by the recipe-detection path
// so the catalog knows which recipes belong to the Cosmic surface even
// while the mission flags stay dormant. The Splendorous-tool bonus is
// character-equipment state rather than recipe state, so it lives in
// the simulator's stats layer (CraftStats.HasSplendorousTool in module
// 02) instead of carrying a flag on this record.
//
// Adapter invariant (validated when the catalog is built):
// MissionHasMaterialMiracle == true implies IsCosmic == true
// MissionHasSteadyHand == true implies IsCosmic == true
//
// Inconsistent recipe states (sub-flag without master flag) throw at
// catalog-build time instead of silently corrupting the simulator.
public required bool IsCosmic { get; init; }
public required bool MissionHasMaterialMiracle { get; init; }
public required bool MissionHasSteadyHand { get; init; }
public required bool IsIshgardExpert { get; init; }
public required byte Stars { get; init; }
public required byte ProgressDivider { get; init; }
public required byte ProgressModifier { get; init; }
public required byte QualityDivider { get; init; }
public required byte QualityModifier { get; init; }
public required IReadOnlyList<AnvilRecipeIngredient> Ingredients { get; init; }
public required string DisplayName { get; init; }
}
public sealed record AnvilRecipeIngredient(uint ItemId, byte Amount);