Compare commits
7 Commits
186e02c3ee
..
v0.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 05c3a84754 | |||
| fd4fe52665 | |||
| 01f35c65bc | |||
| 825c5d3c5c | |||
| 12013ba6e6 | |||
| ce3fdda51c | |||
| 8e624e509a |
+1
-1
@@ -1,6 +1,6 @@
|
||||
<Project Sdk="Dalamud.NET.Sdk/15.0.0">
|
||||
<PropertyGroup>
|
||||
<Version>0.1.0</Version>
|
||||
<Version>0.1.1</Version>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
|
||||
@@ -32,6 +32,37 @@ tags:
|
||||
- UI
|
||||
- Privacy
|
||||
changelog: |-
|
||||
**v0.1.1 — Module 01 Spec-Sync Hotfix (2026-05-28)**
|
||||
|
||||
Code-sync against the 01-RecipeData Rev 6 spec polish that landed
|
||||
during the Module 02 (CraftingSimulator) Phase-2 review. No new
|
||||
surface; the existing v0.1.0 module shape is brought in line with
|
||||
the spec edits the simulator design needs.
|
||||
|
||||
- `AnvilRecipe.RecipeLevelTableClassJobLevel` added. Carries the
|
||||
character-level equivalent of a recipe's difficulty (e.g. 90 for
|
||||
Endwalker 3-star, 100 for Dawntrail endgame). The simulator
|
||||
module uses this for the mod-penalty branch in BaseProgress and
|
||||
BaseQuality. Adapter resolves it from
|
||||
`RecipeLevelTable.Value.ClassJobLevel`.
|
||||
- `AnvilRecipe.IsCollectable` added. Marks a recipe as a
|
||||
collectable submission (custom delivery, masterpiece supply,
|
||||
collectables shop). The simulator module's Summarize branch
|
||||
uses this discriminator. Adapter resolves it from
|
||||
`recipe.IsExpert` or the output item's `IsCollectable` flag.
|
||||
- `AnvilRecipe.IsSplendorCosmic` removed. The Splendorous-tool
|
||||
bonus is character-equipment state, not recipe state — it lives
|
||||
in the stats layer of the simulator module instead.
|
||||
- `ActionMechanicsTable.Reflect` EfficiencyQuality corrected
|
||||
from 100 to 300, matching the live game value.
|
||||
- `ActionMechanicsTable.RefinedTouch` IqStackBonus comment
|
||||
clarified: the +1 IQ stack is unconditional. The combo gating
|
||||
is a UI/solver hint, not a data-layer rule.
|
||||
|
||||
Inspired by Craftimizer by Asriel Camora (MIT).
|
||||
|
||||
---
|
||||
|
||||
**v0.1.0 — Initial Release (2026-05-27)**
|
||||
|
||||
First Hellion Forge release of Anvil. Module 01 (RecipeData) is in
|
||||
|
||||
@@ -16,6 +16,14 @@ public sealed record AnvilRecipe
|
||||
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; }
|
||||
@@ -25,22 +33,30 @@ 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
|
||||
// touching the type. IsCosmic / IsSplendorCosmic are still set by the
|
||||
// recipe-detection path so the catalog knows which recipes belong to the
|
||||
// Cosmic surface even while the action flags stay dormant.
|
||||
// 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):
|
||||
// IsSplendorCosmic == true implies IsCosmic == true
|
||||
// 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 IsSplendorCosmic { get; init; }
|
||||
public required bool MissionHasMaterialMiracle { get; init; }
|
||||
public required bool MissionHasSteadyHand { get; init; }
|
||||
public required bool IsIshgardExpert { get; init; }
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -568,6 +573,7 @@ internal sealed class LuminaRecipeAdapter
|
||||
OutputAmount = recipe.AmountResult,
|
||||
ClassJobId = classJobId,
|
||||
RecipeLevel = (ushort)recipe.RecipeLevelTable.RowId,
|
||||
RecipeLevelTableClassJobLevel = levelTable.ClassJobLevel,
|
||||
Difficulty = difficulty,
|
||||
QualityMax = qualityMax,
|
||||
Durability = durability,
|
||||
@@ -576,8 +582,8 @@ internal sealed class LuminaRecipeAdapter
|
||||
QualityForHQ = recipe.CanHq ? qualityMax : 0,
|
||||
IsExpertRecipe = recipe.IsExpert,
|
||||
CanHQ = recipe.CanHq,
|
||||
IsCollectable = isCollectable,
|
||||
IsCosmic = isCosmic,
|
||||
IsSplendorCosmic = false,
|
||||
MissionHasMaterialMiracle = false,
|
||||
MissionHasSteadyHand = false,
|
||||
IsIshgardExpert = false,
|
||||
@@ -636,11 +642,6 @@ internal sealed class LuminaRecipeAdapter
|
||||
// time instead of producing a corrupt simulator state at runtime.
|
||||
private static void ValidateCosmicInvariant(AnvilRecipe recipe)
|
||||
{
|
||||
if (recipe.IsSplendorCosmic && !recipe.IsCosmic)
|
||||
throw new InvalidOperationException(
|
||||
$"AnvilRecipe {recipe.RecipeId}: IsSplendorCosmic implies IsCosmic, "
|
||||
+ "but IsCosmic is false."
|
||||
);
|
||||
if (recipe.MissionHasMaterialMiracle && !recipe.IsCosmic)
|
||||
throw new InvalidOperationException(
|
||||
$"AnvilRecipe {recipe.RecipeId}: MissionHasMaterialMiracle implies IsCosmic, "
|
||||
|
||||
@@ -209,7 +209,9 @@ internal static class ActionMechanicsTable
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 100,
|
||||
// +1 only after BasicTouch combo; simulator gates the bonus.
|
||||
// +1 IQ stack unconditionally. The BasicTouch combo that the
|
||||
// UI and solver hint at is a presentation layer rule, not a
|
||||
// data-layer one - the catalog stays free of the gate.
|
||||
IqStackBonus = 1,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
@@ -286,7 +288,7 @@ internal static class ActionMechanicsTable
|
||||
CpCost = 6,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 100,
|
||||
EfficiencyQuality = 300,
|
||||
IqStackBonus = 1,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
// behaviour for Robust - that wording predates the verified mechanics table
|
||||
// and the values below are the source of truth.)
|
||||
//
|
||||
// SplendorCosmic raises Good's quality multiplier from 1.5 to 1.75 on
|
||||
// recipes flagged IsSplendorCosmic. The override lives in the simulator;
|
||||
// the table holds the non-Splendor default.
|
||||
// Splendor (the Splendorous-tool bonus) raises Good's quality multiplier
|
||||
// from 1.5 to 1.75 when the character has the tool equipped. The override
|
||||
// lives in the simulator's stats layer (CraftStats.HasSplendorousTool in
|
||||
// module 02); the table holds the non-Splendor default.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
@@ -144,7 +144,33 @@ internal sealed class RecipeDataAdapterLoadStep : ISelfTestStep
|
||||
}
|
||||
}
|
||||
|
||||
// #9: Cosmic-surface sanity. v0.1.0 ships with SH-15 option B (all
|
||||
// #9: RecipeLevelTableClassJobLevel sanity. At least one recipe must
|
||||
// resolve a positive value from RecipeLevelTable.ClassJobLevel - if
|
||||
// every recipe shows zero the adapter never walked the second sheet,
|
||||
// which would silently break the simulator's mod-penalty branch.
|
||||
if (!_catalog.RecipesById.Values.Any(r => r.RecipeLevelTableClassJobLevel > 0))
|
||||
{
|
||||
_logger.LogError(
|
||||
"RecipeDataCatalog.RecipesById has no recipe with "
|
||||
+ "RecipeLevelTableClassJobLevel > 0; RecipeLevelTable walk failed."
|
||||
);
|
||||
failures++;
|
||||
}
|
||||
|
||||
// #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
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Changelog
|
||||
|
||||
All notable Anvil releases. See the Gitea release history for prior builds.
|
||||
|
||||
## v0.1.1 — Module 01 Spec-Sync Hotfix (2026-05-28)
|
||||
|
||||
Code-sync against the 01-RecipeData Rev 6 spec polish that landed during
|
||||
the Module 02 (CraftingSimulator) Phase-2 review. No new surface; the
|
||||
existing v0.1.0 module shape is brought in line with the spec edits the
|
||||
simulator design needs.
|
||||
|
||||
- `AnvilRecipe.RecipeLevelTableClassJobLevel` added. Carries the
|
||||
character-level equivalent of a recipe's difficulty (e.g. 90 for
|
||||
Endwalker 3-star, 100 for Dawntrail endgame). The simulator module
|
||||
uses this for the mod-penalty branch in BaseProgress and BaseQuality.
|
||||
Adapter resolves it from `RecipeLevelTable.Value.ClassJobLevel`.
|
||||
- `AnvilRecipe.IsCollectable` added. Marks a recipe as a collectable
|
||||
submission (custom delivery, masterpiece supply, collectables shop).
|
||||
The simulator module's Summarize branch uses this discriminator.
|
||||
Adapter resolves it from `recipe.IsExpert` or the output item's
|
||||
`IsCollectable` flag.
|
||||
- `AnvilRecipe.IsSplendorCosmic` removed. The Splendorous-tool bonus
|
||||
is character-equipment state, not recipe state — it lives in the
|
||||
stats layer of the simulator module instead.
|
||||
- `ActionMechanicsTable.Reflect` EfficiencyQuality corrected from 100
|
||||
to 300, matching the live game value.
|
||||
- `ActionMechanicsTable.RefinedTouch` IqStackBonus comment clarified:
|
||||
the +1 IQ stack is unconditional. The combo gating is a UI/solver
|
||||
hint, not a data-layer rule.
|
||||
|
||||
Inspired by Craftimizer by Asriel Camora (MIT).
|
||||
|
||||
## v0.1.0 — Initial Release (2026-05-27)
|
||||
|
||||
First Hellion Forge release of Anvil. Module 01 (RecipeData) is in
|
||||
place: catalogues every FFXIV crafting recipe, item, action, buff,
|
||||
condition, and food/medicine from Lumina sheets, surfaces them through
|
||||
a Dalamud-free read-only API, and verifies the load via the `/xlperf`
|
||||
SelfTest step.
|
||||
|
||||
Note: This release is the foundation layer. UI windows, simulator,
|
||||
solver, macros, hooks, and the IPC provider follow in v0.2.0+.
|
||||
|
||||
Inspired by Craftimizer by Asriel Camora (MIT).
|
||||
@@ -0,0 +1,38 @@
|
||||
[
|
||||
{
|
||||
"Author": "Jon Kazama (Hellion Forge)",
|
||||
"Name": "Anvil",
|
||||
"InternalName": "Anvil",
|
||||
"AssemblyVersion": "0.1.1.0",
|
||||
"Description": "A Hellion Forge plugin — privacy-focused FFXIV crafting helper.\n\nAnvil is a clean-room crafting plugin for FINAL FANTASY XIV. It provides a state-machine crafting simulator, a wrapper for the Raphael Rust solver, a recipe-note overlay, a synth-helper overlay, a macro editor, recipe bookmarks, and an opt-in Auto-Craft hook.\n\nPrivacy:\n- Zero telemetry, no network calls\n- Bookmarks and settings stay on your machine\n- Auto-Craft is OFF by default with an explicit warning modal\n\nStatus: v0.1.1 ships the spec-sync hotfix for the Module 01 (RecipeData) foundation. UI windows, simulator, solver wrapper, macro engine, and the IPC provider follow in v0.2.0+.\n\nCosmic Exploration support: planned for v0.2.0 (recipe-flag schema is already in place, mission-flag resolution is deferred).\n\nInspired by Craftimizer by Asriel Camora (MIT). Anvil is an independent re-implementation; no code was ported from Craftimizer.",
|
||||
"ApplicableVersion": "any",
|
||||
"RepoUrl": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Anvil",
|
||||
"Tags": [
|
||||
"Crafting",
|
||||
"Macros",
|
||||
"Solver",
|
||||
"UI",
|
||||
"Privacy"
|
||||
],
|
||||
"DalamudApiLevel": 15,
|
||||
"LoadRequiredState": 0,
|
||||
"LoadSync": false,
|
||||
"CanUnloadAsync": false,
|
||||
"LoadPriority": 0,
|
||||
"Punchline": "A Hellion Forge plugin — privacy-focused FFXIV crafting helper with simulator, solver, and bookmarks.",
|
||||
"Changelog": "**v0.1.1 — Module 01 Spec-Sync Hotfix (2026-05-28)**\n\nCode-sync against the 01-RecipeData Rev 6 spec polish that landed during the Module 02 (CraftingSimulator) Phase-2 review. No new surface; the existing v0.1.0 module shape is brought in line with the spec edits the simulator design needs.\n\n- `AnvilRecipe.RecipeLevelTableClassJobLevel` added. Carries the character-level equivalent of a recipe's difficulty (e.g. 90 for Endwalker 3-star, 100 for Dawntrail endgame). The simulator module uses this for the mod-penalty branch in BaseProgress and BaseQuality. Adapter resolves it from `RecipeLevelTable.Value.ClassJobLevel`.\n- `AnvilRecipe.IsCollectable` added. Marks a recipe as a collectable submission (custom delivery, masterpiece supply, collectables shop). The simulator module's Summarize branch uses this discriminator. Adapter resolves it from `recipe.IsExpert` or the output item's `IsCollectable` flag.\n- `AnvilRecipe.IsSplendorCosmic` removed. The Splendorous-tool bonus is character-equipment state, not recipe state — it lives in the stats layer of the simulator module instead.\n- `ActionMechanicsTable.Reflect` EfficiencyQuality corrected from 100 to 300, matching the live game value.\n- `ActionMechanicsTable.RefinedTouch` IqStackBonus comment clarified: the +1 IQ stack is unconditional. The combo gating is a UI/solver hint, not a data-layer rule.\n\nInspired by Craftimizer by Asriel Camora (MIT).\n\n---\n\n**v0.1.0 — Initial Release (2026-05-27)**\n\nFirst Hellion Forge release of Anvil. Module 01 (RecipeData) is in place: catalogues every FFXIV crafting recipe, item, action, buff, condition, and food/medicine from Lumina sheets, surfaces them through a Dalamud-free read-only API, and verifies the load via the `/xlperf` SelfTest step.\n\nNote: This release is the foundation layer. UI windows, simulator, solver, macros, hooks, and the IPC provider follow in v0.2.0+.\n\nInspired by Craftimizer by Asriel Camora (MIT).\n\n---\n\nFull history: https://gitea.hellion-forge.cloud/JonKazama-Hellion/Anvil/releases",
|
||||
"AcceptsFeedback": true,
|
||||
"DownloadLinkInstall": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Anvil/releases/download/v0.1.1/latest.zip",
|
||||
"DownloadLinkUpdate": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Anvil/releases/download/v0.1.1/latest.zip",
|
||||
"DownloadLinkTesting": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Anvil/releases/download/v0.1.1/latest.zip",
|
||||
"TestingAssemblyVersion": "0.1.1.0",
|
||||
"IconUrl": "https://gitea.hellion-forge.cloud/JonKazama-Hellion/Anvil/raw/branch/main/Anvil/images/icon.png",
|
||||
"ImageUrls": [],
|
||||
"DownloadCount": 0,
|
||||
"IsHide": false,
|
||||
"IsTestingExclusive": false,
|
||||
"CategoryTags": [
|
||||
"utility"
|
||||
]
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user