Commit Graph

7 Commits

Author SHA1 Message Date
JonKazama-Hellion d8efc213e3 chore: add pre-push preflight + setup-hooks
Four-block pre-push gate matching the HellionChat pattern:

- Block A (verify-version-consistency.sh): csproj <Version> vs
  repo.json AssemblyVersion / TestingAssemblyVersion / DownloadLink*
  tag presence. Tolerant of repo.json being absent so v0.1.0 (which
  has no public release manifest yet) does not fail at push time;
  the missing-file path turns into the full cross-check once repo.json
  lands.
- Block B: dotnet build Anvil.sln -p:Platform=x64 -c Release. Platform
  pin is forge-wide (Forgeimizer v0.1.0 lesson: solution build defaults
  drift to AnyCPU otherwise).
- Block C: dotnet csharpier check ./Anvil. Catches the accumulated
  formatter drift that hit HellionChat v1.5.6 (12 files) when only
  build was checked per task.
- Block D: markdownlint-cli2 over the repo's *.md files (excludes node_modules,
  bin, obj, .claude, CLAUDE.md).

Plus setup-hooks.sh as the one-shot installer that points
core.hooksPath at .githooks/ and chmods the scripts.

README.md: MD040 fix for the custom-repo URL fence (added text language tag).
2026-05-27 22:25:35 +02:00
JonKazama-Hellion 90803bcd3c feat(bootstrap): wire DI host, hosted services, and plugin entry
The plugin is now loadable. Dalamud injects five services into the Plugin
constructor (Lightless pattern), the constructor builds the generic-host
container synchronously, and PluginLifecycle drives StartAsync from
LoadAsync. Module 02+ extends PluginHostFactory; this file set stays put.

- PluginHostDependencies (record): bundles the five Dalamud services
  v0.1.0 needs (IDalamudPluginInterface, IPluginLog, IDataManager,
  IFramework, ISelfTestRegistry).
- PluginHostFactory.Build: HostBuilder + AddDalamudLogging + the four
  service blocks (Dalamud services, Anvil singletons, ISelfTestStep
  collection, IHostedService init chain). Every registration uses an
  explicit factory lambda - the default activator only sees public
  ctors and Anvil follows the internal-sealed convention.
- PluginLifecycle (IAsyncDisposable): owns Host.StartAsync, marshals the
  Host.Dispose call onto the framework thread, idempotency guard via
  Interlocked, ExceptionDispatchInfo.Capture preserves the original
  load-throw stack when a failure cascades.
- Plugin (IAsyncDalamudPlugin): constructor injection of the five
  Dalamud services, builds the dependencies record, kicks off the host
  build, hands DisposeAsync to the lifecycle.
- Hosting/RecipeDataLoadHostedService: dispatches LuminaRecipeAdapter
  .LoadInternal onto the framework thread on StartAsync. Lumina sheet
  reads have no documented thread safety; conservative default.
- Hosting/SelfTestRegistrationHostedService: collects every
  ISelfTestStep registration from DI and hands them to
  ISelfTestRegistry.RegisterTestSteps once the host is up.
- SelfTest/RecipeDataAdapterLoadStep: nine pass criteria per spec §4.1
  (IsLoaded, RecipeCount > 0, ActionCount in 30..80, BuffsByKind.Count
  == 14, ConditionsByKind.Count == 11, Foods >= 30, Medicines >= 5,
  BasicSynthesis.RowIdByClassJob[8] == 100001, Cosmic-surface
  silent-degradation warning). Returns Waiting while the catalog is
  still loading.
- Infrastructure/Logging trio: DalamudLogger maps
  Microsoft.Extensions.Logging levels to IPluginLog, the provider
  emits an Anvil bootstrap banner with a Forge-Bronze fingerprint on
  ctor, the extension wires the provider into the ILoggingBuilder via
  TryAddEnumerable.
2026-05-27 21:58:38 +02:00
JonKazama-Hellion 401ebc9495 feat(recipedata): add LuminaRecipeAdapter + AnvilStrings resx
The adapter is the only place in the plugin that touches Lumina and
Dalamud types - everything outside Anvil.RecipeData.Internal stays on
the BCL surface (Critical Boundary per 00-Anvil-Scope §3.3).

