biiiig changes
This commit is contained in:
@@ -11,4 +11,5 @@ public static class LuminaSheets
|
|||||||
public static readonly ExcelSheet<Action> ActionSheet = Service.DataManager.GetExcelSheet<Action>()!;
|
public static readonly ExcelSheet<Action> ActionSheet = Service.DataManager.GetExcelSheet<Action>()!;
|
||||||
public static readonly ExcelSheet<CraftAction> CraftActionSheet = Service.DataManager.GetExcelSheet<CraftAction>()!;
|
public static readonly ExcelSheet<CraftAction> CraftActionSheet = Service.DataManager.GetExcelSheet<CraftAction>()!;
|
||||||
public static readonly ExcelSheet<Status> StatusSheet = Service.DataManager.GetExcelSheet<Status>()!;
|
public static readonly ExcelSheet<Status> StatusSheet = Service.DataManager.GetExcelSheet<Status>()!;
|
||||||
|
public static readonly ExcelSheet<Addon> AddonSheet = Service.DataManager.GetExcelSheet<Addon>()!;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ using Craftimizer.Simulator;
|
|||||||
using Craftimizer.Simulator.Actions;
|
using Craftimizer.Simulator.Actions;
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
using Dalamud.Interface.Windowing;
|
using Dalamud.Interface.Windowing;
|
||||||
|
using Dalamud.Logging;
|
||||||
|
using Dalamud.Utility;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -14,6 +16,8 @@ public class SimulatorWindow : Window
|
|||||||
public Simulation Simulation { get; }
|
public Simulation Simulation { get; }
|
||||||
public BaseAction[] AvailableActions { get; }
|
public BaseAction[] AvailableActions { get; }
|
||||||
|
|
||||||
|
private bool showOnlyGuaranteedActions = true;
|
||||||
|
|
||||||
public SimulatorWindow() : base("Craftimizer")
|
public SimulatorWindow() : base("Craftimizer")
|
||||||
{
|
{
|
||||||
SizeConstraints = new WindowSizeConstraints()
|
SizeConstraints = new WindowSizeConstraints()
|
||||||
@@ -22,7 +26,7 @@ public class SimulatorWindow : Window
|
|||||||
MaximumSize = new Vector2(float.MaxValue, float.MaxValue)
|
MaximumSize = new Vector2(float.MaxValue, float.MaxValue)
|
||||||
};
|
};
|
||||||
|
|
||||||
Simulation = new(new CharacterStats { Craftsmanship = 4041, Control = 3905, CP = 609, Level = 90 }, LuminaSheets.RecipeSheet.GetRow(35573)!);
|
Simulation = new(new CharacterStats { Craftsmanship = 4041, Control = 3905, CP = 609, Level = 90 }, LuminaSheets.RecipeSheet.GetRow(35499)!);
|
||||||
AvailableActions = BaseAction.Actions.Select(a => (Activator.CreateInstance(a, Simulation)! as BaseAction)!).ToArray();
|
AvailableActions = BaseAction.Actions.Select(a => (Activator.CreateInstance(a, Simulation)! as BaseAction)!).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,18 +36,22 @@ public class SimulatorWindow : Window
|
|||||||
ImGui.TableSetupColumn("CraftimizerActionsColumn", ImGuiTableColumnFlags.WidthFixed, 300);
|
ImGui.TableSetupColumn("CraftimizerActionsColumn", ImGuiTableColumnFlags.WidthFixed, 300);
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.BeginChild("CraftimizerActions", Vector2.Zero, true, ImGuiWindowFlags.NoDecoration);
|
ImGui.BeginChild("CraftimizerActions", Vector2.Zero, true, ImGuiWindowFlags.NoDecoration);
|
||||||
|
ImGui.Checkbox("Show only guaranteed actions", ref showOnlyGuaranteedActions);
|
||||||
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
ImGui.PushStyleVar(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
||||||
foreach(var category in AvailableActions.GroupBy(a=>a.Category))
|
foreach(var category in AvailableActions.GroupBy(a => a.Category))
|
||||||
{
|
{
|
||||||
var i = 0;
|
var i = 0;
|
||||||
ImGuiUtils.BeginGroupPanel(category.Key.ToString());
|
ImGuiUtils.BeginGroupPanel(category.Key.GetDisplayName());
|
||||||
foreach (var action in category)
|
foreach (var action in category.OrderBy(a => a.Level))
|
||||||
{
|
{
|
||||||
|
if (showOnlyGuaranteedActions && !action.IsGuaranteedAction)
|
||||||
|
continue;
|
||||||
|
|
||||||
ImGui.BeginDisabled(!action.CanUse);
|
ImGui.BeginDisabled(!action.CanUse);
|
||||||
if (ImGui.ImageButton(action.GetIcon(ClassJob.Carpenter).ImGuiHandle, new Vector2(ImGui.GetFontSize() * 2)))
|
if (ImGui.ImageButton(action.GetIcon(ClassJob.Carpenter).ImGuiHandle, new Vector2(ImGui.GetFontSize() * 2)))
|
||||||
Simulation.Execute(action);
|
Simulation.Execute(action);
|
||||||
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
|
||||||
ImGui.SetTooltip(action.Tooltip);
|
ImGui.SetTooltip(action.GetTooltip(true));
|
||||||
ImGui.EndDisabled();
|
ImGui.EndDisabled();
|
||||||
if (++i % 5 != 0)
|
if (++i % 5 != 0)
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
@@ -55,6 +63,9 @@ public class SimulatorWindow : Window
|
|||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
ImGui.BeginChild("CraftimizerSimulator", Vector2.Zero, true, ImGuiWindowFlags.NoDecoration);
|
ImGui.BeginChild("CraftimizerSimulator", Vector2.Zero, true, ImGuiWindowFlags.NoDecoration);
|
||||||
ImGui.Text($"Step {Simulation.StepCount + 1}");
|
ImGui.Text($"Step {Simulation.StepCount + 1}");
|
||||||
|
ImGui.Text(Simulation.Condition.Name());
|
||||||
|
if (ImGui.IsItemHovered())
|
||||||
|
ImGui.SetTooltip(Simulation.Condition.Description(Simulation.Stats.HasRelic));
|
||||||
ImGui.Text($"{Simulation.HQPercent}%% HQ");
|
ImGui.Text($"{Simulation.HQPercent}%% HQ");
|
||||||
ImGui.PushStyleColor(ImGuiCol.PlotHistogram, new Vector4(.2f, 1f, .2f, 1f));
|
ImGui.PushStyleColor(ImGuiCol.PlotHistogram, new Vector4(.2f, 1f, .2f, 1f));
|
||||||
ImGui.ProgressBar(Math.Min((float)Simulation.Progress / Simulation.MaxProgress, 1f), new Vector2(200, 20), $"{Simulation.Progress} / {Simulation.MaxProgress}");
|
ImGui.ProgressBar(Math.Min((float)Simulation.Progress / Simulation.MaxProgress, 1f), new Vector2(200, 20), $"{Simulation.Progress} / {Simulation.MaxProgress}");
|
||||||
@@ -70,20 +81,14 @@ public class SimulatorWindow : Window
|
|||||||
ImGui.PopStyleColor();
|
ImGui.PopStyleColor();
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
ImGui.Text($"Effects:");
|
ImGui.Text($"Effects:");
|
||||||
foreach (var (effect, strength, stepsLeft) in Simulation.ActiveEffects)
|
foreach (var effect in Simulation.ActiveEffects)
|
||||||
{
|
{
|
||||||
var status = effect.Status();
|
var icon = effect.Icon;
|
||||||
var icon = Icons.GetIconFromId((ushort)status.Icon);
|
|
||||||
var h = ImGui.GetFontSize() * 1.25f;
|
var h = ImGui.GetFontSize() * 1.25f;
|
||||||
var w = icon.Width * h / icon.Height;
|
var w = icon.Width * h / icon.Height;
|
||||||
ImGui.Image(icon.ImGuiHandle, new Vector2(w, h));
|
ImGui.Image(icon.ImGuiHandle, new Vector2(w, h));
|
||||||
if (ImGui.IsItemHovered())
|
|
||||||
ImGui.SetTooltip(status.Name.ToString());
|
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (stepsLeft < 0)
|
ImGui.Text(effect.Tooltip);
|
||||||
ImGui.Text($"{strength}");
|
|
||||||
else
|
|
||||||
ImGui.Text($"> {stepsLeft}");
|
|
||||||
}
|
}
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
{
|
{
|
||||||
@@ -92,7 +97,7 @@ public class SimulatorWindow : Window
|
|||||||
{
|
{
|
||||||
ImGui.Image(action.GetIcon(ClassJob.Carpenter).ImGuiHandle, new Vector2(ImGui.GetFontSize() * 2f));
|
ImGui.Image(action.GetIcon(ClassJob.Carpenter).ImGuiHandle, new Vector2(ImGui.GetFontSize() * 2f));
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetTooltip(action.GetName(ClassJob.Carpenter));
|
ImGui.SetTooltip(action.GetTooltip(false));
|
||||||
if (++i % 5 != 0)
|
if (++i % 5 != 0)
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,3 +9,18 @@ public enum ActionCategory
|
|||||||
Buffs,
|
Buffs,
|
||||||
Other
|
Other
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static class ActionCategoryUtils
|
||||||
|
{
|
||||||
|
public static string GetDisplayName(this ActionCategory category) =>
|
||||||
|
category switch
|
||||||
|
{
|
||||||
|
ActionCategory.FirstTurn => "First Turn",
|
||||||
|
ActionCategory.Synthesis => "Synthesis",
|
||||||
|
ActionCategory.Quality => "Quality",
|
||||||
|
ActionCategory.Durability => "Durability",
|
||||||
|
ActionCategory.Buffs => "Buffs",
|
||||||
|
ActionCategory.Other => "Other",
|
||||||
|
_ => category.ToString()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ internal class AdvancedTouch : BaseAction
|
|||||||
public override int Level => 84;
|
public override int Level => 84;
|
||||||
public override int ActionId => 100411;
|
public override int ActionId => 100411;
|
||||||
|
|
||||||
public override int CPCost => Simulation.GetPreviousAction() is StandardTouch && Simulation.GetPreviousAction(2) is BasicTouch ? 18 : 46;
|
public override int CPCost => Simulation.IsPreviousAction<StandardTouch>() && Simulation.IsPreviousAction<BasicTouch>(2) ? 18 : 46;
|
||||||
public override float Efficiency => 1.50f;
|
public override float Efficiency => 1.50f;
|
||||||
public override bool IncreasesQuality => true;
|
public override bool IncreasesQuality => true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Craftimizer.Plugin;
|
using Craftimizer.Plugin;
|
||||||
|
using Dalamud.Utility;
|
||||||
using ImGuiScene;
|
using ImGuiScene;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using System;
|
using System;
|
||||||
@@ -32,6 +33,7 @@ public abstract class BaseAction
|
|||||||
public virtual float SuccessRate => 1f;
|
public virtual float SuccessRate => 1f;
|
||||||
public virtual int DurabilityCost => 10;
|
public virtual int DurabilityCost => 10;
|
||||||
public virtual bool IncreasesStepCount => true;
|
public virtual bool IncreasesStepCount => true;
|
||||||
|
public virtual bool IsGuaranteedAction => SuccessRate == 1f;
|
||||||
|
|
||||||
private (CraftAction? CraftAction, Action? Action) GetActionRow(ClassJob classJob)
|
private (CraftAction? CraftAction, Action? Action) GetActionRow(ClassJob classJob)
|
||||||
{
|
{
|
||||||
@@ -67,9 +69,9 @@ public abstract class BaseAction
|
|||||||
{
|
{
|
||||||
var (craftAction, action) = GetActionRow(classJob);
|
var (craftAction, action) = GetActionRow(classJob);
|
||||||
if (craftAction != null)
|
if (craftAction != null)
|
||||||
return craftAction.Name;
|
return craftAction.Name.ToDalamudString().TextValue;
|
||||||
else if (action != null)
|
else if (action != null)
|
||||||
return action.Name;
|
return action.Name.ToDalamudString().TextValue;
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +94,7 @@ public abstract class BaseAction
|
|||||||
Simulation.ReduceCP(CPCost);
|
Simulation.ReduceCP(CPCost);
|
||||||
Simulation.ReduceDurability(DurabilityCost);
|
Simulation.ReduceDurability(DurabilityCost);
|
||||||
|
|
||||||
if (Simulation.HasEffect(Effect.Manipulation))
|
if (Simulation.HasEffect(EffectType.Manipulation))
|
||||||
Simulation.RestoreDurability(5);
|
Simulation.RestoreDurability(5);
|
||||||
|
|
||||||
if (Simulation.RollSuccess(SuccessRate))
|
if (Simulation.RollSuccess(SuccessRate))
|
||||||
@@ -113,30 +115,28 @@ public abstract class BaseAction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual string Tooltip
|
public virtual string GetTooltip(bool addUsability)
|
||||||
{
|
{
|
||||||
get
|
var builder = new StringBuilder();
|
||||||
|
builder.AppendLine(GetName(ClassJob.Carpenter));
|
||||||
|
if (addUsability && !CanUse)
|
||||||
|
builder.AppendLine($"Cannot Use");
|
||||||
|
builder.AppendLine($"Level {Level}");
|
||||||
|
if (CPCost != 0)
|
||||||
|
builder.AppendLine($"-{Simulation.CalculateCPCost(CPCost)} CP");
|
||||||
|
if (DurabilityCost != 0)
|
||||||
|
builder.AppendLine($"-{Simulation.CalculateDurabilityCost(DurabilityCost)} Durability");
|
||||||
|
if (Efficiency != 0)
|
||||||
{
|
{
|
||||||
var builder = new StringBuilder();
|
if (IncreasesProgress)
|
||||||
builder.AppendLine(GetName(ClassJob.Carpenter));
|
builder.AppendLine($"+{Simulation.CalculateProgressGain(Efficiency)} Progress");
|
||||||
if (!CanUse)
|
if (IncreasesQuality)
|
||||||
builder.AppendLine($"Cannot Use");
|
builder.AppendLine($"+{Simulation.CalculateQualityGain(Efficiency)} Quality");
|
||||||
builder.AppendLine($"Level {Level}");
|
|
||||||
builder.AppendLine($"CP Cost: {CPCost}");
|
|
||||||
if (DurabilityCost != 0)
|
|
||||||
builder.AppendLine($"Durability Cost: {DurabilityCost}");
|
|
||||||
if (Efficiency != 0)
|
|
||||||
{
|
|
||||||
if (IncreasesProgress)
|
|
||||||
builder.AppendLine($"+{Simulation.CalculateProgressGain(Efficiency)} Progress");
|
|
||||||
if (IncreasesQuality)
|
|
||||||
builder.AppendLine($"+{Simulation.CalculateQualityGain(Efficiency)} Quality");
|
|
||||||
}
|
|
||||||
if (!IncreasesStepCount)
|
|
||||||
builder.AppendLine($"Does Not Increase Step Count");
|
|
||||||
if (SuccessRate != 1f)
|
|
||||||
builder.AppendLine($"{SuccessRate * 100}%% Success Rate");
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
}
|
||||||
|
if (!IncreasesStepCount)
|
||||||
|
builder.AppendLine($"Does Not Increase Step Count");
|
||||||
|
if (SuccessRate != 1f)
|
||||||
|
builder.AppendLine($"{Simulation.CalculateSuccessRate(SuccessRate) * 100}%% Success Rate");
|
||||||
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ public abstract class BaseBuffAction : BaseAction
|
|||||||
public BaseBuffAction(Simulation simulation) : base(simulation) { }
|
public BaseBuffAction(Simulation simulation) : base(simulation) { }
|
||||||
|
|
||||||
public abstract Effect Effect { get; }
|
public abstract Effect Effect { get; }
|
||||||
public abstract int EffectDuration { get; }
|
public virtual EffectType[] ConflictingEffects => Array.Empty<EffectType>();
|
||||||
public virtual Effect[] ConflictingEffects => Array.Empty<Effect>();
|
|
||||||
|
|
||||||
public override int DurabilityCost => 0;
|
public override int DurabilityCost => 0;
|
||||||
|
|
||||||
@@ -18,18 +17,13 @@ public abstract class BaseBuffAction : BaseAction
|
|||||||
if (ConflictingEffects.Length != 0)
|
if (ConflictingEffects.Length != 0)
|
||||||
foreach(var effect in ConflictingEffects)
|
foreach(var effect in ConflictingEffects)
|
||||||
Simulation.RemoveEffect(effect);
|
Simulation.RemoveEffect(effect);
|
||||||
Simulation.AddEffect(Effect, EffectDuration);
|
Simulation.AddEffect(Effect.Type, Effect.Duration, Effect.Strength);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Tooltip {
|
public override string GetTooltip(bool addUsability)
|
||||||
get
|
{
|
||||||
{
|
var builder = new StringBuilder(base.GetTooltip(addUsability));
|
||||||
var builder = new StringBuilder(base.Tooltip);
|
builder.AppendLine($"Effect: {Effect.Tooltip}");
|
||||||
builder.AppendLine($"Effect: {Effect.Status().Name}");
|
return builder.ToString();
|
||||||
builder.AppendLine($"Duration: {EffectDuration} steps");
|
|
||||||
foreach(var effect in ConflictingEffects)
|
|
||||||
builder.AppendLine($"Conflicts with: {effect.Status().Name}");
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ internal class ByregotsBlessing : BaseAction
|
|||||||
public override int ActionId => 100339;
|
public override int ActionId => 100339;
|
||||||
|
|
||||||
public override int CPCost => 24;
|
public override int CPCost => 24;
|
||||||
public override float Efficiency => 1.00f + (0.20f * (Simulation.GetEffect(Effect.InnerQuiet)?.Strength ?? 0));
|
public override float Efficiency => 1.00f + (0.20f * (Simulation.GetEffect(EffectType.InnerQuiet)?.Strength ?? 0));
|
||||||
public override bool IncreasesQuality => true;
|
public override bool IncreasesQuality => true;
|
||||||
|
|
||||||
public override bool CanUse => Simulation.HasEffect(Effect.InnerQuiet) && base.CanUse;
|
public override bool CanUse => Simulation.HasEffect(EffectType.InnerQuiet) && base.CanUse;
|
||||||
|
|
||||||
public override void UseSuccess()
|
public override void UseSuccess()
|
||||||
{
|
{
|
||||||
base.UseSuccess();
|
base.UseSuccess();
|
||||||
Simulation.RemoveEffect(Effect.InnerQuiet);
|
Simulation.RemoveEffect(EffectType.InnerQuiet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
namespace Craftimizer.Simulator.Actions;
|
||||||
|
|
||||||
|
internal class CarefulObservation : BaseAction
|
||||||
|
{
|
||||||
|
public CarefulObservation(Simulation simulation) : base(simulation) { }
|
||||||
|
|
||||||
|
public override ActionCategory Category => ActionCategory.Other;
|
||||||
|
public override int Level => 55;
|
||||||
|
public override int ActionId => 100395;
|
||||||
|
|
||||||
|
public override int CPCost => 0;
|
||||||
|
public override int DurabilityCost => 0;
|
||||||
|
public override bool IncreasesStepCount => false;
|
||||||
|
|
||||||
|
public override bool CanUse => Simulation.Stats.IsSpecialist && Simulation.CountPreviousAction<CarefulObservation>() < 3;
|
||||||
|
|
||||||
|
public override void UseSuccess() =>
|
||||||
|
Simulation.StepCondition();
|
||||||
|
}
|
||||||
@@ -11,6 +11,5 @@ internal class FinalAppraisal : BaseBuffAction
|
|||||||
public override int CPCost => 1;
|
public override int CPCost => 1;
|
||||||
public override bool IncreasesStepCount => false;
|
public override bool IncreasesStepCount => false;
|
||||||
|
|
||||||
public override Effect Effect => Effect.FinalAppraisal;
|
public override Effect Effect => new() { Type = EffectType.FinalAppraisal, Duration = 5 };
|
||||||
public override int EffectDuration => 5;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ internal class FocusedSynthesis : BaseAction
|
|||||||
public override int CPCost => 5;
|
public override int CPCost => 5;
|
||||||
public override float Efficiency => 2.00f;
|
public override float Efficiency => 2.00f;
|
||||||
public override bool IncreasesProgress => true;
|
public override bool IncreasesProgress => true;
|
||||||
public override float SuccessRate => Simulation.GetPreviousAction() is Observe ? 1.00f : 0.50f;
|
public override float SuccessRate => Simulation.IsPreviousAction<Observe>() ? 1.00f : 0.50f;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,5 +11,5 @@ internal class FocusedTouch : BaseAction
|
|||||||
public override int CPCost => 18;
|
public override int CPCost => 18;
|
||||||
public override float Efficiency => 1.50f;
|
public override float Efficiency => 1.50f;
|
||||||
public override bool IncreasesQuality => true;
|
public override bool IncreasesQuality => true;
|
||||||
public override float SuccessRate => Simulation.GetPreviousAction() is Observe ? 1.00f : 0.50f;
|
public override float SuccessRate => Simulation.IsPreviousAction<Observe>() ? 1.00f : 0.50f;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,5 @@ internal class GreatStrides : BaseBuffAction
|
|||||||
|
|
||||||
public override int CPCost => 32;
|
public override int CPCost => 32;
|
||||||
|
|
||||||
public override Effect Effect => Effect.GreatStrides;
|
public override Effect Effect => new() { Type = EffectType.GreatStrides, Duration = 3 };
|
||||||
public override int EffectDuration => 3;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ internal class Groundwork : BaseAction
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
var ret = Simulation.Stats.Level >= 86 ? 3.60f : 3.00f;
|
var ret = Simulation.Stats.Level >= 86 ? 3.60f : 3.00f;
|
||||||
|
// TODO: does not account for waste not
|
||||||
return Simulation.Durability < DurabilityCost ? ret / 2 : ret;
|
return Simulation.Durability < DurabilityCost ? ret / 2 : ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
namespace Craftimizer.Simulator.Actions;
|
||||||
|
|
||||||
|
internal class HeartAndSoul : BaseBuffAction
|
||||||
|
{
|
||||||
|
public HeartAndSoul(Simulation simulation) : base(simulation) { }
|
||||||
|
|
||||||
|
public override ActionCategory Category => ActionCategory.Other;
|
||||||
|
public override int Level => 86;
|
||||||
|
public override int ActionId => 100419;
|
||||||
|
|
||||||
|
public override int CPCost => 0;
|
||||||
|
public override bool IncreasesStepCount => false;
|
||||||
|
|
||||||
|
public override Effect Effect => new() { Type = EffectType.HeartAndSoul };
|
||||||
|
|
||||||
|
public override bool CanUse => Simulation.Stats.IsSpecialist && Simulation.CountPreviousAction<HeartAndSoul>() == 0;
|
||||||
|
}
|
||||||
@@ -10,6 +10,5 @@ internal class Innovation : BaseBuffAction
|
|||||||
|
|
||||||
public override int CPCost => 18;
|
public override int CPCost => 18;
|
||||||
|
|
||||||
public override Effect Effect => Effect.Innovation;
|
public override Effect Effect => new() { Type = EffectType.Innovation, Duration = 4 };
|
||||||
public override int EffectDuration => 4;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,16 @@ internal class IntensiveSynthesis : BaseAction
|
|||||||
public override int CPCost => 6;
|
public override int CPCost => 6;
|
||||||
public override float Efficiency => 4.00f;
|
public override float Efficiency => 4.00f;
|
||||||
public override bool IncreasesProgress => true;
|
public override bool IncreasesProgress => true;
|
||||||
|
public override bool IsGuaranteedAction => false;
|
||||||
|
|
||||||
public override bool CanUse =>
|
public override bool CanUse =>
|
||||||
(Simulation.Condition == Condition.Good || Simulation.Condition == Condition.Excellent)
|
(Simulation.Condition == Condition.Good || Simulation.Condition == Condition.Excellent || Simulation.HasEffect(EffectType.HeartAndSoul))
|
||||||
&& base.CanUse;
|
&& base.CanUse;
|
||||||
|
|
||||||
|
public override void UseSuccess()
|
||||||
|
{
|
||||||
|
base.UseSuccess();
|
||||||
|
if (Simulation.Condition != Condition.Good && Simulation.Condition != Condition.Excellent)
|
||||||
|
Simulation.RemoveEffect(EffectType.HeartAndSoul);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,5 @@ internal class Manipulation : BaseBuffAction
|
|||||||
|
|
||||||
public override int CPCost => 96;
|
public override int CPCost => 96;
|
||||||
|
|
||||||
public override Effect Effect => Effect.Manipulation;
|
public override Effect Effect => new() { Type = EffectType.Manipulation, Duration = 8 };
|
||||||
public override int EffectDuration => 8;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ internal class MuscleMemory : BaseAction
|
|||||||
public override void UseSuccess()
|
public override void UseSuccess()
|
||||||
{
|
{
|
||||||
base.UseSuccess();
|
base.UseSuccess();
|
||||||
Simulation.AddEffect(Effect.MuscleMemory, 5);
|
Simulation.AddEffect(EffectType.MuscleMemory, 5);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,14 +11,17 @@ internal class PreciseTouch : BaseAction
|
|||||||
public override int CPCost => 18;
|
public override int CPCost => 18;
|
||||||
public override float Efficiency => 1.50f;
|
public override float Efficiency => 1.50f;
|
||||||
public override bool IncreasesQuality => true;
|
public override bool IncreasesQuality => true;
|
||||||
|
public override bool IsGuaranteedAction => false;
|
||||||
|
|
||||||
public override bool CanUse =>
|
public override bool CanUse =>
|
||||||
(Simulation.Condition == Condition.Good || Simulation.Condition == Condition.Excellent)
|
(Simulation.Condition == Condition.Good || Simulation.Condition == Condition.Excellent || Simulation.HasEffect(EffectType.HeartAndSoul))
|
||||||
&& base.CanUse;
|
&& base.CanUse;
|
||||||
|
|
||||||
public override void UseSuccess()
|
public override void UseSuccess()
|
||||||
{
|
{
|
||||||
base.UseSuccess();
|
base.UseSuccess();
|
||||||
Simulation.StrengthenEffect(Effect.InnerQuiet);
|
Simulation.StrengthenEffect(EffectType.InnerQuiet);
|
||||||
|
if (Simulation.Condition != Condition.Good && Simulation.Condition != Condition.Excellent)
|
||||||
|
Simulation.RemoveEffect(EffectType.HeartAndSoul);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,6 @@ internal class PreparatoryTouch : BaseAction
|
|||||||
public override void UseSuccess()
|
public override void UseSuccess()
|
||||||
{
|
{
|
||||||
base.UseSuccess();
|
base.UseSuccess();
|
||||||
Simulation.StrengthenEffect(Effect.InnerQuiet);
|
Simulation.StrengthenEffect(EffectType.InnerQuiet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,6 @@ internal class PrudentSynthesis : BaseAction
|
|||||||
public override int DurabilityCost => base.DurabilityCost / 2;
|
public override int DurabilityCost => base.DurabilityCost / 2;
|
||||||
|
|
||||||
public override bool CanUse =>
|
public override bool CanUse =>
|
||||||
!(Simulation.HasEffect(Effect.WasteNot) || Simulation.HasEffect(Effect.WasteNot2))
|
!(Simulation.HasEffect(EffectType.WasteNot) || Simulation.HasEffect(EffectType.WasteNot2))
|
||||||
&& base.CanUse;
|
&& base.CanUse;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,6 @@ internal class PrudentTouch : BaseAction
|
|||||||
public override int DurabilityCost => base.DurabilityCost / 2;
|
public override int DurabilityCost => base.DurabilityCost / 2;
|
||||||
|
|
||||||
public override bool CanUse =>
|
public override bool CanUse =>
|
||||||
!(Simulation.HasEffect(Effect.WasteNot) || Simulation.HasEffect(Effect.WasteNot2))
|
!(Simulation.HasEffect(EffectType.WasteNot) || Simulation.HasEffect(EffectType.WasteNot2))
|
||||||
&& base.CanUse;
|
&& base.CanUse;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,6 @@ internal class Reflect : BaseAction
|
|||||||
public override void UseSuccess()
|
public override void UseSuccess()
|
||||||
{
|
{
|
||||||
base.UseSuccess();
|
base.UseSuccess();
|
||||||
Simulation.StrengthenEffect(Effect.InnerQuiet);
|
Simulation.StrengthenEffect(EffectType.InnerQuiet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ internal class StandardTouch : BaseAction
|
|||||||
public override int Level => 18;
|
public override int Level => 18;
|
||||||
public override int ActionId => 100004;
|
public override int ActionId => 100004;
|
||||||
|
|
||||||
public override int CPCost => Simulation.GetPreviousAction() is BasicTouch ? 18 : 32;
|
public override int CPCost => Simulation.IsPreviousAction<BasicTouch>() ? 18 : 32;
|
||||||
public override float Efficiency => 1.25f;
|
public override float Efficiency => 1.25f;
|
||||||
public override bool IncreasesQuality => true;
|
public override bool IncreasesQuality => true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,6 @@ internal class TrainedFinesse : BaseAction
|
|||||||
public override int DurabilityCost => 0;
|
public override int DurabilityCost => 0;
|
||||||
|
|
||||||
public override bool CanUse =>
|
public override bool CanUse =>
|
||||||
(Simulation.GetEffect(Effect.InnerQuiet)?.Strength ?? 0) == 10
|
(Simulation.GetEffect(EffectType.InnerQuiet)?.Strength ?? 0) == 10
|
||||||
&& base.CanUse;
|
&& base.CanUse;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,16 @@ internal class TricksOfTheTrade : BaseAction
|
|||||||
|
|
||||||
public override int CPCost => 0;
|
public override int CPCost => 0;
|
||||||
public override int DurabilityCost => 0;
|
public override int DurabilityCost => 0;
|
||||||
|
public override bool IsGuaranteedAction => false;
|
||||||
|
|
||||||
public override bool CanUse =>
|
public override bool CanUse =>
|
||||||
(Simulation.Condition == Condition.Good || Simulation.Condition == Condition.Excellent)
|
(Simulation.Condition == Condition.Good || Simulation.Condition == Condition.Excellent || Simulation.HasEffect(EffectType.HeartAndSoul))
|
||||||
&& base.CanUse;
|
&& base.CanUse;
|
||||||
|
|
||||||
public override void UseSuccess() =>
|
public override void UseSuccess()
|
||||||
|
{
|
||||||
Simulation.RestoreCP(20);
|
Simulation.RestoreCP(20);
|
||||||
|
if (Simulation.Condition != Condition.Good && Simulation.Condition != Condition.Excellent)
|
||||||
|
Simulation.RemoveEffect(EffectType.HeartAndSoul);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,5 @@ internal class Veneration : BaseBuffAction
|
|||||||
public override int CPCost => 18;
|
public override int CPCost => 18;
|
||||||
public override int DurabilityCost => 0;
|
public override int DurabilityCost => 0;
|
||||||
|
|
||||||
public override Effect Effect => Effect.Veneration;
|
public override Effect Effect => new() { Type = EffectType.Veneration, Duration = 4 };
|
||||||
public override int EffectDuration => 4;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ internal class WasteNot : BaseBuffAction
|
|||||||
|
|
||||||
public override int CPCost => 56;
|
public override int CPCost => 56;
|
||||||
|
|
||||||
public override Effect Effect => Effect.WasteNot;
|
public override Effect Effect => new() { Type = EffectType.WasteNot, Duration = 4 };
|
||||||
public override int EffectDuration => 4;
|
public override EffectType[] ConflictingEffects => new[] { EffectType.WasteNot2 };
|
||||||
public override Effect[] ConflictingEffects => new[] { Effect.WasteNot2 };
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ internal class WasteNot2 : BaseBuffAction
|
|||||||
|
|
||||||
public override int CPCost => 98;
|
public override int CPCost => 98;
|
||||||
|
|
||||||
public override Effect Effect => Effect.WasteNot2;
|
public override Effect Effect => new() { Type = EffectType.WasteNot2, Duration = 8 };
|
||||||
public override int EffectDuration => 8;
|
public override EffectType[] ConflictingEffects => new[] { EffectType.WasteNot };
|
||||||
public override Effect[] ConflictingEffects => new[] { Effect.WasteNot };
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ public record CharacterStats
|
|||||||
public int Control { get; init; }
|
public int Control { get; init; }
|
||||||
public int CP { get; init; }
|
public int CP { get; init; }
|
||||||
public int Level { get; init; }
|
public int Level { get; init; }
|
||||||
|
public bool HasRelic { get; init; }
|
||||||
|
public bool IsSpecialist { get; init; }
|
||||||
|
|
||||||
public int CLvl => Level <= 80
|
public int CLvl => Level <= 80
|
||||||
? LuminaSheets.ParamGrowSheet.GetRow((uint)Level)!.CraftingLevel
|
? LuminaSheets.ParamGrowSheet.GetRow((uint)Level)!.CraftingLevel
|
||||||
|
|||||||
@@ -1,15 +1,60 @@
|
|||||||
|
using Craftimizer.Plugin;
|
||||||
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
|
using Dalamud.Game.Text.SeStringHandling.Payloads;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Craftimizer.Simulator;
|
namespace Craftimizer.Simulator;
|
||||||
|
|
||||||
public enum Condition
|
public enum Condition : ushort
|
||||||
{
|
{
|
||||||
Poor,
|
Poor = 0x0008,
|
||||||
Normal,
|
Normal = 0x0001,
|
||||||
Good,
|
Good = 0x0002,
|
||||||
Excellent,
|
Excellent = 0x0004,
|
||||||
|
|
||||||
//Centered,
|
Centered = 0x0010,
|
||||||
//Sturdy,
|
Sturdy = 0x0020,
|
||||||
//Pliant,
|
Pliant = 0x0040,
|
||||||
//Malleable,
|
Malleable = 0x0080,
|
||||||
//Primed
|
Primed = 0x0100,
|
||||||
|
GoodOmen = 0x0200,
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class ConditionUtils
|
||||||
|
{
|
||||||
|
public static Condition[] GetPossibleConditions(ushort conditionsFlag) =>
|
||||||
|
Enum.GetValues<Condition>().Where(c => ((Condition)conditionsFlag).HasFlag(c)).ToArray();
|
||||||
|
|
||||||
|
public static (uint Name, uint Description) AddonIds(this Condition me) =>
|
||||||
|
me switch
|
||||||
|
{
|
||||||
|
Condition.Poor => (229, 14203),
|
||||||
|
Condition.Normal => (226, 14200),
|
||||||
|
Condition.Good => (227, 14201),
|
||||||
|
Condition.Excellent => (228, 14202),
|
||||||
|
Condition.Centered => (239, 14204),
|
||||||
|
Condition.Sturdy => (240, 14205),
|
||||||
|
Condition.Pliant => (241, 14206),
|
||||||
|
Condition.Malleable => (13455, 14208),
|
||||||
|
Condition.Primed => (13454, 14207),
|
||||||
|
Condition.GoodOmen => (14214, 14215),
|
||||||
|
_ => (226, 14200) // Unknown
|
||||||
|
};
|
||||||
|
|
||||||
|
public static string Name(this Condition me) =>
|
||||||
|
LuminaSheets.AddonSheet.GetRow(me.AddonIds().Name)!.Text.ToDalamudString().TextValue;
|
||||||
|
|
||||||
|
public static string Description(this Condition me, bool isRelic)
|
||||||
|
{
|
||||||
|
var text = LuminaSheets.AddonSheet.GetRow(me.AddonIds().Description)!.Text.ToDalamudString();
|
||||||
|
for (var i = 0; i < text.Payloads.Count; ++i)
|
||||||
|
if (text.Payloads[i] is RawPayload)
|
||||||
|
text.Payloads[i] = new TextPayload(isRelic ? "1.75" : "1.5");
|
||||||
|
return text.TextValue;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,39 @@
|
|||||||
using Craftimizer.Plugin;
|
using Craftimizer.Plugin;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Dalamud.Utility;
|
||||||
|
using ImGuiScene;
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Craftimizer.Simulator;
|
namespace Craftimizer.Simulator;
|
||||||
|
|
||||||
public enum Effect
|
public record Effect
|
||||||
{
|
{
|
||||||
InnerQuiet,
|
public EffectType Type { get; init; }
|
||||||
WasteNot,
|
public int? Duration { get; set; }
|
||||||
Veneration,
|
public int? Strength { get; set; }
|
||||||
GreatStrides,
|
|
||||||
Innovation,
|
|
||||||
FinalAppraisal,
|
|
||||||
WasteNot2,
|
|
||||||
MuscleMemory,
|
|
||||||
Manipulation,
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static class EffectExtensions
|
public ushort IconId { get
|
||||||
{
|
|
||||||
public static uint StatusId(this Effect me) =>
|
|
||||||
me switch
|
|
||||||
{
|
{
|
||||||
Effect.InnerQuiet => 251,
|
var status = Type.Status();
|
||||||
Effect.WasteNot => 252,
|
var iconId = status.Icon;
|
||||||
Effect.Veneration => 2226,
|
if (status.MaxStacks != 0 && Strength != null)
|
||||||
Effect.GreatStrides => 254,
|
iconId += (uint)Math.Clamp(Strength.Value, 1, status.MaxStacks) - 1;
|
||||||
Effect.Innovation => 2189,
|
return (ushort)iconId;
|
||||||
Effect.FinalAppraisal => 2190,
|
}
|
||||||
Effect.WasteNot2 => 257,
|
}
|
||||||
Effect.MuscleMemory => 2191,
|
|
||||||
Effect.Manipulation => 258,
|
|
||||||
_ => 3412,
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Status Status(this Effect me) =>
|
public TextureWrap Icon => Icons.GetIconFromId(IconId);
|
||||||
LuminaSheets.StatusSheet.GetRow(me.StatusId())!;
|
|
||||||
|
public string Tooltip { get
|
||||||
|
{
|
||||||
|
var status = Type.Status();
|
||||||
|
var name = new StringBuilder();
|
||||||
|
name.Append(status.Name.ToDalamudString().TextValue);
|
||||||
|
if (status.MaxStacks != 0 && Strength != null)
|
||||||
|
name.Append($" {Strength}");
|
||||||
|
if (!status.IsPermanent && Duration != null)
|
||||||
|
name.Append($" > {Duration}");
|
||||||
|
return name.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using Craftimizer.Plugin;
|
||||||
|
using Lumina.Excel.GeneratedSheets;
|
||||||
|
|
||||||
|
namespace Craftimizer.Simulator;
|
||||||
|
|
||||||
|
public enum EffectType
|
||||||
|
{
|
||||||
|
InnerQuiet,
|
||||||
|
WasteNot,
|
||||||
|
Veneration,
|
||||||
|
GreatStrides,
|
||||||
|
Innovation,
|
||||||
|
FinalAppraisal,
|
||||||
|
WasteNot2,
|
||||||
|
MuscleMemory,
|
||||||
|
Manipulation,
|
||||||
|
HeartAndSoul,
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class EffectExtensions
|
||||||
|
{
|
||||||
|
public static uint StatusId(this EffectType me) =>
|
||||||
|
me switch
|
||||||
|
{
|
||||||
|
EffectType.InnerQuiet => 251,
|
||||||
|
EffectType.WasteNot => 252,
|
||||||
|
EffectType.Veneration => 2226,
|
||||||
|
EffectType.GreatStrides => 254,
|
||||||
|
EffectType.Innovation => 2189,
|
||||||
|
EffectType.FinalAppraisal => 2190,
|
||||||
|
EffectType.WasteNot2 => 257,
|
||||||
|
EffectType.MuscleMemory => 2191,
|
||||||
|
EffectType.Manipulation => 258,
|
||||||
|
EffectType.HeartAndSoul => 2665,
|
||||||
|
_ => 3412,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static Status Status(this EffectType me) =>
|
||||||
|
LuminaSheets.StatusSheet.GetRow(me.StatusId())!;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ using Dalamud.Logging;
|
|||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Craftimizer.Simulator;
|
namespace Craftimizer.Simulator;
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ public class Simulation
|
|||||||
public Recipe Recipe { get; }
|
public Recipe Recipe { get; }
|
||||||
public RecipeLevelTable RecipeTable => Recipe.RecipeLevelTable.Value!;
|
public RecipeLevelTable RecipeTable => Recipe.RecipeLevelTable.Value!;
|
||||||
public int RLvl => (int)RecipeTable.RowId;
|
public int RLvl => (int)RecipeTable.RowId;
|
||||||
|
public readonly Condition[] AvailableConditions;
|
||||||
|
|
||||||
public int MaxDurability => RecipeTable.Durability * Recipe.DurabilityFactor / 100;
|
public int MaxDurability => RecipeTable.Durability * Recipe.DurabilityFactor / 100;
|
||||||
public int MaxQuality => (int)RecipeTable.Quality * Recipe.QualityFactor / 100;
|
public int MaxQuality => (int)RecipeTable.Quality * Recipe.QualityFactor / 100;
|
||||||
@@ -24,7 +26,7 @@ public class Simulation
|
|||||||
public int Durability { get; private set; }
|
public int Durability { get; private set; }
|
||||||
public int CP { get; private set; }
|
public int CP { get; private set; }
|
||||||
public Condition Condition { get; private set; }
|
public Condition Condition { get; private set; }
|
||||||
public List<(Effect effect, int strength, int stepsLeft)> ActiveEffects { get; } = new();
|
public List<Effect> ActiveEffects { get; } = new();
|
||||||
public List<BaseAction> ActionHistory { get; } = new();
|
public List<BaseAction> ActionHistory { get; } = new();
|
||||||
|
|
||||||
// https://github.com/ffxiv-teamcraft/simulator/blob/0682dfa76043ff4ccb38832c184d046ceaff0733/src/model/tables.ts#L2
|
// https://github.com/ffxiv-teamcraft/simulator/blob/0682dfa76043ff4ccb38832c184d046ceaff0733/src/model/tables.ts#L2
|
||||||
@@ -51,6 +53,7 @@ public class Simulation
|
|||||||
Durability = MaxDurability;
|
Durability = MaxDurability;
|
||||||
CP = Stats.CP;
|
CP = Stats.CP;
|
||||||
Condition = Condition.Normal;
|
Condition = Condition.Normal;
|
||||||
|
AvailableConditions = ConditionUtils.GetPossibleConditions(RecipeTable.ConditionsFlag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActionResponse Execute(BaseAction action)
|
public ActionResponse Execute(BaseAction action)
|
||||||
@@ -72,14 +75,13 @@ public class Simulation
|
|||||||
|
|
||||||
for (var i = 0; i < ActiveEffects.Count; ++i)
|
for (var i = 0; i < ActiveEffects.Count; ++i)
|
||||||
{
|
{
|
||||||
var (effect, strength, stepsLeft) = ActiveEffects[i];
|
var effect = ActiveEffects[i];
|
||||||
if (stepsLeft == 1)
|
effect.Duration--;
|
||||||
|
if (effect.Duration == 0)
|
||||||
{
|
{
|
||||||
ActiveEffects.RemoveAt(i);
|
ActiveEffects.RemoveAt(i);
|
||||||
--i;
|
--i;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
ActiveEffects[i] = (effect, strength, stepsLeft - 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Progress >= MaxProgress)
|
if (Progress >= MaxProgress)
|
||||||
@@ -99,59 +101,91 @@ public class Simulation
|
|||||||
public ActionResponse Execute<T>() where T : BaseAction =>
|
public ActionResponse Execute<T>() where T : BaseAction =>
|
||||||
Execute((T)Activator.CreateInstance(typeof(T), this)!);
|
Execute((T)Activator.CreateInstance(typeof(T), this)!);
|
||||||
|
|
||||||
public (int Strength, int Duration)? GetEffect(Effect effect)
|
public Effect? GetEffect(EffectType effect) =>
|
||||||
{
|
ActiveEffects.FirstOrDefault(e => e.Type == effect);
|
||||||
var idx = ActiveEffects.FindIndex(x => x.effect == effect);
|
|
||||||
if (idx == -1)
|
|
||||||
return null;
|
|
||||||
var (_, strength, duration) = ActiveEffects[idx];
|
|
||||||
return (strength, duration);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddEffect(Effect effect, int duration, int strength = 1)
|
public void AddEffect(EffectType effect, int? duration = null, int? strength = null)
|
||||||
{
|
{
|
||||||
|
if (Condition == Condition.Primed && duration != null)
|
||||||
|
duration += 2;
|
||||||
|
|
||||||
// Duration will be decreased in the next step, so we need to add 1
|
// Duration will be decreased in the next step, so we need to add 1
|
||||||
duration += 1;
|
if (duration != null)
|
||||||
|
duration++;
|
||||||
|
|
||||||
var idx = ActiveEffects.FindIndex(x => x.effect == effect);
|
var currentEffect = GetEffect(effect);
|
||||||
if (idx == -1)
|
if (currentEffect != null) {
|
||||||
ActiveEffects.Add((effect, strength, duration));
|
currentEffect.Duration = duration;
|
||||||
|
currentEffect.Strength = strength;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ActiveEffects[idx] = (effect, strength, duration);
|
ActiveEffects.Add(new Effect { Type = effect, Duration = duration, Strength = strength });
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StrengthenEffect(Effect effect, int duration = -1)
|
public void StrengthenEffect(EffectType effect, int? duration = null)
|
||||||
{
|
{
|
||||||
var idx = ActiveEffects.FindIndex(x => x.effect == effect);
|
if (duration != null)
|
||||||
if (idx == -1)
|
duration += 1;
|
||||||
ActiveEffects.Add((effect, 1, duration));
|
|
||||||
|
var currentEffect = GetEffect(effect);
|
||||||
|
if (currentEffect != null)
|
||||||
|
{
|
||||||
|
if (effect.Status().MaxStacks > currentEffect.Strength)
|
||||||
|
currentEffect.Strength++;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ActiveEffects[idx] = (effect, ActiveEffects[idx].strength + 1, duration);
|
AddEffect(effect, duration, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveEffect(Effect effect)
|
public void RemoveEffect(EffectType effect) =>
|
||||||
{
|
ActiveEffects.RemoveAll(e => e.Type == effect);
|
||||||
var idx = ActiveEffects.FindIndex(x => x.effect == effect);
|
|
||||||
if (idx != -1)
|
|
||||||
ActiveEffects.RemoveAt(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasEffect(Effect effect) => GetEffect(effect) != null;
|
public bool HasEffect(EffectType effect) =>
|
||||||
|
ActiveEffects.Any(e => e.Type == effect);
|
||||||
|
|
||||||
public BaseAction? GetPreviousAction(int stepsBack = 1) =>
|
public bool IsPreviousAction<T>(int stepsBack = 1) where T : BaseAction =>
|
||||||
ActionHistory.Count < stepsBack ? null : ActionHistory[^stepsBack];
|
ActionHistory.Count >= stepsBack && ActionHistory[^stepsBack] is T;
|
||||||
|
|
||||||
public bool RollSuccess(float successRate) =>
|
public int CountPreviousAction<T>() where T : BaseAction =>
|
||||||
|
ActionHistory.Count(x => x is T);
|
||||||
|
|
||||||
|
public bool RollSuccessRaw(float successRate) =>
|
||||||
successRate >= Random.NextSingle();
|
successRate >= Random.NextSingle();
|
||||||
|
|
||||||
public void IncreaseStepCount() =>
|
public bool RollSuccess(float successRate) =>
|
||||||
StepCount++;
|
RollSuccessRaw(CalculateSuccessRate(successRate));
|
||||||
|
|
||||||
public void ReduceDurability(int amount)
|
public void IncreaseStepCount()
|
||||||
{
|
{
|
||||||
if (HasEffect(Effect.WasteNot) || HasEffect(Effect.WasteNot2))
|
StepCount++;
|
||||||
amount /= 2;
|
StepCondition();
|
||||||
Durability -= amount;
|
}
|
||||||
|
|
||||||
|
private float GetConditionChance(Condition condition) =>
|
||||||
|
condition switch
|
||||||
|
{
|
||||||
|
Condition.Good => Recipe.IsExpert ? 0.12f : (Stats.Level >= 63 ? 0.15f : 0.18f),
|
||||||
|
Condition.Excellent => 0.04f,
|
||||||
|
Condition.Centered => 0.15f,
|
||||||
|
Condition.Sturdy => 0.15f,
|
||||||
|
Condition.Pliant => 0.10f,
|
||||||
|
Condition.Malleable => 0.13f,
|
||||||
|
Condition.Primed => 0.15f,
|
||||||
|
Condition.GoodOmen => 0.12f, // https://github.com/ffxiv-teamcraft/simulator/issues/77
|
||||||
|
_ => 0.00f
|
||||||
|
};
|
||||||
|
|
||||||
|
public void StepCondition()
|
||||||
|
{
|
||||||
|
var conditionChance = Random.NextSingle();
|
||||||
|
|
||||||
|
Condition = Condition switch {
|
||||||
|
Condition.Poor => Condition.Normal,
|
||||||
|
Condition.Good => Condition.Normal,
|
||||||
|
Condition.Excellent => Condition.Poor,
|
||||||
|
Condition.GoodOmen => Condition.Good,
|
||||||
|
_ => AvailableConditions.FirstOrDefault(c => (conditionChance -= GetConditionChance(c)) < 0, Condition.Normal)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RestoreDurability(int amount)
|
public void RestoreDurability(int amount)
|
||||||
@@ -162,11 +196,6 @@ public class Simulation
|
|||||||
Durability = MaxDurability;
|
Durability = MaxDurability;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReduceCP(int amount)
|
|
||||||
{
|
|
||||||
CP -= amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RestoreCP(int amount)
|
public void RestoreCP(int amount)
|
||||||
{
|
{
|
||||||
CP += amount;
|
CP += amount;
|
||||||
@@ -175,82 +204,104 @@ public class Simulation
|
|||||||
CP = Stats.CP;
|
CP = Stats.CP;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CalculateProgressGain(float efficiency)
|
public float CalculateSuccessRate(float successRate)
|
||||||
|
{
|
||||||
|
if (Condition == Condition.Centered)
|
||||||
|
successRate += 0.25f;
|
||||||
|
return Math.Clamp(successRate, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CalculateDurabilityCost(int amount)
|
||||||
|
{
|
||||||
|
var amt = (double)amount;
|
||||||
|
if (HasEffect(EffectType.WasteNot) || HasEffect(EffectType.WasteNot2))
|
||||||
|
amt /= 2;
|
||||||
|
if (Condition == Condition.Sturdy)
|
||||||
|
amt /= 2;
|
||||||
|
return (int)Math.Ceiling(amt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CalculateCPCost(int amount)
|
||||||
|
{
|
||||||
|
var amt = (double)amount;
|
||||||
|
if (Condition == Condition.Pliant)
|
||||||
|
amt /= 2;
|
||||||
|
return (int)Math.Ceiling(amt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CalculateProgressGain(float efficiency, bool dryRun = true)
|
||||||
{
|
{
|
||||||
var buffModifier = 1.00f;
|
var buffModifier = 1.00f;
|
||||||
if (HasEffect(Effect.MuscleMemory))
|
if (HasEffect(EffectType.MuscleMemory))
|
||||||
{
|
{
|
||||||
buffModifier += 1.00f;
|
buffModifier += 1.00f;
|
||||||
RemoveEffect(Effect.MuscleMemory);
|
if (!dryRun)
|
||||||
|
RemoveEffect(EffectType.MuscleMemory);
|
||||||
}
|
}
|
||||||
if (HasEffect(Effect.Veneration))
|
if (HasEffect(EffectType.Veneration))
|
||||||
buffModifier += 0.50f;
|
buffModifier += 0.50f;
|
||||||
|
|
||||||
// https://github.com/NotRanged/NotRanged.github.io/blob/0f4aee074f969fb05aad34feaba605057c08ffd1/app/js/ffxivcraftmodel.js#L88
|
var conditionModifier = Condition switch
|
||||||
PluginLog.LogDebug($"Efficiency: {efficiency}");
|
|
||||||
PluginLog.LogDebug($"Buff Modifier: {buffModifier}");
|
|
||||||
var baseIncrease = (Stats.Craftsmanship * 10f / RecipeTable.ProgressDivider) + 2;
|
|
||||||
PluginLog.LogDebug($"Increase: {baseIncrease}");
|
|
||||||
if (Stats.CLvl <= RLvl)
|
|
||||||
{
|
{
|
||||||
baseIncrease *= RecipeTable.ProgressModifier / 100f;
|
Condition.Malleable => 1.50f,
|
||||||
PluginLog.LogDebug($"Boosted Increase: {baseIncrease}");
|
_ => 1.00f
|
||||||
}
|
};
|
||||||
baseIncrease = MathF.Floor(baseIncrease);
|
|
||||||
PluginLog.LogDebug($"Adj. Increase: {baseIncrease}");
|
|
||||||
|
|
||||||
var progressGain = (int)(baseIncrease * efficiency * buffModifier);
|
// https://github.com/NotRanged/NotRanged.github.io/blob/0f4aee074f969fb05aad34feaba605057c08ffd1/app/js/ffxivcraftmodel.js#L88
|
||||||
PluginLog.LogDebug($"Progress Gain: {progressGain}");
|
var baseIncrease = (Stats.Craftsmanship * 10f / RecipeTable.ProgressDivider) + 2;
|
||||||
|
if (Stats.CLvl <= RLvl)
|
||||||
|
baseIncrease *= RecipeTable.ProgressModifier / 100f;
|
||||||
|
baseIncrease = MathF.Floor(baseIncrease);
|
||||||
|
|
||||||
|
var progressGain = (int)(baseIncrease * efficiency * conditionModifier * buffModifier);
|
||||||
return progressGain;
|
return progressGain;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int CalculateQualityGain(float efficiency)
|
public int CalculateQualityGain(float efficiency, bool dryRun = true)
|
||||||
{
|
{
|
||||||
var buffModifier = 1.00f;
|
var buffModifier = 1.00f;
|
||||||
if (HasEffect(Effect.GreatStrides))
|
if (HasEffect(EffectType.GreatStrides))
|
||||||
{
|
{
|
||||||
buffModifier += 1.00f;
|
buffModifier += 1.00f;
|
||||||
RemoveEffect(Effect.GreatStrides);
|
if (!dryRun)
|
||||||
|
RemoveEffect(EffectType.GreatStrides);
|
||||||
}
|
}
|
||||||
if (HasEffect(Effect.Innovation))
|
if (HasEffect(EffectType.Innovation))
|
||||||
buffModifier += 0.50f;
|
buffModifier += 0.50f;
|
||||||
|
|
||||||
buffModifier *= 1 + ((GetEffect(Effect.InnerQuiet)?.Strength ?? 0) * 0.10f);
|
buffModifier *= 1 + ((GetEffect(EffectType.InnerQuiet)?.Strength ?? 0) * 0.10f);
|
||||||
|
|
||||||
var conditionModifier = Condition switch
|
var conditionModifier = Condition switch
|
||||||
{
|
{
|
||||||
Condition.Poor => 0.50f,
|
Condition.Poor => 0.50f,
|
||||||
Condition.Good => 1.50f, // 1.75f if relic tool
|
Condition.Good => Stats.HasRelic ? 1.75f : 1.50f,
|
||||||
Condition.Excellent => 4.00f,
|
Condition.Excellent => 4.00f,
|
||||||
_ => 1.00f,
|
_ => 1.00f,
|
||||||
};
|
};
|
||||||
|
|
||||||
PluginLog.LogDebug($"Efficiency: {efficiency}");
|
|
||||||
PluginLog.LogDebug($"Buff Modifier: {buffModifier}");
|
|
||||||
PluginLog.LogDebug($"Cond Modifier: {conditionModifier}");
|
|
||||||
var baseIncrease = (Stats.Control * 10f / RecipeTable.QualityDivider) + 35;
|
var baseIncrease = (Stats.Control * 10f / RecipeTable.QualityDivider) + 35;
|
||||||
PluginLog.LogDebug($"Increase: {baseIncrease}");
|
|
||||||
if (Stats.CLvl <= RLvl)
|
if (Stats.CLvl <= RLvl)
|
||||||
{
|
|
||||||
baseIncrease *= RecipeTable.QualityModifier / 100f;
|
baseIncrease *= RecipeTable.QualityModifier / 100f;
|
||||||
PluginLog.LogDebug($"Boosted Increase: {baseIncrease}");
|
|
||||||
}
|
|
||||||
baseIncrease = MathF.Floor(baseIncrease);
|
baseIncrease = MathF.Floor(baseIncrease);
|
||||||
PluginLog.LogDebug($"Adj. Increase: {baseIncrease}");
|
|
||||||
|
|
||||||
var qualityGain = (int)(baseIncrease * efficiency * conditionModifier * buffModifier);
|
var qualityGain = (int)(baseIncrease * efficiency * conditionModifier * buffModifier);
|
||||||
PluginLog.LogDebug($"Quality Gain: {qualityGain}");
|
|
||||||
return qualityGain;
|
return qualityGain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ReduceDurabilityRaw(int amount) =>
|
||||||
|
Durability -= amount;
|
||||||
|
|
||||||
|
public void ReduceCPRaw(int amount) =>
|
||||||
|
CP -= amount;
|
||||||
|
|
||||||
public void IncreaseProgressRaw(int progressGain)
|
public void IncreaseProgressRaw(int progressGain)
|
||||||
{
|
{
|
||||||
Progress += progressGain;
|
Progress += progressGain;
|
||||||
|
|
||||||
if (HasEffect(Effect.FinalAppraisal) && Progress >= MaxProgress)
|
if (HasEffect(EffectType.FinalAppraisal) && Progress >= MaxProgress)
|
||||||
{
|
{
|
||||||
Progress = MaxProgress - 1;
|
Progress = MaxProgress - 1;
|
||||||
RemoveEffect(Effect.FinalAppraisal);
|
RemoveEffect(EffectType.FinalAppraisal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -259,12 +310,18 @@ public class Simulation
|
|||||||
Quality += qualityGain;
|
Quality += qualityGain;
|
||||||
|
|
||||||
if (Stats.Level >= 11)
|
if (Stats.Level >= 11)
|
||||||
StrengthenEffect(Effect.InnerQuiet);
|
StrengthenEffect(EffectType.InnerQuiet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ReduceDurability(int amount) =>
|
||||||
|
ReduceDurabilityRaw(CalculateDurabilityCost(amount));
|
||||||
|
|
||||||
|
public void ReduceCP(int amount) =>
|
||||||
|
ReduceCPRaw(CalculateCPCost(amount));
|
||||||
|
|
||||||
public void IncreaseProgress(float efficiency) =>
|
public void IncreaseProgress(float efficiency) =>
|
||||||
IncreaseProgressRaw(CalculateProgressGain(efficiency));
|
IncreaseProgressRaw(CalculateProgressGain(efficiency, false));
|
||||||
|
|
||||||
public void IncreaseQuality(float efficiency) =>
|
public void IncreaseQuality(float efficiency) =>
|
||||||
IncreaseQualityRaw(CalculateQualityGain(efficiency));
|
IncreaseQualityRaw(CalculateQualityGain(efficiency, false));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user