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.
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
// Reverse map from FFXIV English action names to AnvilActionKind. Used by
|
||||
// the adapter when walking CraftAction + Action sheets - the adapter looks
|
||||
// each Name up here and skips (with a warning) anything that does not
|
||||
// resolve. The map is keyed by the exact English string the game ships
|
||||
// (Lumina ExtractText with the en-US client). Localised game strings are
|
||||
// resolved through the catalog's DisplayName field, not here.
|
||||
//
|
||||
// Patch maintenance: if a future FFXIV patch renames an action or adds a
|
||||
// new crafting action, this map and the AnvilActionKind enum need a
|
||||
// parallel update. The adapter's "unknown action name" warning is the
|
||||
// detection signal.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Anvil.RecipeData.Mechanics;
|
||||
|
||||
internal static class ActionKindByName
|
||||
{
|
||||
public static IReadOnlyDictionary<string, AnvilActionKind> Map { get; } =
|
||||
new Dictionary<string, AnvilActionKind>
|
||||
{
|
||||
// Progress
|
||||
["Basic Synthesis"] = AnvilActionKind.BasicSynthesis,
|
||||
["Careful Synthesis"] = AnvilActionKind.CarefulSynthesis,
|
||||
["Rapid Synthesis"] = AnvilActionKind.RapidSynthesis,
|
||||
["Intensive Synthesis"] = AnvilActionKind.IntensiveSynthesis,
|
||||
["Prudent Synthesis"] = AnvilActionKind.PrudentSynthesis,
|
||||
["Groundwork"] = AnvilActionKind.Groundwork,
|
||||
["Muscle Memory"] = AnvilActionKind.MuscleMemory,
|
||||
["Delicate Synthesis"] = AnvilActionKind.DelicateSynthesis,
|
||||
|
||||
// Quality
|
||||
["Basic Touch"] = AnvilActionKind.BasicTouch,
|
||||
["Standard Touch"] = AnvilActionKind.StandardTouch,
|
||||
["Advanced Touch"] = AnvilActionKind.AdvancedTouch,
|
||||
["Precise Touch"] = AnvilActionKind.PreciseTouch,
|
||||
["Preparatory Touch"] = AnvilActionKind.PreparatoryTouch,
|
||||
["Prudent Touch"] = AnvilActionKind.PrudentTouch,
|
||||
["Refined Touch"] = AnvilActionKind.RefinedTouch,
|
||||
["Byregot's Blessing"] = AnvilActionKind.ByregotsBlessing,
|
||||
["Hasty Touch"] = AnvilActionKind.HastyTouch,
|
||||
["Daring Touch"] = AnvilActionKind.DaringTouch,
|
||||
["Trained Eye"] = AnvilActionKind.TrainedEye,
|
||||
["Trained Finesse"] = AnvilActionKind.TrainedFinesse,
|
||||
["Reflect"] = AnvilActionKind.Reflect,
|
||||
["Tricks of the Trade"] = AnvilActionKind.TricksOfTheTrade,
|
||||
|
||||
// Buff
|
||||
["Veneration"] = AnvilActionKind.Veneration,
|
||||
["Innovation"] = AnvilActionKind.Innovation,
|
||||
["Great Strides"] = AnvilActionKind.GreatStrides,
|
||||
["Final Appraisal"] = AnvilActionKind.FinalAppraisal,
|
||||
["Manipulation"] = AnvilActionKind.Manipulation,
|
||||
["Waste Not"] = AnvilActionKind.WasteNot,
|
||||
["Waste Not II"] = AnvilActionKind.WasteNot2,
|
||||
|
||||
// Repair
|
||||
["Master's Mend"] = AnvilActionKind.MastersMend,
|
||||
["Immaculate Mend"] = AnvilActionKind.ImmaculateMend,
|
||||
|
||||
// Observe / Specialist
|
||||
["Observe"] = AnvilActionKind.Observe,
|
||||
["Careful Observation"] = AnvilActionKind.CarefulObservation,
|
||||
["Heart and Soul"] = AnvilActionKind.HeartAndSoul,
|
||||
["Quick Innovation"] = AnvilActionKind.QuickInnovation,
|
||||
["Trained Perfection"] = AnvilActionKind.TrainedPerfection,
|
||||
|
||||
// Cosmic Exploration
|
||||
["Material Miracle"] = AnvilActionKind.MaterialMiracle,
|
||||
["Stellar Steady Hand"] = AnvilActionKind.StellarSteadyHand,
|
||||
};
|
||||
|
||||
// The whitelist of Action-sheet (vs CraftAction-sheet) names. Used by the
|
||||
// adapter's Action-sheet walk: only rows whose Name matches one of these
|
||||
// are considered for inclusion (the Action sheet otherwise carries
|
||||
// thousands of combat / fishing / gathering rows that would pollute the
|
||||
// catalog). Note that Action-sheet entries with PrimaryCostValue == 0
|
||||
// (legacy ARR singletons) are filtered out separately.
|
||||
public static IReadOnlySet<string> ActionSheetWhitelist { get; } =
|
||||
new HashSet<string>
|
||||
{
|
||||
// Step-counter buff actions
|
||||
"Veneration",
|
||||
"Innovation",
|
||||
"Manipulation",
|
||||
"Waste Not",
|
||||
"Waste Not II",
|
||||
"Great Strides",
|
||||
"Final Appraisal",
|
||||
// Cosmic singletons
|
||||
"Material Miracle",
|
||||
"Stellar Steady Hand",
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Per-action mechanics row. Source of truth for everything the CraftAction
|
||||
// and Action Lumina sheets do not carry: category, CP cost, durability,
|
||||
// efficiency, IQ-stack bonus, charges, granted buff, and flags. The adapter
|
||||
// merges this with the per-job sheet rows (DisplayName, IconId,
|
||||
// LevelRequired, RowIdByClassJob, SpecialistOnly) to build an AnvilAction.
|
||||
//
|
||||
// CP cost lives in the table because the spec mechanics table is the
|
||||
// curated, version-verified source. The sheet's Cost column matches in
|
||||
// practice; if a patch drifts the two apart, the adapter logs the diff
|
||||
// while keeping the table value (so the simulator stays deterministic
|
||||
// against the spec-pinned numbers).
|
||||
//
|
||||
// Combo-state CP costs (StandardTouch is 18 CP after BasicTouch etc.) are
|
||||
// not modelled here - the table holds the base value and the simulator
|
||||
// applies the combo discount in its own scope.
|
||||
|
||||
namespace Anvil.RecipeData.Mechanics;
|
||||
|
||||
internal sealed record ActionMechanicsEntry
|
||||
{
|
||||
public required AnvilActionCategory Category { get; init; }
|
||||
public required short CpCost { get; init; }
|
||||
public required short DurabilityCost { get; init; }
|
||||
public required short EfficiencyProgress { get; init; }
|
||||
public required short EfficiencyQuality { get; init; }
|
||||
public required byte IqStackBonus { get; init; }
|
||||
public required byte ChargesPerCraft { get; init; }
|
||||
public required AnvilBuffKind? GrantsBuff { get; init; }
|
||||
public required AnvilActionFlags Flags { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,526 @@
|
||||
// Hardcoded action mechanics table. Values are FFXIV game data (Square
|
||||
// Enix' domain) and are cross-checked against the spec mechanics table in
|
||||
// 01-RecipeData.md §2.3.2 - which was itself verified against Artisan
|
||||
// (RawInformation/Character/Skills.cs). The table is the source of truth
|
||||
// for everything the simulator needs that the Lumina sheets do not carry:
|
||||
// category, CP cost, durability, efficiency, IQ-stack bonus, charges,
|
||||
// granted buff, and flags.
|
||||
//
|
||||
// Note on combo-state CP costs: StandardTouch and AdvancedTouch are listed
|
||||
// at their base CP (32 / 46). The combo discount (StandardTouch costs 18
|
||||
// after BasicTouch, AdvancedTouch costs 18 after StandardTouch) lives in
|
||||
// the simulator, not in the data layer.
|
||||
//
|
||||
// Note on TrainedEye efficiency: short.MaxValue is the sentinel for
|
||||
// "max-quality override" - the simulator recognises the Kind and applies
|
||||
// Recipe.QualityMax in one step instead of using the efficiency arithmetic.
|
||||
// Likewise ByregotsBlessing has base EfficiencyQuality=100; the simulator
|
||||
// adds the 20% per IQ stack itself.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Anvil.RecipeData.Mechanics;
|
||||
|
||||
internal static class ActionMechanicsTable
|
||||
{
|
||||
public static IReadOnlyDictionary<AnvilActionKind, ActionMechanicsEntry> Entries { get; } =
|
||||
new Dictionary<AnvilActionKind, ActionMechanicsEntry>
|
||||
{
|
||||
// ---- Progress actions ----
|
||||
[AnvilActionKind.BasicSynthesis] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.ProgressAction,
|
||||
CpCost = 0,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 120,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.CarefulSynthesis] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.ProgressAction,
|
||||
CpCost = 7,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 180,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.RapidSynthesis] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.ProgressAction,
|
||||
CpCost = 0,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 500,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
// 50% base success rate lives in the simulator, not in flags.
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.IntensiveSynthesis] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.ProgressAction,
|
||||
CpCost = 6,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 400,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.RequiresGoodOrExcellent,
|
||||
},
|
||||
[AnvilActionKind.PrudentSynthesis] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.ProgressAction,
|
||||
CpCost = 18,
|
||||
DurabilityCost = 5,
|
||||
EfficiencyProgress = 180,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
// WasteNot-lockout is a simulator rule (cannot use under WasteNot/2).
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.Groundwork] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.ProgressAction,
|
||||
CpCost = 18,
|
||||
DurabilityCost = 20,
|
||||
EfficiencyProgress = 360,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
// Halved efficiency when remaining durability < cost - simulator rule.
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.MuscleMemory] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.ProgressAction,
|
||||
CpCost = 6,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 300,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = AnvilBuffKind.MuscleMemory,
|
||||
Flags = AnvilActionFlags.RequiresFirstStep,
|
||||
},
|
||||
[AnvilActionKind.DelicateSynthesis] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.HybridProgressQuality,
|
||||
CpCost = 32,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 100,
|
||||
EfficiencyQuality = 100,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
|
||||
// ---- Quality actions ----
|
||||
[AnvilActionKind.BasicTouch] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
CpCost = 18,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 100,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.StandardTouch] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
// Base 32 CP; simulator drops to 18 after BasicTouch combo.
|
||||
CpCost = 32,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 125,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.AdvancedTouch] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
// Base 46 CP; simulator drops to 18 after StandardTouch combo.
|
||||
CpCost = 46,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 150,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.PreciseTouch] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
CpCost = 18,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 150,
|
||||
IqStackBonus = 1,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.RequiresGoodOrExcellent,
|
||||
},
|
||||
[AnvilActionKind.PreparatoryTouch] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
CpCost = 40,
|
||||
DurabilityCost = 20,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 200,
|
||||
IqStackBonus = 1,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.PrudentTouch] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
CpCost = 25,
|
||||
DurabilityCost = 5,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 100,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.RefinedTouch] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
CpCost = 24,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 100,
|
||||
// +1 only after BasicTouch combo; simulator gates the bonus.
|
||||
IqStackBonus = 1,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.ByregotsBlessing] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
CpCost = 24,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 0,
|
||||
// Base 100; simulator adds 20 per IQ stack and consumes them.
|
||||
EfficiencyQuality = 100,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.HastyTouch] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
CpCost = 0,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 100,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
// 60% base success rate is a simulator rule, not a flag.
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.DaringTouch] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
CpCost = 0,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 150,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.RequiresExpedience,
|
||||
},
|
||||
[AnvilActionKind.TrainedEye] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
CpCost = 250,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 0,
|
||||
// Sentinel: simulator treats short.MaxValue as
|
||||
// "fill Recipe.QualityMax in one step".
|
||||
EfficiencyQuality = short.MaxValue,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 1,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.RequiresFirstStep | AnvilActionFlags.RequiresLowLevelRecipe,
|
||||
},
|
||||
[AnvilActionKind.TrainedFinesse] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
CpCost = 32,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 100,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.RequiresFullIQ,
|
||||
},
|
||||
[AnvilActionKind.Reflect] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
CpCost = 6,
|
||||
DurabilityCost = 10,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 100,
|
||||
IqStackBonus = 1,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.RequiresFirstStep,
|
||||
},
|
||||
[AnvilActionKind.TricksOfTheTrade] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.QualityAction,
|
||||
CpCost = 0,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
// 0 EffQuality; simulator returns 20 CP via the Kind-specific path.
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.RequiresGoodOrExcellent,
|
||||
},
|
||||
|
||||
// ---- Buff actions ----
|
||||
[AnvilActionKind.Veneration] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.BuffAction,
|
||||
CpCost = 18,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = AnvilBuffKind.Veneration,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.Innovation] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.BuffAction,
|
||||
CpCost = 18,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = AnvilBuffKind.Innovation,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.GreatStrides] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.BuffAction,
|
||||
CpCost = 32,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = AnvilBuffKind.GreatStrides,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.FinalAppraisal] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.BuffAction,
|
||||
CpCost = 1,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = AnvilBuffKind.FinalAppraisal,
|
||||
Flags = AnvilActionFlags.NoBuffTick | AnvilActionFlags.NoConditionChange,
|
||||
},
|
||||
[AnvilActionKind.Manipulation] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.BuffAction,
|
||||
CpCost = 96,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = AnvilBuffKind.Manipulation,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.WasteNot] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.BuffAction,
|
||||
CpCost = 56,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = AnvilBuffKind.WasteNot,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.WasteNot2] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.BuffAction,
|
||||
CpCost = 98,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = AnvilBuffKind.WasteNot2,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
|
||||
// ---- Repair actions ----
|
||||
[AnvilActionKind.MastersMend] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.RepairAction,
|
||||
CpCost = 88,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
// +30 durability restoration lives in the simulator.
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.ImmaculateMend] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.RepairAction,
|
||||
CpCost = 112,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
// Full durability restoration lives in the simulator.
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
|
||||
// ---- Observe / Specialist ----
|
||||
[AnvilActionKind.Observe] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.ObserveAction,
|
||||
CpCost = 7,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 0,
|
||||
GrantsBuff = null,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
[AnvilActionKind.CarefulObservation] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.ObserveAction,
|
||||
CpCost = 0,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 3,
|
||||
GrantsBuff = null,
|
||||
Flags =
|
||||
AnvilActionFlags.SpecialistOnly
|
||||
| AnvilActionFlags.NoBuffTick
|
||||
| AnvilActionFlags.NoConditionChange,
|
||||
},
|
||||
[AnvilActionKind.HeartAndSoul] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.ObserveAction,
|
||||
CpCost = 0,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 1,
|
||||
GrantsBuff = AnvilBuffKind.HeartAndSoul,
|
||||
Flags =
|
||||
AnvilActionFlags.SpecialistOnly
|
||||
| AnvilActionFlags.NoBuffTick
|
||||
| AnvilActionFlags.NoConditionChange,
|
||||
},
|
||||
[AnvilActionKind.QuickInnovation] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.BuffAction,
|
||||
CpCost = 0,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 1,
|
||||
// Grants the regular Innovation buff; simulator hardcodes the
|
||||
// 1-step duration override on this trigger.
|
||||
GrantsBuff = AnvilBuffKind.Innovation,
|
||||
Flags =
|
||||
AnvilActionFlags.SpecialistOnly
|
||||
| AnvilActionFlags.NoBuffTick
|
||||
| AnvilActionFlags.NoConditionChange,
|
||||
},
|
||||
[AnvilActionKind.TrainedPerfection] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.BuffAction,
|
||||
CpCost = 0,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 1,
|
||||
GrantsBuff = AnvilBuffKind.TrainedPerfection,
|
||||
Flags = AnvilActionFlags.None,
|
||||
},
|
||||
|
||||
// ---- Cosmic Exploration (v0.1.0 catalog-present, recipe-gated off) ----
|
||||
[AnvilActionKind.MaterialMiracle] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.Cosmic,
|
||||
CpCost = 1,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 1,
|
||||
GrantsBuff = AnvilBuffKind.MaterialMiracleBuff,
|
||||
Flags =
|
||||
AnvilActionFlags.CosmicOnly
|
||||
| AnvilActionFlags.NoBuffTick
|
||||
| AnvilActionFlags.NoConditionChange,
|
||||
},
|
||||
[AnvilActionKind.StellarSteadyHand] = new ActionMechanicsEntry
|
||||
{
|
||||
Category = AnvilActionCategory.Cosmic,
|
||||
CpCost = 1,
|
||||
DurabilityCost = 0,
|
||||
EfficiencyProgress = 0,
|
||||
EfficiencyQuality = 0,
|
||||
IqStackBonus = 0,
|
||||
ChargesPerCraft = 2,
|
||||
// 100% success-rate effect lives in the StellarSteadyHand buff
|
||||
// via ActionCounter behavior - no Action-flag duplication.
|
||||
GrantsBuff = AnvilBuffKind.StellarSteadyHandBuff,
|
||||
Flags = AnvilActionFlags.CosmicOnly,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Per-buff mechanics row. Carries the constants that the FFXIV Status sheet
|
||||
// does not expose directly: the AnvilBuffBehavior class and the
|
||||
// duration triple (steps / seconds / actions, populated according to
|
||||
// Behavior). The Status sheet does deliver StatusId / Icon / StackMax;
|
||||
// the table mirrors them so consumers and the adapter cross-checks share
|
||||
// a single source.
|
||||
//
|
||||
// Step durations live in the table because FFXIV exposes them only in the
|
||||
// Status tooltip text ("for the next four steps"). String-parsing the
|
||||
// tooltip would be fragile across locales and patches.
|
||||
|
||||
namespace Anvil.RecipeData.Mechanics;
|
||||
|
||||
internal sealed record BuffMechanicsEntry
|
||||
{
|
||||
public required uint StatusId { get; init; }
|
||||
public required uint IconId { get; init; }
|
||||
public required byte StackMax { get; init; }
|
||||
public required AnvilBuffBehavior Behavior { get; init; }
|
||||
public required byte? DefaultDurationSteps { get; init; }
|
||||
public required float? DefaultDurationSeconds { get; init; }
|
||||
public required byte? DefaultDurationActions { get; init; }
|
||||
public required AnvilBuffCategory Category { get; init; }
|
||||
|
||||
// Older Status-sheet RowIds that still resolve to the same Anvil buff
|
||||
// (e.g. Innovation 259 -> 2189, Manipulation 258 -> 1164). Used by the
|
||||
// adapter's reverse mapping so save-state restore from earlier patches
|
||||
// still resolves correctly. Null when no legacy id exists.
|
||||
public required uint? LegacyStatusId { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
// Hardcoded buff mechanics table. Values cross-checked against the spec
|
||||
// mechanics table in 01-RecipeData.md §2.4 - which was itself verified
|
||||
// against ffxiv-datamining Status.csv (StatusId, Icon, StackMax) and
|
||||
// Artisan's tooltip-derived duration constants (Steps / Seconds / Actions).
|
||||
//
|
||||
// All step-counter durations come from the in-game tooltip text rather
|
||||
// than a structured Status sheet column - that is why the table is the
|
||||
// source of truth instead of a sheet read.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Anvil.RecipeData.Mechanics;
|
||||
|
||||
internal static class BuffMechanicsTable
|
||||
{
|
||||
public static IReadOnlyDictionary<AnvilBuffKind, BuffMechanicsEntry> Entries { get; } =
|
||||
new Dictionary<AnvilBuffKind, BuffMechanicsEntry>
|
||||
{
|
||||
[AnvilBuffKind.InnerQuiet] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 251,
|
||||
IconId = 217321,
|
||||
StackMax = 10,
|
||||
Behavior = AnvilBuffBehavior.StackBased,
|
||||
DefaultDurationSteps = null,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.QualityBooster,
|
||||
LegacyStatusId = null,
|
||||
},
|
||||
[AnvilBuffKind.WasteNot] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 252,
|
||||
IconId = 211701,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.StepCounter,
|
||||
DefaultDurationSteps = 4,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.DurabilitySaver,
|
||||
LegacyStatusId = null,
|
||||
},
|
||||
[AnvilBuffKind.WasteNot2] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 257,
|
||||
IconId = 211702,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.StepCounter,
|
||||
DefaultDurationSteps = 8,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.DurabilitySaver,
|
||||
LegacyStatusId = null,
|
||||
},
|
||||
[AnvilBuffKind.Veneration] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 2226,
|
||||
IconId = 216126,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.StepCounter,
|
||||
DefaultDurationSteps = 4,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.ProgressBooster,
|
||||
LegacyStatusId = null,
|
||||
},
|
||||
[AnvilBuffKind.GreatStrides] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 254,
|
||||
IconId = 216105,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.HybridStepOrUse,
|
||||
DefaultDurationSteps = 3,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.QualityBooster,
|
||||
LegacyStatusId = null,
|
||||
},
|
||||
[AnvilBuffKind.Innovation] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 2189,
|
||||
IconId = 211652,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.StepCounter,
|
||||
DefaultDurationSteps = 4,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.QualityBooster,
|
||||
LegacyStatusId = 259,
|
||||
},
|
||||
[AnvilBuffKind.FinalAppraisal] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 2190,
|
||||
IconId = 216124,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.HybridStepOrUse,
|
||||
DefaultDurationSteps = 5,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.Utility,
|
||||
LegacyStatusId = null,
|
||||
},
|
||||
[AnvilBuffKind.MuscleMemory] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 2191,
|
||||
IconId = 216125,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.HybridStepOrUse,
|
||||
DefaultDurationSteps = 5,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.ProgressBooster,
|
||||
LegacyStatusId = null,
|
||||
},
|
||||
[AnvilBuffKind.Manipulation] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 1164,
|
||||
IconId = 211651,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.StepCounter,
|
||||
DefaultDurationSteps = 8,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.DurabilityRepair,
|
||||
LegacyStatusId = 258,
|
||||
},
|
||||
[AnvilBuffKind.HeartAndSoul] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 2665,
|
||||
IconId = 216127,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.UseConsumed,
|
||||
DefaultDurationSteps = null,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.ConditionBypass,
|
||||
LegacyStatusId = null,
|
||||
},
|
||||
[AnvilBuffKind.Expedience] = new BuffMechanicsEntry
|
||||
{
|
||||
// Two non-crafting Status rows also carry the name "Expedience"
|
||||
// (RowIds 2712 / 3092). Reverse mapping must use the explicit
|
||||
// StatusId, not a name lookup, to avoid ambiguity.
|
||||
StatusId = 3812,
|
||||
IconId = 216128,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.InstantTrigger,
|
||||
DefaultDurationSteps = null,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.Utility,
|
||||
LegacyStatusId = null,
|
||||
},
|
||||
[AnvilBuffKind.TrainedPerfection] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 3813,
|
||||
IconId = 216129,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.UseConsumed,
|
||||
DefaultDurationSteps = null,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.Utility,
|
||||
LegacyStatusId = null,
|
||||
},
|
||||
|
||||
// ---- Cosmic Exploration (Patch 7.x; catalog-present in v0.1.0) ----
|
||||
[AnvilBuffKind.MaterialMiracleBuff] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 4220,
|
||||
IconId = 216254,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.TimedSeconds,
|
||||
DefaultDurationSteps = null,
|
||||
DefaultDurationSeconds = 45.0f,
|
||||
DefaultDurationActions = null,
|
||||
Category = AnvilBuffCategory.CosmicConditionShaper,
|
||||
LegacyStatusId = null,
|
||||
},
|
||||
[AnvilBuffKind.StellarSteadyHandBuff] = new BuffMechanicsEntry
|
||||
{
|
||||
StatusId = 4839,
|
||||
IconId = 211551,
|
||||
StackMax = 1,
|
||||
Behavior = AnvilBuffBehavior.ActionCounter,
|
||||
DefaultDurationSteps = null,
|
||||
DefaultDurationSeconds = null,
|
||||
DefaultDurationActions = 3,
|
||||
Category = AnvilBuffCategory.CosmicSuccessGuarantee,
|
||||
LegacyStatusId = null,
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Per-condition mechanics row. Mirrors AnvilCondition's stat multipliers and
|
||||
// the per-step base probability. BaseProbability defaults to 0.0 in the
|
||||
// table because FFXIV computes the spawn distribution from Recipe.IsExpert
|
||||
// and RecipeLevelTable.Stars at simulator runtime - the static catalog has
|
||||
// no useful per-recipe value to expose.
|
||||
|
||||
namespace Anvil.RecipeData.Mechanics;
|
||||
|
||||
internal sealed record ConditionMechanicsEntry
|
||||
{
|
||||
public required float QualityMultiplier { get; init; }
|
||||
public required float ProgressMultiplier { get; init; }
|
||||
public required float CpDiscountMultiplier { get; init; }
|
||||
public required float DurabilityDiscountMultiplier { get; init; }
|
||||
public required float BaseProbability { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// Hardcoded condition mechanics table. Values follow the spec mechanics
|
||||
// table in 01-RecipeData.md §2.5, which was itself verified against
|
||||
// Artisan's Simulator.cs.
|
||||
//
|
||||
// Robust is the Cosmic-Exploration condition variant. Its mechanics follow
|
||||
// the spec mechanics table (Quality-Mult = 1.0, Durability-Discount = 0.5,
|
||||
// no other stat changes) and Artisan Simulator.cs:430. The Sturdy follow-up
|
||||
// mapping that Artisan applies belongs to the simulator, not the catalog.
|
||||
// (Note: the enum doc comment in AnvilCondition.cs mentions a "+50% quality"
|
||||
// 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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Anvil.RecipeData.Mechanics;
|
||||
|
||||
internal static class ConditionMechanicsTable
|
||||
{
|
||||
public static IReadOnlyDictionary<AnvilConditionKind, ConditionMechanicsEntry> Entries { get; } =
|
||||
new Dictionary<AnvilConditionKind, ConditionMechanicsEntry>
|
||||
{
|
||||
[AnvilConditionKind.Normal] = new ConditionMechanicsEntry
|
||||
{
|
||||
QualityMultiplier = 1.0f,
|
||||
ProgressMultiplier = 1.0f,
|
||||
CpDiscountMultiplier = 1.0f,
|
||||
DurabilityDiscountMultiplier = 1.0f,
|
||||
BaseProbability = 0.0f,
|
||||
},
|
||||
[AnvilConditionKind.Good] = new ConditionMechanicsEntry
|
||||
{
|
||||
QualityMultiplier = 1.5f,
|
||||
ProgressMultiplier = 1.0f,
|
||||
CpDiscountMultiplier = 1.0f,
|
||||
DurabilityDiscountMultiplier = 1.0f,
|
||||
BaseProbability = 0.0f,
|
||||
},
|
||||
[AnvilConditionKind.Excellent] = new ConditionMechanicsEntry
|
||||
{
|
||||
QualityMultiplier = 4.0f,
|
||||
ProgressMultiplier = 1.0f,
|
||||
CpDiscountMultiplier = 1.0f,
|
||||
DurabilityDiscountMultiplier = 1.0f,
|
||||
BaseProbability = 0.0f,
|
||||
},
|
||||
[AnvilConditionKind.Poor] = new ConditionMechanicsEntry
|
||||
{
|
||||
QualityMultiplier = 0.5f,
|
||||
ProgressMultiplier = 1.0f,
|
||||
CpDiscountMultiplier = 1.0f,
|
||||
DurabilityDiscountMultiplier = 1.0f,
|
||||
BaseProbability = 0.0f,
|
||||
},
|
||||
[AnvilConditionKind.Centered] = new ConditionMechanicsEntry
|
||||
{
|
||||
QualityMultiplier = 1.0f,
|
||||
ProgressMultiplier = 1.0f,
|
||||
CpDiscountMultiplier = 1.0f,
|
||||
DurabilityDiscountMultiplier = 1.0f,
|
||||
// +25% success rate is a simulator rule, not a stat multiplier.
|
||||
BaseProbability = 0.0f,
|
||||
},
|
||||
[AnvilConditionKind.Sturdy] = new ConditionMechanicsEntry
|
||||
{
|
||||
QualityMultiplier = 1.0f,
|
||||
ProgressMultiplier = 1.0f,
|
||||
CpDiscountMultiplier = 1.0f,
|
||||
DurabilityDiscountMultiplier = 0.5f,
|
||||
BaseProbability = 0.0f,
|
||||
},
|
||||
[AnvilConditionKind.Pliant] = new ConditionMechanicsEntry
|
||||
{
|
||||
QualityMultiplier = 1.0f,
|
||||
ProgressMultiplier = 1.0f,
|
||||
CpDiscountMultiplier = 0.5f,
|
||||
DurabilityDiscountMultiplier = 1.0f,
|
||||
BaseProbability = 0.0f,
|
||||
},
|
||||
[AnvilConditionKind.Malleable] = new ConditionMechanicsEntry
|
||||
{
|
||||
QualityMultiplier = 1.0f,
|
||||
ProgressMultiplier = 1.5f,
|
||||
CpDiscountMultiplier = 1.0f,
|
||||
DurabilityDiscountMultiplier = 1.0f,
|
||||
BaseProbability = 0.0f,
|
||||
},
|
||||
[AnvilConditionKind.Primed] = new ConditionMechanicsEntry
|
||||
{
|
||||
QualityMultiplier = 1.0f,
|
||||
ProgressMultiplier = 1.0f,
|
||||
CpDiscountMultiplier = 1.0f,
|
||||
DurabilityDiscountMultiplier = 1.0f,
|
||||
// +2 step duration on newly-applied buffs is a simulator rule.
|
||||
BaseProbability = 0.0f,
|
||||
},
|
||||
[AnvilConditionKind.GoodOmen] = new ConditionMechanicsEntry
|
||||
{
|
||||
QualityMultiplier = 1.0f,
|
||||
ProgressMultiplier = 1.0f,
|
||||
CpDiscountMultiplier = 1.0f,
|
||||
DurabilityDiscountMultiplier = 1.0f,
|
||||
// Forces Good as the next condition - simulator rule.
|
||||
BaseProbability = 0.0f,
|
||||
},
|
||||
[AnvilConditionKind.Robust] = new ConditionMechanicsEntry
|
||||
{
|
||||
// Quality stays neutral; only durability is discounted.
|
||||
QualityMultiplier = 1.0f,
|
||||
ProgressMultiplier = 1.0f,
|
||||
CpDiscountMultiplier = 1.0f,
|
||||
DurabilityDiscountMultiplier = 0.5f,
|
||||
BaseProbability = 0.0f,
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user