- LuminaRecipeAdapter: walks Status, CraftAction, Action, Item, ItemFood,
  and Recipe + RecipeLevelTable, builds the AnvilXxx records, and
  swaps the CatalogState into RecipeDataCatalog in one atomic write.
  Cosmic actions normalise the Action-sheet ClassJob=-1 sentinel to
  RowIdByClassJob key 0 (spec §2.3.1); the legacy ARR singletons
  (Manipulation 278, Waste Not 279, Innovation 284, Waste Not II 285)
  drop out via the PrimaryCostValue == 0 filter (spec §3.1 #3). The
  CraftAction sheet wins the canonical icon at the CRP variant
  (ClassJobId 8). Per-job rows are cross-checked for Cost, ClassJobLevel,
  and Specialist consistency; mismatches log a warning.
- SH-15 option B is hard-wired: every AnvilRecipe ships with
  MissionHasMaterialMiracle = false, MissionHasSteadyHand = false, and
  IsSplendorCosmic = false in v0.1.0. IsCosmic is still set from
  Recipe.Number == 0 so the catalog can label Cosmic recipes; the WKS
  mission sheet chain is not walked. v0.2.0 will replace those constant
  assignments with the resolver and the rest of the adapter stays put.
- The adapter invariant (Cosmic sub-flag implies the master IsCosmic
  flag) throws on inconsistent recipe states at catalog-build time so
  v0.2.0+ catches a broken WKS resolver before it corrupts the simulator.
- ItemFood walk uses ItemAction.Data[0] (48 = "well fed", 49 =
  "medicated") instead of an ItemAction.Type column that does not exist
  in the current Lumina schema. The bonus extractor filters
  ItemFood.Params to the three crafting BaseParam ids (CP=11,
  Craftsmanship=70, Control=71). Item rows are populated only for the
  ItemIds that recipes / foods actually reference - no 50k-row mirror.
- AnvilStrings.resx + AnvilStrings.de.resx + AnvilStrings.Designer.cs:
  Localization layer for condition display names and the SelfTest step
  name. The adapter looks the condition strings up through the generated
  ResourceManager so culture-switching is a no-restart change later on.
2026-05-27 21:27:55 +02:00
JonKazama-Hellion eb5753eea6 feat(recipedata): add catalog + load result, wire csharpier
Two concerns bundled into one commit because the csharpier tool restore
reformatted four earlier files as soon as the manifest landed:

- .config/dotnet-tools.json: csharpier 1.2.6 as local tool, follows
  the standard .config layout (dotnet tool restore will find it).
- Anvil/packages.lock.json: produced by dotnet restore, pinned alongside
  the csproj's RestorePackagesWithLockFile=true.
- Anvil/RecipeData/RecipeDataLoadResult.cs: public record carrying the
  adapter's load-pass summary (counts + duration + warnings). Surfaced
  to the SelfTest step and future UI status surfaces.
- Anvil/RecipeData/RecipeDataCatalog.cs: public DI singleton with all
  seven dictionary lookups, the two helper methods (ActionsForClassJob,
  TryResolveActionRowId), and the four meta properties from spec §2.7.
  Backed by a private CatalogState record that the adapter swaps in via
  a volatile reference - readers either see the empty initial state or
  the fully populated post-load snapshot, never a partial mid-build.
  TryResolveActionRowId carries the Cosmic-sentinel logic so hook code
  does not need to know about the CosmicOnly flag itself.
- Reformatting in AnvilAction.cs, ActionMechanicsTable.cs,
  ConditionMechanicsTable.cs, and Anvil.csproj: csharpier-mandated
  line-wrap / blank-line tweaks. No semantic changes.
2026-05-27 20:54:14 +02:00
JonKazama-Hellion d7e8c42cc7 feat(recipedata): add hardcoded mechanics tables
Three game-mechanics tables for the simulator-relevant constants that the
Lumina sheets do not expose, plus the reverse name map the adapter uses
to walk the CraftAction + Action sheets.

- ActionMechanicsTable: 38 entries with Category, CP cost, durability,
  efficiency, IQ-stack bonus, charges, granted buff, and flags.
  Cross-checked against the spec mechanics table in 01-RecipeData.md
  §2.3.2 (verified there against Artisan's RawInformation/Character/
  Skills.cs). Combo-state CP costs (Standard/Advanced Touch) carry the
  base value; the simulator applies the discount. TrainedEye uses
  short.MaxValue as the sentinel for "fill Recipe.QualityMax in one
  step"; ByregotsBlessing carries base EfficiencyQuality=100 with the
  IQ multiplier added by the simulator.
- BuffMechanicsTable: 14 entries with StatusId, Icon, StackMax, Behavior,
  the three duration fields (Steps / Seconds / Actions populated per
  Behavior), Category, and LegacyStatusId for the older Innovation 259
  and Manipulation 258 ids. Cross-checked against ffxiv-datamining
  Status.csv (StatusId / Icon / StackMax) and Artisan's tooltip-derived
  step durations. Expedience uses the explicit StatusId 3812 - the other
  two "Expedience" rows (2712 / 3092) are non-crafting status effects
  that would otherwise produce an ambiguous name match.
- ConditionMechanicsTable: 11 entries with Quality / Progress / CP /
  Durability multipliers plus a BaseProbability slot. Robust mirrors
  Sturdy's durability discount and keeps Quality neutral (per the
  spec mechanics table - the enum doc comment in AnvilCondition.cs
  predates rev 5 and is noted as superseded in the table header).
  SplendorCosmic's Good=1.75 override lives in the simulator.
  BaseProbability stays 0.0; the static catalog has no useful per-recipe
  spawn distribution because FFXIV derives that from Recipe.IsExpert +
  RecipeLevelTable.Stars at runtime.
