b598c03e9e
Reformats the entire Craftimizer source tree with dotnet csharpier 1.2.6 to match the Hellion Forge house style (matches what HellionChat enforces in its pre-push pipeline). Pure whitespace + using-block sorting; no semantic changes. This is a one-time noisy commit. Future code edits in this fork should land csharpier-clean because the pre-push hook (introduced in the next commit) runs `dotnet csharpier check Craftimizer/` as Block C of the preflight gate. Trade-off acknowledged: this widens the merge gap with upstream Craftimizer should Asriel ever resume maintenance. Given the upstream has been dormant since FFXIV 7.4 and the fork is light-rename only (internal namespaces unchanged), the marginal cost is acceptable.
181 lines
6.5 KiB
C#
181 lines
6.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Craftimizer.Plugin;
|
|
using Craftimizer.Simulator;
|
|
using Lumina.Excel.Sheets;
|
|
using ClassJob = Craftimizer.Simulator.ClassJob;
|
|
|
|
namespace Craftimizer.Utils;
|
|
|
|
public sealed record RecipeData
|
|
{
|
|
public ushort RecipeId { get; }
|
|
|
|
public Recipe Recipe { get; }
|
|
public RecipeLevelTable Table { get; }
|
|
|
|
public ClassJob ClassJob { get; }
|
|
public RecipeInfo RecipeInfo { get; }
|
|
public bool IsCollectable => Recipe.ItemResult.ValueNullable?.AlwaysCollectable ?? false;
|
|
public IReadOnlyList<int?>? CollectableThresholds { get; }
|
|
public IReadOnlyList<(Item Item, int Amount)> Ingredients { get; }
|
|
public int MaxStartingQuality { get; }
|
|
public ushort? AdjustedJobLevel { get; }
|
|
private int TotalHqILvls { get; }
|
|
|
|
public RecipeData(ushort recipeId, ushort? explicitlyAdjustedJobLevel = null)
|
|
{
|
|
RecipeId = recipeId;
|
|
|
|
Recipe =
|
|
LuminaSheets.RecipeSheet.GetRowOrDefault(recipeId)
|
|
?? throw new ArgumentException($"Invalid recipe id {recipeId}", nameof(recipeId));
|
|
|
|
ClassJob = (ClassJob)Recipe.CraftType.RowId;
|
|
|
|
var resolvedLevelTableRow = Recipe.RecipeLevelTable.RowId;
|
|
if (Recipe.MaxAdjustableJobLevel.RowId != 0)
|
|
{
|
|
AdjustedJobLevel = Math.Min(
|
|
explicitlyAdjustedJobLevel ?? ClassJob.GetWKSSyncedLevel(),
|
|
(ushort)Recipe.MaxAdjustableJobLevel.RowId
|
|
);
|
|
resolvedLevelTableRow = LuminaSheets
|
|
.GathererCrafterLvAdjustTableSheet.GetRow(AdjustedJobLevel.Value)
|
|
.RecipeLevel.RowId;
|
|
}
|
|
Table = LuminaSheets.RecipeLevelTableSheet.GetRow(resolvedLevelTableRow);
|
|
|
|
RecipeInfo = new()
|
|
{
|
|
IsExpert = Recipe.IsExpert,
|
|
ClassJobLevel = Table.ClassJobLevel,
|
|
ConditionsFlag = Table.ConditionsFlag,
|
|
MaxDurability =
|
|
(Recipe.MaxAdjustableJobLevel.RowId != 0 ? 80 : Table.Durability)
|
|
* Recipe.DurabilityFactor
|
|
/ 100,
|
|
MaxQuality =
|
|
(Recipe.CanHq || Recipe.RequiredQuality > 0)
|
|
? (int)Table.Quality * Recipe.QualityFactor / 100
|
|
: 0,
|
|
MaxProgress = Table.Difficulty * Recipe.DifficultyFactor / 100,
|
|
QualityModifier = Table.QualityModifier,
|
|
QualityDivider = Table.QualityDivider,
|
|
ProgressModifier = Table.ProgressModifier,
|
|
ProgressDivider = Table.ProgressDivider,
|
|
};
|
|
|
|
int[]? thresholds = null;
|
|
if (Recipe.CollectableMetadata.GetValueOrDefault<CollectablesShopRefine>() is { } row)
|
|
thresholds = [row.LowCollectability, row.MidCollectability, row.HighCollectability];
|
|
else if (Recipe.CollectableMetadata.GetValueOrDefault<HWDCrafterSupply>() is { } row2)
|
|
{
|
|
foreach (var entry in row2.HWDCrafterSupplyParams)
|
|
{
|
|
if (entry.ItemTradeIn.RowId == Recipe.ItemResult.RowId)
|
|
{
|
|
thresholds =
|
|
[
|
|
entry.BaseCollectableRating,
|
|
entry.MidCollectableRating,
|
|
entry.HighCollectableRating,
|
|
];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (
|
|
Recipe.CollectableMetadata.GetValueOrDefaultSubrow<SatisfactionSupply>() is { } row3
|
|
)
|
|
{
|
|
foreach (var subrow in row3)
|
|
{
|
|
if (subrow.Item.RowId == Recipe.ItemResult.RowId)
|
|
{
|
|
thresholds =
|
|
[
|
|
subrow.CollectabilityLow,
|
|
subrow.CollectabilityMid,
|
|
subrow.CollectabilityHigh,
|
|
];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (
|
|
Recipe.CollectableMetadata.GetValueOrDefault<SharlayanCraftWorksSupply>() is { } row5
|
|
)
|
|
{
|
|
foreach (var item in row5.Item)
|
|
{
|
|
if (item.ItemId.RowId == Recipe.ItemResult.RowId)
|
|
{
|
|
thresholds = [item.CollectabilityMid, item.CollectabilityHigh];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (Recipe.CollectableMetadata.GetValueOrDefault<CollectablesRefine>() is { } row6)
|
|
thresholds = [row6.CollectabilityLow, row6.CollectabilityMid, row6.CollectabilityHigh];
|
|
else if (
|
|
Recipe.CollectableMetadataKey == 7
|
|
&& LuminaSheets.WKSMissionToDoEvalutionRefinSheet.TryGetRow(
|
|
Recipe.CollectableMetadata.RowId,
|
|
out var row7
|
|
)
|
|
)
|
|
{
|
|
thresholds = [row7.Unknown0, row7.Unknown1, row7.Unknown2];
|
|
thresholds =
|
|
[
|
|
.. thresholds.Select(percentage => RecipeInfo.MaxQuality * percentage / 1000),
|
|
];
|
|
}
|
|
|
|
if (thresholds != null)
|
|
{
|
|
var t = thresholds.Where(t => t != 0).Cast<int?>();
|
|
t = Enumerable.Concat(Enumerable.Repeat((int?)null, 3 - t.Count()), t);
|
|
CollectableThresholds = t.ToArray();
|
|
}
|
|
|
|
Ingredients = Recipe
|
|
.Ingredient.Zip(Recipe.AmountIngredient)
|
|
.Take(6)
|
|
.Where(i => i.First.IsValid)
|
|
.Select(i => (i.First.Value, (int)i.Second))
|
|
.ToList();
|
|
MaxStartingQuality = (int)
|
|
Math.Floor(Recipe.MaterialQualityFactor * RecipeInfo.MaxQuality / 100f);
|
|
|
|
TotalHqILvls = (int)
|
|
Ingredients.Where(i => i.Item.CanBeHq).Sum(i => i.Item.LevelItem.RowId * i.Amount);
|
|
}
|
|
|
|
public int CalculateItemStartingQuality(int itemIdx, int amount)
|
|
{
|
|
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(itemIdx, Ingredients.Count);
|
|
|
|
var ingredient = Ingredients[itemIdx];
|
|
ArgumentOutOfRangeException.ThrowIfGreaterThan(amount, ingredient.Amount);
|
|
|
|
if (!ingredient.Item.CanBeHq)
|
|
return 0;
|
|
|
|
var iLvls = ingredient.Item.LevelItem.RowId * amount;
|
|
return (int)Math.Floor((float)iLvls / TotalHqILvls * MaxStartingQuality);
|
|
}
|
|
|
|
public int CalculateStartingQuality(IEnumerable<int> hqQuantities)
|
|
{
|
|
if (TotalHqILvls == 0)
|
|
return 0;
|
|
|
|
var iLvls = Ingredients.Zip(hqQuantities).Sum(i => i.First.Item.LevelItem.RowId * i.Second);
|
|
|
|
return (int)Math.Floor((float)iLvls / TotalHqILvls * MaxStartingQuality);
|
|
}
|
|
}
|