From faa73d238cf4a04e1813af9fb06d46670c77201e Mon Sep 17 00:00:00 2001 From: Asriel Camora Date: Thu, 15 Jun 2023 16:19:43 -0700 Subject: [PATCH] biiiig changes --- Craftimizer/Plugin/LuminaSheets.cs | 1 + Craftimizer/Plugin/SimulatorWindow.cs | 35 +-- Craftimizer/Simulator/ActionCategory.cs | 15 ++ .../Simulator/Actions/AdvancedTouch.cs | 2 +- Craftimizer/Simulator/Actions/BaseAction.cs | 50 ++-- .../Simulator/Actions/BaseBuffAction.cs | 20 +- .../Simulator/Actions/ByregotsBlessing.cs | 6 +- .../Simulator/Actions/CarefulObservation.cs | 19 ++ .../Simulator/Actions/FinalAppraisal.cs | 3 +- .../Simulator/Actions/FocusedSynthesis.cs | 2 +- Craftimizer/Simulator/Actions/FocusedTouch.cs | 2 +- Craftimizer/Simulator/Actions/GreatStrides.cs | 3 +- Craftimizer/Simulator/Actions/Groundwork.cs | 1 + Craftimizer/Simulator/Actions/HeartAndSoul.cs | 17 ++ Craftimizer/Simulator/Actions/Innovation.cs | 3 +- .../Simulator/Actions/IntensiveSynthesis.cs | 10 +- Craftimizer/Simulator/Actions/Manipulation.cs | 3 +- Craftimizer/Simulator/Actions/MuscleMemory.cs | 2 +- Craftimizer/Simulator/Actions/PreciseTouch.cs | 7 +- .../Simulator/Actions/PreparatoryTouch.cs | 2 +- .../Simulator/Actions/PrudentSynthesis.cs | 2 +- Craftimizer/Simulator/Actions/PrudentTouch.cs | 2 +- Craftimizer/Simulator/Actions/Reflect.cs | 2 +- .../Simulator/Actions/StandardTouch.cs | 2 +- .../Simulator/Actions/TrainedFinesse.cs | 2 +- .../Simulator/Actions/TricksOfTheTrade.cs | 9 +- Craftimizer/Simulator/Actions/Veneration.cs | 3 +- Craftimizer/Simulator/Actions/WasteNot.cs | 5 +- Craftimizer/Simulator/Actions/WasteNot2.cs | 5 +- Craftimizer/Simulator/CharacterStats.cs | 2 + Craftimizer/Simulator/Condition.cs | 65 +++++- Craftimizer/Simulator/Effect.cs | 59 ++--- Craftimizer/Simulator/EffectType.cs | 40 ++++ Craftimizer/Simulator/Simulation.cs | 221 +++++++++++------- 34 files changed, 414 insertions(+), 208 deletions(-) create mode 100644 Craftimizer/Simulator/Actions/CarefulObservation.cs create mode 100644 Craftimizer/Simulator/Actions/HeartAndSoul.cs create mode 100644 Craftimizer/Simulator/EffectType.cs diff --git a/Craftimizer/Plugin/LuminaSheets.cs b/Craftimizer/Plugin/LuminaSheets.cs index 40c221c..06ab351 100644 --- a/Craftimizer/Plugin/LuminaSheets.cs +++ b/Craftimizer/Plugin/LuminaSheets.cs @@ -11,4 +11,5 @@ public static class LuminaSheets public static readonly ExcelSheet ActionSheet = Service.DataManager.GetExcelSheet()!; public static readonly ExcelSheet CraftActionSheet = Service.DataManager.GetExcelSheet()!; public static readonly ExcelSheet StatusSheet = Service.DataManager.GetExcelSheet()!; + public static readonly ExcelSheet AddonSheet = Service.DataManager.GetExcelSheet()!; } diff --git a/Craftimizer/Plugin/SimulatorWindow.cs b/Craftimizer/Plugin/SimulatorWindow.cs index 0c3a352..a506895 100644 --- a/Craftimizer/Plugin/SimulatorWindow.cs +++ b/Craftimizer/Plugin/SimulatorWindow.cs @@ -2,6 +2,8 @@ using Craftimizer.Simulator; using Craftimizer.Simulator.Actions; using Dalamud.Interface; using Dalamud.Interface.Windowing; +using Dalamud.Logging; +using Dalamud.Utility; using ImGuiNET; using System; using System.Linq; @@ -14,6 +16,8 @@ public class SimulatorWindow : Window public Simulation Simulation { get; } public BaseAction[] AvailableActions { get; } + private bool showOnlyGuaranteedActions = true; + public SimulatorWindow() : base("Craftimizer") { SizeConstraints = new WindowSizeConstraints() @@ -22,7 +26,7 @@ public class SimulatorWindow : Window 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(); } @@ -32,18 +36,22 @@ public class SimulatorWindow : Window ImGui.TableSetupColumn("CraftimizerActionsColumn", ImGuiTableColumnFlags.WidthFixed, 300); ImGui.TableNextColumn(); ImGui.BeginChild("CraftimizerActions", Vector2.Zero, true, ImGuiWindowFlags.NoDecoration); + ImGui.Checkbox("Show only guaranteed actions", ref showOnlyGuaranteedActions); 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; - ImGuiUtils.BeginGroupPanel(category.Key.ToString()); - foreach (var action in category) + ImGuiUtils.BeginGroupPanel(category.Key.GetDisplayName()); + foreach (var action in category.OrderBy(a => a.Level)) { + if (showOnlyGuaranteedActions && !action.IsGuaranteedAction) + continue; + ImGui.BeginDisabled(!action.CanUse); if (ImGui.ImageButton(action.GetIcon(ClassJob.Carpenter).ImGuiHandle, new Vector2(ImGui.GetFontSize() * 2))) Simulation.Execute(action); if (ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled)) - ImGui.SetTooltip(action.Tooltip); + ImGui.SetTooltip(action.GetTooltip(true)); ImGui.EndDisabled(); if (++i % 5 != 0) ImGui.SameLine(); @@ -55,6 +63,9 @@ public class SimulatorWindow : Window ImGui.TableNextColumn(); ImGui.BeginChild("CraftimizerSimulator", Vector2.Zero, true, ImGuiWindowFlags.NoDecoration); 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.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}"); @@ -70,20 +81,14 @@ public class SimulatorWindow : Window ImGui.PopStyleColor(); ImGuiHelpers.ScaledDummy(5); ImGui.Text($"Effects:"); - foreach (var (effect, strength, stepsLeft) in Simulation.ActiveEffects) + foreach (var effect in Simulation.ActiveEffects) { - var status = effect.Status(); - var icon = Icons.GetIconFromId((ushort)status.Icon); + var icon = effect.Icon; var h = ImGui.GetFontSize() * 1.25f; var w = icon.Width * h / icon.Height; ImGui.Image(icon.ImGuiHandle, new Vector2(w, h)); - if (ImGui.IsItemHovered()) - ImGui.SetTooltip(status.Name.ToString()); ImGui.SameLine(); - if (stepsLeft < 0) - ImGui.Text($"{strength}"); - else - ImGui.Text($"> {stepsLeft}"); + ImGui.Text(effect.Tooltip); } ImGuiHelpers.ScaledDummy(5); { @@ -92,7 +97,7 @@ public class SimulatorWindow : Window { ImGui.Image(action.GetIcon(ClassJob.Carpenter).ImGuiHandle, new Vector2(ImGui.GetFontSize() * 2f)); if (ImGui.IsItemHovered()) - ImGui.SetTooltip(action.GetName(ClassJob.Carpenter)); + ImGui.SetTooltip(action.GetTooltip(false)); if (++i % 5 != 0) ImGui.SameLine(); } diff --git a/Craftimizer/Simulator/ActionCategory.cs b/Craftimizer/Simulator/ActionCategory.cs index 97ce59e..5aeb8c6 100644 --- a/Craftimizer/Simulator/ActionCategory.cs +++ b/Craftimizer/Simulator/ActionCategory.cs @@ -9,3 +9,18 @@ public enum ActionCategory Buffs, 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() + }; +} diff --git a/Craftimizer/Simulator/Actions/AdvancedTouch.cs b/Craftimizer/Simulator/Actions/AdvancedTouch.cs index 305f5bd..e5dcf18 100644 --- a/Craftimizer/Simulator/Actions/AdvancedTouch.cs +++ b/Craftimizer/Simulator/Actions/AdvancedTouch.cs @@ -8,7 +8,7 @@ internal class AdvancedTouch : BaseAction public override int Level => 84; 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() && Simulation.IsPreviousAction(2) ? 18 : 46; public override float Efficiency => 1.50f; public override bool IncreasesQuality => true; } diff --git a/Craftimizer/Simulator/Actions/BaseAction.cs b/Craftimizer/Simulator/Actions/BaseAction.cs index 4979f79..b4086cf 100644 --- a/Craftimizer/Simulator/Actions/BaseAction.cs +++ b/Craftimizer/Simulator/Actions/BaseAction.cs @@ -1,4 +1,5 @@ using Craftimizer.Plugin; +using Dalamud.Utility; using ImGuiScene; using Lumina.Excel.GeneratedSheets; using System; @@ -32,6 +33,7 @@ public abstract class BaseAction public virtual float SuccessRate => 1f; public virtual int DurabilityCost => 10; public virtual bool IncreasesStepCount => true; + public virtual bool IsGuaranteedAction => SuccessRate == 1f; private (CraftAction? CraftAction, Action? Action) GetActionRow(ClassJob classJob) { @@ -67,9 +69,9 @@ public abstract class BaseAction { var (craftAction, action) = GetActionRow(classJob); if (craftAction != null) - return craftAction.Name; + return craftAction.Name.ToDalamudString().TextValue; else if (action != null) - return action.Name; + return action.Name.ToDalamudString().TextValue; return "Unknown"; } @@ -92,7 +94,7 @@ public abstract class BaseAction Simulation.ReduceCP(CPCost); Simulation.ReduceDurability(DurabilityCost); - if (Simulation.HasEffect(Effect.Manipulation)) + if (Simulation.HasEffect(EffectType.Manipulation)) Simulation.RestoreDurability(5); 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(); - builder.AppendLine(GetName(ClassJob.Carpenter)); - if (!CanUse) - builder.AppendLine($"Cannot Use"); - 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 (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($"{Simulation.CalculateSuccessRate(SuccessRate) * 100}%% Success Rate"); + return builder.ToString(); } } diff --git a/Craftimizer/Simulator/Actions/BaseBuffAction.cs b/Craftimizer/Simulator/Actions/BaseBuffAction.cs index 2d6fa2d..d295249 100644 --- a/Craftimizer/Simulator/Actions/BaseBuffAction.cs +++ b/Craftimizer/Simulator/Actions/BaseBuffAction.cs @@ -8,8 +8,7 @@ public abstract class BaseBuffAction : BaseAction public BaseBuffAction(Simulation simulation) : base(simulation) { } public abstract Effect Effect { get; } - public abstract int EffectDuration { get; } - public virtual Effect[] ConflictingEffects => Array.Empty(); + public virtual EffectType[] ConflictingEffects => Array.Empty(); public override int DurabilityCost => 0; @@ -18,18 +17,13 @@ public abstract class BaseBuffAction : BaseAction if (ConflictingEffects.Length != 0) foreach(var effect in ConflictingEffects) Simulation.RemoveEffect(effect); - Simulation.AddEffect(Effect, EffectDuration); + Simulation.AddEffect(Effect.Type, Effect.Duration, Effect.Strength); } - public override string Tooltip { - get - { - var builder = new StringBuilder(base.Tooltip); - builder.AppendLine($"Effect: {Effect.Status().Name}"); - builder.AppendLine($"Duration: {EffectDuration} steps"); - foreach(var effect in ConflictingEffects) - builder.AppendLine($"Conflicts with: {effect.Status().Name}"); - return builder.ToString(); - } + public override string GetTooltip(bool addUsability) + { + var builder = new StringBuilder(base.GetTooltip(addUsability)); + builder.AppendLine($"Effect: {Effect.Tooltip}"); + return builder.ToString(); } } diff --git a/Craftimizer/Simulator/Actions/ByregotsBlessing.cs b/Craftimizer/Simulator/Actions/ByregotsBlessing.cs index b0c3ff2..610e7d1 100644 --- a/Craftimizer/Simulator/Actions/ByregotsBlessing.cs +++ b/Craftimizer/Simulator/Actions/ByregotsBlessing.cs @@ -9,14 +9,14 @@ internal class ByregotsBlessing : BaseAction public override int ActionId => 100339; 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 CanUse => Simulation.HasEffect(Effect.InnerQuiet) && base.CanUse; + public override bool CanUse => Simulation.HasEffect(EffectType.InnerQuiet) && base.CanUse; public override void UseSuccess() { base.UseSuccess(); - Simulation.RemoveEffect(Effect.InnerQuiet); + Simulation.RemoveEffect(EffectType.InnerQuiet); } } diff --git a/Craftimizer/Simulator/Actions/CarefulObservation.cs b/Craftimizer/Simulator/Actions/CarefulObservation.cs new file mode 100644 index 0000000..fcd6528 --- /dev/null +++ b/Craftimizer/Simulator/Actions/CarefulObservation.cs @@ -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() < 3; + + public override void UseSuccess() => + Simulation.StepCondition(); +} diff --git a/Craftimizer/Simulator/Actions/FinalAppraisal.cs b/Craftimizer/Simulator/Actions/FinalAppraisal.cs index 1e367c6..4b87d9b 100644 --- a/Craftimizer/Simulator/Actions/FinalAppraisal.cs +++ b/Craftimizer/Simulator/Actions/FinalAppraisal.cs @@ -11,6 +11,5 @@ internal class FinalAppraisal : BaseBuffAction public override int CPCost => 1; public override bool IncreasesStepCount => false; - public override Effect Effect => Effect.FinalAppraisal; - public override int EffectDuration => 5; + public override Effect Effect => new() { Type = EffectType.FinalAppraisal, Duration = 5 }; } diff --git a/Craftimizer/Simulator/Actions/FocusedSynthesis.cs b/Craftimizer/Simulator/Actions/FocusedSynthesis.cs index 82753ce..fd98eed 100644 --- a/Craftimizer/Simulator/Actions/FocusedSynthesis.cs +++ b/Craftimizer/Simulator/Actions/FocusedSynthesis.cs @@ -11,5 +11,5 @@ internal class FocusedSynthesis : BaseAction public override int CPCost => 5; public override float Efficiency => 2.00f; public override bool IncreasesProgress => true; - public override float SuccessRate => Simulation.GetPreviousAction() is Observe ? 1.00f : 0.50f; + public override float SuccessRate => Simulation.IsPreviousAction() ? 1.00f : 0.50f; } diff --git a/Craftimizer/Simulator/Actions/FocusedTouch.cs b/Craftimizer/Simulator/Actions/FocusedTouch.cs index 5d13b6b..222855c 100644 --- a/Craftimizer/Simulator/Actions/FocusedTouch.cs +++ b/Craftimizer/Simulator/Actions/FocusedTouch.cs @@ -11,5 +11,5 @@ internal class FocusedTouch : BaseAction public override int CPCost => 18; public override float Efficiency => 1.50f; public override bool IncreasesQuality => true; - public override float SuccessRate => Simulation.GetPreviousAction() is Observe ? 1.00f : 0.50f; + public override float SuccessRate => Simulation.IsPreviousAction() ? 1.00f : 0.50f; } diff --git a/Craftimizer/Simulator/Actions/GreatStrides.cs b/Craftimizer/Simulator/Actions/GreatStrides.cs index dafcc7b..a9112b0 100644 --- a/Craftimizer/Simulator/Actions/GreatStrides.cs +++ b/Craftimizer/Simulator/Actions/GreatStrides.cs @@ -10,6 +10,5 @@ internal class GreatStrides : BaseBuffAction public override int CPCost => 32; - public override Effect Effect => Effect.GreatStrides; - public override int EffectDuration => 3; + public override Effect Effect => new() { Type = EffectType.GreatStrides, Duration = 3 }; } diff --git a/Craftimizer/Simulator/Actions/Groundwork.cs b/Craftimizer/Simulator/Actions/Groundwork.cs index 0a01727..b961605 100644 --- a/Craftimizer/Simulator/Actions/Groundwork.cs +++ b/Craftimizer/Simulator/Actions/Groundwork.cs @@ -15,6 +15,7 @@ internal class Groundwork : BaseAction get { var ret = Simulation.Stats.Level >= 86 ? 3.60f : 3.00f; + // TODO: does not account for waste not return Simulation.Durability < DurabilityCost ? ret / 2 : ret; } } diff --git a/Craftimizer/Simulator/Actions/HeartAndSoul.cs b/Craftimizer/Simulator/Actions/HeartAndSoul.cs new file mode 100644 index 0000000..e64fe65 --- /dev/null +++ b/Craftimizer/Simulator/Actions/HeartAndSoul.cs @@ -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() == 0; +} diff --git a/Craftimizer/Simulator/Actions/Innovation.cs b/Craftimizer/Simulator/Actions/Innovation.cs index 4764cf6..4dff593 100644 --- a/Craftimizer/Simulator/Actions/Innovation.cs +++ b/Craftimizer/Simulator/Actions/Innovation.cs @@ -10,6 +10,5 @@ internal class Innovation : BaseBuffAction public override int CPCost => 18; - public override Effect Effect => Effect.Innovation; - public override int EffectDuration => 4; + public override Effect Effect => new() { Type = EffectType.Innovation, Duration = 4 }; } diff --git a/Craftimizer/Simulator/Actions/IntensiveSynthesis.cs b/Craftimizer/Simulator/Actions/IntensiveSynthesis.cs index 4bef0cb..9ad6ec2 100644 --- a/Craftimizer/Simulator/Actions/IntensiveSynthesis.cs +++ b/Craftimizer/Simulator/Actions/IntensiveSynthesis.cs @@ -11,8 +11,16 @@ internal class IntensiveSynthesis : BaseAction public override int CPCost => 6; public override float Efficiency => 4.00f; public override bool IncreasesProgress => true; + public override bool IsGuaranteedAction => false; 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; + + public override void UseSuccess() + { + base.UseSuccess(); + if (Simulation.Condition != Condition.Good && Simulation.Condition != Condition.Excellent) + Simulation.RemoveEffect(EffectType.HeartAndSoul); + } } diff --git a/Craftimizer/Simulator/Actions/Manipulation.cs b/Craftimizer/Simulator/Actions/Manipulation.cs index ffd41c4..82b2cfb 100644 --- a/Craftimizer/Simulator/Actions/Manipulation.cs +++ b/Craftimizer/Simulator/Actions/Manipulation.cs @@ -10,6 +10,5 @@ internal class Manipulation : BaseBuffAction public override int CPCost => 96; - public override Effect Effect => Effect.Manipulation; - public override int EffectDuration => 8; + public override Effect Effect => new() { Type = EffectType.Manipulation, Duration = 8 }; } diff --git a/Craftimizer/Simulator/Actions/MuscleMemory.cs b/Craftimizer/Simulator/Actions/MuscleMemory.cs index f8f28d8..30dac5e 100644 --- a/Craftimizer/Simulator/Actions/MuscleMemory.cs +++ b/Craftimizer/Simulator/Actions/MuscleMemory.cs @@ -17,6 +17,6 @@ internal class MuscleMemory : BaseAction public override void UseSuccess() { base.UseSuccess(); - Simulation.AddEffect(Effect.MuscleMemory, 5); + Simulation.AddEffect(EffectType.MuscleMemory, 5); } } diff --git a/Craftimizer/Simulator/Actions/PreciseTouch.cs b/Craftimizer/Simulator/Actions/PreciseTouch.cs index 0e3d967..c1ea445 100644 --- a/Craftimizer/Simulator/Actions/PreciseTouch.cs +++ b/Craftimizer/Simulator/Actions/PreciseTouch.cs @@ -11,14 +11,17 @@ internal class PreciseTouch : BaseAction public override int CPCost => 18; public override float Efficiency => 1.50f; public override bool IncreasesQuality => true; + public override bool IsGuaranteedAction => false; 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; public override void UseSuccess() { base.UseSuccess(); - Simulation.StrengthenEffect(Effect.InnerQuiet); + Simulation.StrengthenEffect(EffectType.InnerQuiet); + if (Simulation.Condition != Condition.Good && Simulation.Condition != Condition.Excellent) + Simulation.RemoveEffect(EffectType.HeartAndSoul); } } diff --git a/Craftimizer/Simulator/Actions/PreparatoryTouch.cs b/Craftimizer/Simulator/Actions/PreparatoryTouch.cs index c513ec9..14e0701 100644 --- a/Craftimizer/Simulator/Actions/PreparatoryTouch.cs +++ b/Craftimizer/Simulator/Actions/PreparatoryTouch.cs @@ -16,6 +16,6 @@ internal class PreparatoryTouch : BaseAction public override void UseSuccess() { base.UseSuccess(); - Simulation.StrengthenEffect(Effect.InnerQuiet); + Simulation.StrengthenEffect(EffectType.InnerQuiet); } } diff --git a/Craftimizer/Simulator/Actions/PrudentSynthesis.cs b/Craftimizer/Simulator/Actions/PrudentSynthesis.cs index 5bbb1c5..d2564a7 100644 --- a/Craftimizer/Simulator/Actions/PrudentSynthesis.cs +++ b/Craftimizer/Simulator/Actions/PrudentSynthesis.cs @@ -14,6 +14,6 @@ internal class PrudentSynthesis : BaseAction public override int DurabilityCost => base.DurabilityCost / 2; public override bool CanUse => - !(Simulation.HasEffect(Effect.WasteNot) || Simulation.HasEffect(Effect.WasteNot2)) + !(Simulation.HasEffect(EffectType.WasteNot) || Simulation.HasEffect(EffectType.WasteNot2)) && base.CanUse; } diff --git a/Craftimizer/Simulator/Actions/PrudentTouch.cs b/Craftimizer/Simulator/Actions/PrudentTouch.cs index f601894..04f0e05 100644 --- a/Craftimizer/Simulator/Actions/PrudentTouch.cs +++ b/Craftimizer/Simulator/Actions/PrudentTouch.cs @@ -14,6 +14,6 @@ internal class PrudentTouch : BaseAction public override int DurabilityCost => base.DurabilityCost / 2; public override bool CanUse => - !(Simulation.HasEffect(Effect.WasteNot) || Simulation.HasEffect(Effect.WasteNot2)) + !(Simulation.HasEffect(EffectType.WasteNot) || Simulation.HasEffect(EffectType.WasteNot2)) && base.CanUse; } diff --git a/Craftimizer/Simulator/Actions/Reflect.cs b/Craftimizer/Simulator/Actions/Reflect.cs index 1228c37..13396d7 100644 --- a/Craftimizer/Simulator/Actions/Reflect.cs +++ b/Craftimizer/Simulator/Actions/Reflect.cs @@ -17,6 +17,6 @@ internal class Reflect : BaseAction public override void UseSuccess() { base.UseSuccess(); - Simulation.StrengthenEffect(Effect.InnerQuiet); + Simulation.StrengthenEffect(EffectType.InnerQuiet); } } diff --git a/Craftimizer/Simulator/Actions/StandardTouch.cs b/Craftimizer/Simulator/Actions/StandardTouch.cs index bcd887d..1dffa65 100644 --- a/Craftimizer/Simulator/Actions/StandardTouch.cs +++ b/Craftimizer/Simulator/Actions/StandardTouch.cs @@ -8,7 +8,7 @@ internal class StandardTouch : BaseAction public override int Level => 18; public override int ActionId => 100004; - public override int CPCost => Simulation.GetPreviousAction() is BasicTouch ? 18 : 32; + public override int CPCost => Simulation.IsPreviousAction() ? 18 : 32; public override float Efficiency => 1.25f; public override bool IncreasesQuality => true; } diff --git a/Craftimizer/Simulator/Actions/TrainedFinesse.cs b/Craftimizer/Simulator/Actions/TrainedFinesse.cs index 4640942..5553d88 100644 --- a/Craftimizer/Simulator/Actions/TrainedFinesse.cs +++ b/Craftimizer/Simulator/Actions/TrainedFinesse.cs @@ -14,6 +14,6 @@ internal class TrainedFinesse : BaseAction public override int DurabilityCost => 0; public override bool CanUse => - (Simulation.GetEffect(Effect.InnerQuiet)?.Strength ?? 0) == 10 + (Simulation.GetEffect(EffectType.InnerQuiet)?.Strength ?? 0) == 10 && base.CanUse; } diff --git a/Craftimizer/Simulator/Actions/TricksOfTheTrade.cs b/Craftimizer/Simulator/Actions/TricksOfTheTrade.cs index 5085a94..36e81de 100644 --- a/Craftimizer/Simulator/Actions/TricksOfTheTrade.cs +++ b/Craftimizer/Simulator/Actions/TricksOfTheTrade.cs @@ -10,11 +10,16 @@ internal class TricksOfTheTrade : BaseAction public override int CPCost => 0; public override int DurabilityCost => 0; + public override bool IsGuaranteedAction => false; 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; - public override void UseSuccess() => + public override void UseSuccess() + { Simulation.RestoreCP(20); + if (Simulation.Condition != Condition.Good && Simulation.Condition != Condition.Excellent) + Simulation.RemoveEffect(EffectType.HeartAndSoul); + } } diff --git a/Craftimizer/Simulator/Actions/Veneration.cs b/Craftimizer/Simulator/Actions/Veneration.cs index 2961a5a..83ca912 100644 --- a/Craftimizer/Simulator/Actions/Veneration.cs +++ b/Craftimizer/Simulator/Actions/Veneration.cs @@ -11,6 +11,5 @@ internal class Veneration : BaseBuffAction public override int CPCost => 18; public override int DurabilityCost => 0; - public override Effect Effect => Effect.Veneration; - public override int EffectDuration => 4; + public override Effect Effect => new() { Type = EffectType.Veneration, Duration = 4 }; } diff --git a/Craftimizer/Simulator/Actions/WasteNot.cs b/Craftimizer/Simulator/Actions/WasteNot.cs index 2870b7d..498c3a5 100644 --- a/Craftimizer/Simulator/Actions/WasteNot.cs +++ b/Craftimizer/Simulator/Actions/WasteNot.cs @@ -10,7 +10,6 @@ internal class WasteNot : BaseBuffAction public override int CPCost => 56; - public override Effect Effect => Effect.WasteNot; - public override int EffectDuration => 4; - public override Effect[] ConflictingEffects => new[] { Effect.WasteNot2 }; + public override Effect Effect => new() { Type = EffectType.WasteNot, Duration = 4 }; + public override EffectType[] ConflictingEffects => new[] { EffectType.WasteNot2 }; } diff --git a/Craftimizer/Simulator/Actions/WasteNot2.cs b/Craftimizer/Simulator/Actions/WasteNot2.cs index 127ac79..51f2a89 100644 --- a/Craftimizer/Simulator/Actions/WasteNot2.cs +++ b/Craftimizer/Simulator/Actions/WasteNot2.cs @@ -10,7 +10,6 @@ internal class WasteNot2 : BaseBuffAction public override int CPCost => 98; - public override Effect Effect => Effect.WasteNot2; - public override int EffectDuration => 8; - public override Effect[] ConflictingEffects => new[] { Effect.WasteNot }; + public override Effect Effect => new() { Type = EffectType.WasteNot2, Duration = 8 }; + public override EffectType[] ConflictingEffects => new[] { EffectType.WasteNot }; } diff --git a/Craftimizer/Simulator/CharacterStats.cs b/Craftimizer/Simulator/CharacterStats.cs index 7ed084c..afe5bb8 100644 --- a/Craftimizer/Simulator/CharacterStats.cs +++ b/Craftimizer/Simulator/CharacterStats.cs @@ -9,6 +9,8 @@ public record CharacterStats public int Control { get; init; } public int CP { get; init; } public int Level { get; init; } + public bool HasRelic { get; init; } + public bool IsSpecialist { get; init; } public int CLvl => Level <= 80 ? LuminaSheets.ParamGrowSheet.GetRow((uint)Level)!.CraftingLevel diff --git a/Craftimizer/Simulator/Condition.cs b/Craftimizer/Simulator/Condition.cs index 9ed2443..28d1159 100644 --- a/Craftimizer/Simulator/Condition.cs +++ b/Craftimizer/Simulator/Condition.cs @@ -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; -public enum Condition +public enum Condition : ushort { - Poor, - Normal, - Good, - Excellent, + Poor = 0x0008, + Normal = 0x0001, + Good = 0x0002, + Excellent = 0x0004, - //Centered, - //Sturdy, - //Pliant, - //Malleable, - //Primed + Centered = 0x0010, + Sturdy = 0x0020, + Pliant = 0x0040, + Malleable = 0x0080, + Primed = 0x0100, + GoodOmen = 0x0200, +} + +internal static class ConditionUtils +{ + public static Condition[] GetPossibleConditions(ushort conditionsFlag) => + Enum.GetValues().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; + } + } diff --git a/Craftimizer/Simulator/Effect.cs b/Craftimizer/Simulator/Effect.cs index d23eeb4..0915428 100644 --- a/Craftimizer/Simulator/Effect.cs +++ b/Craftimizer/Simulator/Effect.cs @@ -1,38 +1,39 @@ using Craftimizer.Plugin; -using Lumina.Excel.GeneratedSheets; +using Dalamud.Utility; +using ImGuiScene; +using System; +using System.Text; namespace Craftimizer.Simulator; -public enum Effect +public record Effect { - InnerQuiet, - WasteNot, - Veneration, - GreatStrides, - Innovation, - FinalAppraisal, - WasteNot2, - MuscleMemory, - Manipulation, -} + public EffectType Type { get; init; } + public int? Duration { get; set; } + public int? Strength { get; set; } -internal static class EffectExtensions -{ - public static uint StatusId(this Effect me) => - me switch + public ushort IconId { get { - Effect.InnerQuiet => 251, - Effect.WasteNot => 252, - Effect.Veneration => 2226, - Effect.GreatStrides => 254, - Effect.Innovation => 2189, - Effect.FinalAppraisal => 2190, - Effect.WasteNot2 => 257, - Effect.MuscleMemory => 2191, - Effect.Manipulation => 258, - _ => 3412, - }; + var status = Type.Status(); + var iconId = status.Icon; + if (status.MaxStacks != 0 && Strength != null) + iconId += (uint)Math.Clamp(Strength.Value, 1, status.MaxStacks) - 1; + return (ushort)iconId; + } + } - public static Status Status(this Effect me) => - LuminaSheets.StatusSheet.GetRow(me.StatusId())!; + public TextureWrap Icon => Icons.GetIconFromId(IconId); + + 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(); + } + } } diff --git a/Craftimizer/Simulator/EffectType.cs b/Craftimizer/Simulator/EffectType.cs new file mode 100644 index 0000000..674b8a8 --- /dev/null +++ b/Craftimizer/Simulator/EffectType.cs @@ -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())!; +} diff --git a/Craftimizer/Simulator/Simulation.cs b/Craftimizer/Simulator/Simulation.cs index c342a90..b0887c8 100644 --- a/Craftimizer/Simulator/Simulation.cs +++ b/Craftimizer/Simulator/Simulation.cs @@ -3,6 +3,7 @@ using Dalamud.Logging; using Lumina.Excel.GeneratedSheets; using System; using System.Collections.Generic; +using System.Linq; namespace Craftimizer.Simulator; @@ -12,6 +13,7 @@ public class Simulation public Recipe Recipe { get; } public RecipeLevelTable RecipeTable => Recipe.RecipeLevelTable.Value!; public int RLvl => (int)RecipeTable.RowId; + public readonly Condition[] AvailableConditions; public int MaxDurability => RecipeTable.Durability * Recipe.DurabilityFactor / 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 CP { get; private set; } public Condition Condition { get; private set; } - public List<(Effect effect, int strength, int stepsLeft)> ActiveEffects { get; } = new(); + public List ActiveEffects { get; } = new(); public List ActionHistory { get; } = new(); // https://github.com/ffxiv-teamcraft/simulator/blob/0682dfa76043ff4ccb38832c184d046ceaff0733/src/model/tables.ts#L2 @@ -51,6 +53,7 @@ public class Simulation Durability = MaxDurability; CP = Stats.CP; Condition = Condition.Normal; + AvailableConditions = ConditionUtils.GetPossibleConditions(RecipeTable.ConditionsFlag); } public ActionResponse Execute(BaseAction action) @@ -72,14 +75,13 @@ public class Simulation for (var i = 0; i < ActiveEffects.Count; ++i) { - var (effect, strength, stepsLeft) = ActiveEffects[i]; - if (stepsLeft == 1) + var effect = ActiveEffects[i]; + effect.Duration--; + if (effect.Duration == 0) { ActiveEffects.RemoveAt(i); --i; } - else - ActiveEffects[i] = (effect, strength, stepsLeft - 1); } if (Progress >= MaxProgress) @@ -99,59 +101,91 @@ public class Simulation public ActionResponse Execute() where T : BaseAction => Execute((T)Activator.CreateInstance(typeof(T), this)!); - public (int Strength, int Duration)? GetEffect(Effect effect) - { - var idx = ActiveEffects.FindIndex(x => x.effect == effect); - if (idx == -1) - return null; - var (_, strength, duration) = ActiveEffects[idx]; - return (strength, duration); - } + public Effect? GetEffect(EffectType effect) => + ActiveEffects.FirstOrDefault(e => e.Type == effect); - 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 += 1; + if (duration != null) + duration++; - var idx = ActiveEffects.FindIndex(x => x.effect == effect); - if (idx == -1) - ActiveEffects.Add((effect, strength, duration)); + var currentEffect = GetEffect(effect); + if (currentEffect != null) { + currentEffect.Duration = duration; + currentEffect.Strength = strength; + } 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 (idx == -1) - ActiveEffects.Add((effect, 1, duration)); + if (duration != null) + duration += 1; + + var currentEffect = GetEffect(effect); + if (currentEffect != null) + { + if (effect.Status().MaxStacks > currentEffect.Strength) + currentEffect.Strength++; + } else - ActiveEffects[idx] = (effect, ActiveEffects[idx].strength + 1, duration); + AddEffect(effect, duration, 1); } - public void RemoveEffect(Effect effect) - { - var idx = ActiveEffects.FindIndex(x => x.effect == effect); - if (idx != -1) - ActiveEffects.RemoveAt(idx); - } + public void RemoveEffect(EffectType effect) => + ActiveEffects.RemoveAll(e => e.Type == effect); - 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) => - ActionHistory.Count < stepsBack ? null : ActionHistory[^stepsBack]; + public bool IsPreviousAction(int stepsBack = 1) where T : BaseAction => + ActionHistory.Count >= stepsBack && ActionHistory[^stepsBack] is T; - public bool RollSuccess(float successRate) => + public int CountPreviousAction() where T : BaseAction => + ActionHistory.Count(x => x is T); + + public bool RollSuccessRaw(float successRate) => successRate >= Random.NextSingle(); - public void IncreaseStepCount() => - StepCount++; + public bool RollSuccess(float successRate) => + RollSuccessRaw(CalculateSuccessRate(successRate)); - public void ReduceDurability(int amount) + public void IncreaseStepCount() { - if (HasEffect(Effect.WasteNot) || HasEffect(Effect.WasteNot2)) - amount /= 2; - Durability -= amount; + StepCount++; + StepCondition(); + } + + 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) @@ -162,11 +196,6 @@ public class Simulation Durability = MaxDurability; } - public void ReduceCP(int amount) - { - CP -= amount; - } - public void RestoreCP(int amount) { CP += amount; @@ -175,82 +204,104 @@ public class Simulation 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; - if (HasEffect(Effect.MuscleMemory)) + if (HasEffect(EffectType.MuscleMemory)) { buffModifier += 1.00f; - RemoveEffect(Effect.MuscleMemory); + if (!dryRun) + RemoveEffect(EffectType.MuscleMemory); } - if (HasEffect(Effect.Veneration)) + if (HasEffect(EffectType.Veneration)) buffModifier += 0.50f; - // https://github.com/NotRanged/NotRanged.github.io/blob/0f4aee074f969fb05aad34feaba605057c08ffd1/app/js/ffxivcraftmodel.js#L88 - 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) + var conditionModifier = Condition switch { - baseIncrease *= RecipeTable.ProgressModifier / 100f; - PluginLog.LogDebug($"Boosted Increase: {baseIncrease}"); - } - baseIncrease = MathF.Floor(baseIncrease); - PluginLog.LogDebug($"Adj. Increase: {baseIncrease}"); + Condition.Malleable => 1.50f, + _ => 1.00f + }; - var progressGain = (int)(baseIncrease * efficiency * buffModifier); - PluginLog.LogDebug($"Progress Gain: {progressGain}"); + // https://github.com/NotRanged/NotRanged.github.io/blob/0f4aee074f969fb05aad34feaba605057c08ffd1/app/js/ffxivcraftmodel.js#L88 + 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; } - public int CalculateQualityGain(float efficiency) + public int CalculateQualityGain(float efficiency, bool dryRun = true) { var buffModifier = 1.00f; - if (HasEffect(Effect.GreatStrides)) + if (HasEffect(EffectType.GreatStrides)) { buffModifier += 1.00f; - RemoveEffect(Effect.GreatStrides); + if (!dryRun) + RemoveEffect(EffectType.GreatStrides); } - if (HasEffect(Effect.Innovation)) + if (HasEffect(EffectType.Innovation)) 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 { 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, _ => 1.00f, }; - PluginLog.LogDebug($"Efficiency: {efficiency}"); - PluginLog.LogDebug($"Buff Modifier: {buffModifier}"); - PluginLog.LogDebug($"Cond Modifier: {conditionModifier}"); var baseIncrease = (Stats.Control * 10f / RecipeTable.QualityDivider) + 35; - PluginLog.LogDebug($"Increase: {baseIncrease}"); if (Stats.CLvl <= RLvl) - { baseIncrease *= RecipeTable.QualityModifier / 100f; - PluginLog.LogDebug($"Boosted Increase: {baseIncrease}"); - } baseIncrease = MathF.Floor(baseIncrease); - PluginLog.LogDebug($"Adj. Increase: {baseIncrease}"); var qualityGain = (int)(baseIncrease * efficiency * conditionModifier * buffModifier); - PluginLog.LogDebug($"Quality Gain: {qualityGain}"); return qualityGain; } + public void ReduceDurabilityRaw(int amount) => + Durability -= amount; + + public void ReduceCPRaw(int amount) => + CP -= amount; + public void IncreaseProgressRaw(int progressGain) { Progress += progressGain; - if (HasEffect(Effect.FinalAppraisal) && Progress >= MaxProgress) + if (HasEffect(EffectType.FinalAppraisal) && Progress >= MaxProgress) { Progress = MaxProgress - 1; - RemoveEffect(Effect.FinalAppraisal); + RemoveEffect(EffectType.FinalAppraisal); } } @@ -259,12 +310,18 @@ public class Simulation Quality += qualityGain; 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) => - IncreaseProgressRaw(CalculateProgressGain(efficiency)); + IncreaseProgressRaw(CalculateProgressGain(efficiency, false)); public void IncreaseQuality(float efficiency) => - IncreaseQualityRaw(CalculateQualityGain(efficiency)); + IncreaseQualityRaw(CalculateQualityGain(efficiency, false)); }