- ActionKindByName: reverse map from English action names to
  AnvilActionKind, plus the Action-sheet whitelist (seven step-counter
  buff actions + two Cosmic singletons) used to filter the Action sheet
  walk down to the crafting subset.
2026-05-27 20:25:52 +02:00
JonKazama-Hellion 47790a3f68 feat(recipedata): add plain-data records and enums
Module 01 public-API surface: six sealed records with init-only
properties plus their nine enums. All BCL-only - no Lumina or Dalamud
types in any public property, which is what keeps the simulator and
catalog xUnit-testable per the Critical Boundary in 00-Anvil-Scope §3.3.

- AnvilRecipe + AnvilRecipeIngredient: flat representation of the
  Recipe + RecipeLevelTable sheet pair with the five Cosmic-Exploration
  flags. v0.1.0 ships with MissionHas* always false (SH-15 option B);
  the schema stays in place so v0.2.0 can wire the WKS mission sheet
  without touching the type.
- AnvilItem: slim per-recipe / per-food item view (full ~50k-row item
  mirror is left to the UI layer).
- AnvilAction + AnvilActionKind + AnvilActionCategory + AnvilActionFlags:
  one logical action per Kind value with RowIdByClassJob mapping; Cosmic
  actions use ClassJobId 0 as the sentinel for "every crafter".
  AnvilActionFlags bit 1 << 2 is intentionally vacant (ConsumesGreatStrides
  was removed in spec rev 5; consumption logic lives in 02-CraftingSimulator).
- AnvilBuff + AnvilBuffKind + AnvilBuffBehavior + AnvilBuffCategory:
  static buff catalog entry with the duration field that matches Behavior
  (Steps / Seconds / Actions). Two Cosmic buffs (MaterialMiracleBuff,
  StellarSteadyHandBuff).
- AnvilCondition + AnvilConditionKind: eleven conditions including the
  Cosmic Robust variant. DisplayName comes from AnvilStrings.resx, not
  Lumina (the Status sheet does not expose the crafting condition labels
  cleanly).
- AnvilFood + AnvilFoodBonus + AnvilFoodKind + AnvilFoodStat: ItemFood
  mirror with IsRelative flag (percentage vs flat bonus).
2026-05-27 19:53:54 +02:00
JonKazama-Hellion 96553a849a chore: bootstrap Anvil repo skeleton
First commit on feature/v0.1.0 establishes the Hellion Forge plugin
scaffold:

- .gitattributes: Linux-first LF defaults, Windows-script CRLF exceptions,
  binary markers for fonts / images / archives. Pre-empts the
  Forgeimizer pre-push hook crash that was caused by Asriels CRLF default.
- .editorconfig: Hellion Forge .NET conventions
  (private fields _camelCase, Allman braces, var-preferred).
- .gitignore: VisualStudio baseline + secrets bucket + Anvil.Tests
  excluded (build-suite lives in the local Hellion Build test repo).
- LICENSE: MIT, Hellion Online Media 2026.
- NOTICE.md: goodwill attribution to Craftimizer and clean-room
  anonymisation note.
- PRIVACY.md: zero-telemetry statement matching 00-Anvil-Scope.
- README.md: v0.1.0 status + planned-feature outline + custom-repo URL.
- Anvil.sln + Anvil/Anvil.csproj: Dalamud.NET.Sdk/15.0.0, x64 platform
  pinned (forge-wide rule), Microsoft.Extensions.Hosting stack closed-range
  pin to 10.0.7 matching HellionChat v1.5.0. No DalamudPackager.targets
  override - SDK 15 default packager handles images / icon / image_urls.
- Anvil/Anvil.yaml: plugin manifest with explicit icon_url / image_urls
  (top-level fields required for SDK 15 default packager) and a v0.1.0
  changelog entry that names RecipeData as the first module.
2026-05-27 19:16:36 +02:00