Compare commits

...

7 Commits

Author SHA1 Message Date
JonKazama-Hellion 05c3a84754 Merge branch 'feature/v0.1.1' 2026-05-28 20:20:05 +02:00
JonKazama-Hellion fd4fe52665 Modul 01 Hotfix A5: RefinedTouch IqStackBonus unconditional
01-RecipeData Rev 6 (A5) removed the BasicTouch combo gate from the
RefinedTouch IQ-stack bonus. The Phase-3 v0.1.0 value was already 1 -
only the inline comment suggested a simulator-side combo gate that
the spec polish moved out of the data layer entirely. The combo with
BasicTouch is now a UI/solver presentation hint, not a catalog rule.

This commit rewrites the comment to match: +1 stack is unconditional,
the combo lives outside the catalog. No numeric change.

Build clean (0/0), csharpier clean.
2026-05-28 20:04:04 +02:00
JonKazama-Hellion 01f35c65bc Modul 01 Hotfix A3: Reflect EfficiencyQuality 100 → 300
01-RecipeData Rev 6 (A3) corrects the live-game value: Reflect's
quality efficiency is 300, not 100. The Phase-3 v0.1.0 table missed
the post-6.0 game tuning. Spec mechanics table (§2.3.2) was already
on 300 - this brings the code in line.

Build clean (0/0), csharpier clean.
2026-05-28 19:57:29 +02:00
JonKazama-Hellion 825c5d3c5c Modul 01 Hotfix A4: AnvilRecipe.IsCollectable ergänzt
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.
2026-05-28 19:26:53 +02:00
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
JonKazama-Hellion ce3fdda51c Modul 01 Hotfix A2: AnvilRecipe.IsSplendorCosmic entfernt
Splendorous-Tool-Bonus is character-equipment state, not recipe state.
The flag moved to the simulator's stats layer (CraftStats.HasSplendorousTool
in module 02) per the 01-RecipeData Rev 6 spec polish (A2).

- AnvilRecipe.cs: IsSplendorCosmic property removed. Cosmic doc-block
  trimmed to four flags (IsCosmic + two MissionHas* + IsIshgardExpert)
  and now points consumers at the stats-layer for the Splendor branch.
- LuminaRecipeAdapter.cs: assignment in the recipe walk removed.
  ValidateCosmicInvariant drops the IsSplendorCosmic implication
  (MissionHas* checks unchanged).
- ConditionMechanicsTable.cs: header comment rewritten to describe
  Splendor as a stats-layer override rather than a recipe-flag override.

Sweep over Anvil/RecipeData/, Anvil/SelfTest/, Anvil/Hosting/,
Plugin.cs, and PluginHostFactory.cs: zero remaining references to
IsSplendorCosmic. Build clean (0/0), csharpier check clean.
2026-05-28 18:30:32 +02:00
JonKazama-Hellion 8e624e509a chore(v0.1.1): bump version and add repo.json + CHANGELOG.md
Versions-bump from 0.1.0 to 0.1.1 in csproj. Extend Anvil.yaml
changelog with the v0.1.1 block; the v0.1.0 entry stays below as
release history.

Add repo.json (Custom-Repo manifest, AssemblyVersion 0.1.1.0,
download links pointing at the v0.1.1 release tag) and CHANGELOG.md
(root-level history file with v0.1.1 hotfix notes and the v0.1.0
foundation block).

This is the scaffolding commit for the Module 01 spec-sync hotfix
cycle. The five spec-edit commits (A1, A2, A3, A4, A5) follow.
2026-05-28 17:59:36 +02:00
9 changed files with 180 additions and 21 deletions
+1 -1
View File
@@ -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>
+31
View File
@@ -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
+21 -5
View File
@@ -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;
+27 -1
View File
@@ -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
+44
View File
@@ -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).
+38
View File
@@ -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"
]
}
]