Big changes 2
- Split into several projects - All dalamud/lumina deps are in the plugin - Crafty/craftingway sim implemented! - optimizations to follow
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
namespace Craftimizer.Simulator;
|
||||
|
||||
public enum ActionCategory
|
||||
{
|
||||
FirstTurn,
|
||||
Synthesis,
|
||||
Quality,
|
||||
Durability,
|
||||
Buffs,
|
||||
Other
|
||||
}
|
||||
|
||||
public 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()
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Craftimizer.Simulator;
|
||||
|
||||
public enum ActionResponse
|
||||
{
|
||||
SimulationComplete,
|
||||
ActionNotUnlocked,
|
||||
NotEnoughCP,
|
||||
NoDurability,
|
||||
CannotUseAction,
|
||||
UsedAction,
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
public enum ActionType
|
||||
{
|
||||
AdvancedTouch,
|
||||
BasicSynthesis,
|
||||
BasicTouch,
|
||||
ByregotsBlessing,
|
||||
CarefulObservation,
|
||||
CarefulSynthesis,
|
||||
DelicateSynthesis,
|
||||
FinalAppraisal,
|
||||
FocusedSynthesis,
|
||||
FocusedTouch,
|
||||
GreatStrides,
|
||||
Groundwork,
|
||||
HastyTouch,
|
||||
HeartAndSoul,
|
||||
Innovation,
|
||||
IntensiveSynthesis,
|
||||
Manipulation,
|
||||
MastersMend,
|
||||
MuscleMemory,
|
||||
Observe,
|
||||
PreciseTouch,
|
||||
PreparatoryTouch,
|
||||
PrudentSynthesis,
|
||||
PrudentTouch,
|
||||
RapidSynthesis,
|
||||
Reflect,
|
||||
StandardTouch,
|
||||
TrainedEye,
|
||||
TrainedFinesse,
|
||||
TricksOfTheTrade,
|
||||
Veneration,
|
||||
WasteNot,
|
||||
WasteNot2,
|
||||
}
|
||||
|
||||
public static class ActionUtils
|
||||
{
|
||||
private static readonly BaseAction[] Actions;
|
||||
|
||||
static ActionUtils()
|
||||
{
|
||||
var types = typeof(BaseAction).Assembly.GetTypes()
|
||||
.Where(t => t.IsAssignableTo(typeof(BaseAction)) && !t.IsAbstract);
|
||||
Actions = Enum.GetNames<ActionType>()
|
||||
.Select(a => types.First(t => t.Name == a))
|
||||
.Select(t => (Activator.CreateInstance(t) as BaseAction)!)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
public static void SetSimulation(Simulator simulation) =>
|
||||
BaseAction.TLSSimulation.Value = simulation;
|
||||
|
||||
public static BaseAction WithUnsafe(this ActionType me) => Actions[(int)me];
|
||||
|
||||
public static BaseAction With(this ActionType me, Simulator simulation)
|
||||
{
|
||||
SetSimulation(simulation);
|
||||
return WithUnsafe(me);
|
||||
}
|
||||
|
||||
public static IEnumerable<ActionType> AvailableActions(Simulator simulation)
|
||||
{
|
||||
if (simulation.IsComplete)
|
||||
return Enumerable.Empty<ActionType>();
|
||||
|
||||
SetSimulation(simulation);
|
||||
return Enum.GetValues<ActionType>()
|
||||
.Where(a => WithUnsafe(a).CanUse);
|
||||
}
|
||||
|
||||
public static int Level(this ActionType me) =>
|
||||
WithUnsafe(me).Level;
|
||||
|
||||
public static ActionCategory Category(this ActionType me) =>
|
||||
WithUnsafe(me).Category;
|
||||
|
||||
public static string IntName(this ActionType me) =>
|
||||
me switch
|
||||
{
|
||||
ActionType.AdvancedTouch => "Advanced Touch",
|
||||
ActionType.BasicSynthesis => "Basic Synthesis",
|
||||
ActionType.BasicTouch => "Basic Touch",
|
||||
ActionType.ByregotsBlessing => "Byregot's Blessing",
|
||||
ActionType.CarefulObservation => "Careful Observation",
|
||||
ActionType.CarefulSynthesis => "Careful Synthesis",
|
||||
ActionType.DelicateSynthesis => "Delicate Synthesis",
|
||||
ActionType.FinalAppraisal => "Final Appraisal",
|
||||
ActionType.FocusedSynthesis => "Focused Synthesis",
|
||||
ActionType.FocusedTouch => "Focused Touch",
|
||||
ActionType.GreatStrides => "Great Strides",
|
||||
ActionType.Groundwork => "Groundwork",
|
||||
ActionType.HastyTouch => "Hasty Touch",
|
||||
ActionType.HeartAndSoul => "Heart And Soul",
|
||||
ActionType.Innovation => "Innovation",
|
||||
ActionType.IntensiveSynthesis => "Intensive Synthesis",
|
||||
ActionType.Manipulation => "Manipulation",
|
||||
ActionType.MastersMend => "Master's Mend",
|
||||
ActionType.MuscleMemory => "Muscle Memory",
|
||||
ActionType.Observe => "Observe",
|
||||
ActionType.PreciseTouch => "Precise Touch",
|
||||
ActionType.PreparatoryTouch => "Preparatory Touch",
|
||||
ActionType.PrudentSynthesis => "Prudent Synthesis",
|
||||
ActionType.PrudentTouch => "Prudent Touch",
|
||||
ActionType.RapidSynthesis => "Rapid Synthesis",
|
||||
ActionType.Reflect => "Reflect",
|
||||
ActionType.StandardTouch => "Standard Touch",
|
||||
ActionType.TrainedEye => "Trained Eye",
|
||||
ActionType.TrainedFinesse => "Trained Finesse",
|
||||
ActionType.TricksOfTheTrade => "Tricks Of The Trade",
|
||||
ActionType.Veneration => "Veneration",
|
||||
ActionType.WasteNot => "Waste Not",
|
||||
ActionType.WasteNot2 => "Waste Not II",
|
||||
_ => me.ToString(),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class AdvancedTouch : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Quality;
|
||||
public override int Level => 84;
|
||||
public override uint ActionId => 100411;
|
||||
|
||||
public override int CPCost => Simulation.IsPreviousAction(ActionType.StandardTouch) && Simulation.IsPreviousAction(ActionType.BasicTouch, 2) ? 18 : 46;
|
||||
public override float Efficiency => 1.50f;
|
||||
public override bool IncreasesQuality => true;
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
public abstract class BaseAction
|
||||
{
|
||||
internal static readonly ThreadLocal<Simulator?> TLSSimulation = new(false);
|
||||
protected static Simulator Simulation => TLSSimulation.Value ?? throw new NullReferenceException();
|
||||
|
||||
public BaseAction() { }
|
||||
|
||||
// Non-instanced properties
|
||||
public abstract ActionCategory Category { get; }
|
||||
public abstract int Level { get; }
|
||||
// Doesn't matter from which class, we'll use the sheet to extrapolate the rest
|
||||
public abstract uint ActionId { get; }
|
||||
|
||||
// Instanced properties
|
||||
public abstract int CPCost { get; }
|
||||
public virtual float Efficiency => 0f;
|
||||
public virtual bool IncreasesProgress => false;
|
||||
public virtual bool IncreasesQuality => false;
|
||||
public virtual float SuccessRate => 1f;
|
||||
public virtual int DurabilityCost => 10;
|
||||
public virtual bool IncreasesStepCount => true;
|
||||
public virtual bool IsGuaranteedAction => SuccessRate == 1f;
|
||||
|
||||
public virtual bool CanUse =>
|
||||
Simulation.Input.Stats.Level >= Level && Simulation.CP >= CPCost;
|
||||
|
||||
public virtual void Use()
|
||||
{
|
||||
Simulation.ReduceCP(CPCost);
|
||||
Simulation.ReduceDurability(DurabilityCost);
|
||||
|
||||
if (Simulation.HasEffect(EffectType.Manipulation))
|
||||
Simulation.RestoreDurability(5);
|
||||
|
||||
if (Simulation.RollSuccess(SuccessRate))
|
||||
UseSuccess();
|
||||
|
||||
if (IncreasesStepCount)
|
||||
Simulation.IncreaseStepCount();
|
||||
}
|
||||
|
||||
public virtual void UseSuccess()
|
||||
{
|
||||
if (Efficiency != 0f)
|
||||
{
|
||||
if (IncreasesProgress)
|
||||
Simulation.IncreaseProgress(Efficiency);
|
||||
if (IncreasesQuality)
|
||||
Simulation.IncreaseQuality(Efficiency);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual string GetTooltip(bool addUsability)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
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)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal abstract class BaseBuffAction : BaseAction
|
||||
{
|
||||
public abstract Effect Effect { get; }
|
||||
public virtual EffectType[] ConflictingEffects => Array.Empty<EffectType>();
|
||||
|
||||
public override int DurabilityCost => 0;
|
||||
|
||||
public override void UseSuccess()
|
||||
{
|
||||
if (ConflictingEffects.Length != 0)
|
||||
foreach(var effect in ConflictingEffects)
|
||||
Simulation.RemoveEffect(effect);
|
||||
Simulation.AddEffect(Effect.Type, Effect.Duration, Effect.Strength);
|
||||
}
|
||||
|
||||
public override string GetTooltip(bool addUsability)
|
||||
{
|
||||
var builder = new StringBuilder(base.GetTooltip(addUsability));
|
||||
builder.AppendLine($"{Effect.Duration} Steps");
|
||||
return builder.ToString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class BasicSynthesis : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Synthesis;
|
||||
public override int Level => 1;
|
||||
public override uint ActionId => 100001;
|
||||
|
||||
public override int CPCost => 0;
|
||||
// Basic Synthesis Mastery Trait
|
||||
public override float Efficiency => Simulation.Input.Stats.Level >= 31 ? 1.20f : 1.00f;
|
||||
public override bool IncreasesProgress => true;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class BasicTouch : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Quality;
|
||||
public override int Level => 5;
|
||||
public override uint ActionId => 100002;
|
||||
|
||||
public override int CPCost => 18;
|
||||
public override float Efficiency => 1.00f;
|
||||
public override bool IncreasesQuality => true;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class ByregotsBlessing : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Quality;
|
||||
public override int Level => 50;
|
||||
public override uint ActionId => 100339;
|
||||
|
||||
public override int CPCost => 24;
|
||||
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(EffectType.InnerQuiet) && base.CanUse;
|
||||
|
||||
public override void UseSuccess()
|
||||
{
|
||||
base.UseSuccess();
|
||||
Simulation.RemoveEffect(EffectType.InnerQuiet);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class CarefulObservation : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Other;
|
||||
public override int Level => 55;
|
||||
public override uint ActionId => 100395;
|
||||
|
||||
public override int CPCost => 0;
|
||||
public override int DurabilityCost => 0;
|
||||
public override bool IncreasesStepCount => false;
|
||||
|
||||
public override bool CanUse => Simulation.Input.Stats.IsSpecialist && Simulation.CountPreviousAction(ActionType.CarefulObservation) < 3;
|
||||
|
||||
public override void UseSuccess() =>
|
||||
Simulation.StepCondition();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class CarefulSynthesis : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Synthesis;
|
||||
public override int Level => 62;
|
||||
public override uint ActionId => 100203;
|
||||
|
||||
public override int CPCost => 7;
|
||||
// Careful Synthesis Mastery Trait
|
||||
public override float Efficiency => Simulation.Input.Stats.Level >= 82 ? 1.80f : 1.50f;
|
||||
public override bool IncreasesProgress => true;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class DelicateSynthesis : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Synthesis;
|
||||
public override int Level => 76;
|
||||
public override uint ActionId => 100323;
|
||||
|
||||
public override int CPCost => 32;
|
||||
public override float Efficiency => 1.00f;
|
||||
public override bool IncreasesProgress => true;
|
||||
public override bool IncreasesQuality => true;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class FinalAppraisal : BaseBuffAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Synthesis;
|
||||
public override int Level => 42;
|
||||
public override uint ActionId => 19012;
|
||||
|
||||
public override int CPCost => 1;
|
||||
public override bool IncreasesStepCount => false;
|
||||
|
||||
public override Effect Effect => new() { Type = EffectType.FinalAppraisal, Duration = 5 };
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class FocusedSynthesis : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Synthesis;
|
||||
public override int Level => 67;
|
||||
public override uint ActionId => 100235;
|
||||
|
||||
public override int CPCost => 5;
|
||||
public override float Efficiency => 2.00f;
|
||||
public override bool IncreasesProgress => true;
|
||||
public override float SuccessRate => Simulation.IsPreviousAction(ActionType.Observe) ? 1.00f : 0.50f;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class FocusedTouch : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Quality;
|
||||
public override int Level => 68;
|
||||
public override uint ActionId => 100243;
|
||||
|
||||
public override int CPCost => 18;
|
||||
public override float Efficiency => 1.50f;
|
||||
public override bool IncreasesQuality => true;
|
||||
public override float SuccessRate => Simulation.IsPreviousAction(ActionType.Observe) ? 1.00f : 0.50f;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class GreatStrides : BaseBuffAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Buffs;
|
||||
public override int Level => 21;
|
||||
public override uint ActionId => 260;
|
||||
|
||||
public override int CPCost => 32;
|
||||
|
||||
public override Effect Effect => new() { Type = EffectType.GreatStrides, Duration = 3 };
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class Groundwork : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Synthesis;
|
||||
public override int Level => 72;
|
||||
public override uint ActionId => 100403;
|
||||
|
||||
public override int CPCost => 18;
|
||||
// Groundwork Mastery Trait
|
||||
public override float Efficiency
|
||||
{
|
||||
get
|
||||
{
|
||||
var ret = Simulation.Input.Stats.Level >= 86 ? 3.60f : 3.00f;
|
||||
// TODO: does not account for waste not
|
||||
return Simulation.Durability < DurabilityCost ? ret / 2 : ret;
|
||||
}
|
||||
}
|
||||
public override bool IncreasesProgress => true;
|
||||
public override int DurabilityCost => 20;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class HastyTouch : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Quality;
|
||||
public override int Level => 9;
|
||||
public override uint ActionId => 100355;
|
||||
|
||||
public override int CPCost => 0;
|
||||
public override float Efficiency => 1.00f;
|
||||
public override bool IncreasesQuality => true;
|
||||
public override float SuccessRate => 0.60f;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class HeartAndSoul : BaseBuffAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Other;
|
||||
public override int Level => 86;
|
||||
public override uint 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.Input.Stats.IsSpecialist && Simulation.CountPreviousAction(ActionType.HeartAndSoul) == 0;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class Innovation : BaseBuffAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Buffs;
|
||||
public override int Level => 26;
|
||||
public override uint ActionId => 19004;
|
||||
|
||||
public override int CPCost => 18;
|
||||
|
||||
public override Effect Effect => new() { Type = EffectType.Innovation, Duration = 4 };
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class IntensiveSynthesis : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Synthesis;
|
||||
public override int Level => 78;
|
||||
public override uint ActionId => 100315;
|
||||
|
||||
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.HasEffect(EffectType.HeartAndSoul))
|
||||
&& base.CanUse;
|
||||
|
||||
public override void UseSuccess()
|
||||
{
|
||||
base.UseSuccess();
|
||||
if (Simulation.Condition != Condition.Good && Simulation.Condition != Condition.Excellent)
|
||||
Simulation.RemoveEffect(EffectType.HeartAndSoul);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class Manipulation : BaseBuffAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Durability;
|
||||
public override int Level => 65;
|
||||
public override uint ActionId => 4574;
|
||||
|
||||
public override int CPCost => 96;
|
||||
|
||||
public override Effect Effect => new() { Type = EffectType.Manipulation, Duration = 8 };
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class MastersMend : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Durability;
|
||||
public override int Level => 7;
|
||||
public override uint ActionId => 100003;
|
||||
|
||||
public override int CPCost => 88;
|
||||
public override int DurabilityCost => 0;
|
||||
|
||||
public override void UseSuccess() =>
|
||||
Simulation.RestoreDurability(30);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class MuscleMemory : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.FirstTurn;
|
||||
public override int Level => 54;
|
||||
public override uint ActionId => 100379;
|
||||
|
||||
public override int CPCost => 6;
|
||||
public override float Efficiency => 3.00f;
|
||||
public override bool IncreasesProgress => true;
|
||||
|
||||
public override bool CanUse => Simulation.IsFirstStep && base.CanUse;
|
||||
|
||||
public override void UseSuccess()
|
||||
{
|
||||
base.UseSuccess();
|
||||
Simulation.AddEffect(EffectType.MuscleMemory, 5);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class Observe : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Other;
|
||||
public override int Level => 13;
|
||||
public override uint ActionId => 100010;
|
||||
|
||||
public override int CPCost => 7;
|
||||
public override int DurabilityCost => 0;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class PreciseTouch : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Quality;
|
||||
public override int Level => 53;
|
||||
public override uint ActionId => 100128;
|
||||
|
||||
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.HasEffect(EffectType.HeartAndSoul))
|
||||
&& base.CanUse;
|
||||
|
||||
public override void UseSuccess()
|
||||
{
|
||||
base.UseSuccess();
|
||||
Simulation.StrengthenEffect(EffectType.InnerQuiet);
|
||||
if (Simulation.Condition != Condition.Good && Simulation.Condition != Condition.Excellent)
|
||||
Simulation.RemoveEffect(EffectType.HeartAndSoul);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class PreparatoryTouch : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Quality;
|
||||
public override int Level => 71;
|
||||
public override uint ActionId => 100299;
|
||||
|
||||
public override int CPCost => 40;
|
||||
public override float Efficiency => 2.00f;
|
||||
public override bool IncreasesQuality => true;
|
||||
public override int DurabilityCost => 20;
|
||||
|
||||
public override void UseSuccess()
|
||||
{
|
||||
base.UseSuccess();
|
||||
Simulation.StrengthenEffect(EffectType.InnerQuiet);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class PrudentSynthesis : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Synthesis;
|
||||
public override int Level => 88;
|
||||
public override uint ActionId => 100427;
|
||||
|
||||
public override int CPCost => 18;
|
||||
public override float Efficiency => 1.80f;
|
||||
public override bool IncreasesProgress => true;
|
||||
public override int DurabilityCost => base.DurabilityCost / 2;
|
||||
|
||||
public override bool CanUse =>
|
||||
!(Simulation.HasEffect(EffectType.WasteNot) || Simulation.HasEffect(EffectType.WasteNot2))
|
||||
&& base.CanUse;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class PrudentTouch : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Quality;
|
||||
public override int Level => 66;
|
||||
public override uint ActionId => 100227;
|
||||
|
||||
public override int CPCost => 25;
|
||||
public override float Efficiency => 1.00f;
|
||||
public override bool IncreasesQuality => true;
|
||||
public override int DurabilityCost => base.DurabilityCost / 2;
|
||||
|
||||
public override bool CanUse =>
|
||||
!(Simulation.HasEffect(EffectType.WasteNot) || Simulation.HasEffect(EffectType.WasteNot2))
|
||||
&& base.CanUse;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class RapidSynthesis : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Synthesis;
|
||||
public override int Level => 9;
|
||||
public override uint ActionId => 100363;
|
||||
|
||||
public override int CPCost => 0;
|
||||
// Rapid Synthesis Mastery Trait
|
||||
public override float Efficiency => Simulation.Input.Stats.Level >= 63 ? 5.00f : 2.50f;
|
||||
public override bool IncreasesProgress => true;
|
||||
public override float SuccessRate => 0.50f;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class Reflect : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.FirstTurn;
|
||||
public override int Level => 69;
|
||||
public override uint ActionId => 100387;
|
||||
|
||||
public override int CPCost => 6;
|
||||
public override float Efficiency => 1.00f;
|
||||
public override bool IncreasesQuality => true;
|
||||
|
||||
public override bool CanUse => Simulation.IsFirstStep && base.CanUse;
|
||||
|
||||
public override void UseSuccess()
|
||||
{
|
||||
base.UseSuccess();
|
||||
Simulation.StrengthenEffect(EffectType.InnerQuiet);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class StandardTouch : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Quality;
|
||||
public override int Level => 18;
|
||||
public override uint ActionId => 100004;
|
||||
|
||||
public override int CPCost => Simulation.IsPreviousAction(ActionType.BasicTouch) ? 18 : 32;
|
||||
public override float Efficiency => 1.25f;
|
||||
public override bool IncreasesQuality => true;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class TrainedEye : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.FirstTurn;
|
||||
public override int Level => 80;
|
||||
public override uint ActionId => 100283;
|
||||
|
||||
public override int CPCost => 250;
|
||||
public override bool IncreasesQuality => true;
|
||||
|
||||
public override bool CanUse =>
|
||||
Simulation.IsFirstStep &&
|
||||
!Simulation.Input.Recipe.IsExpert &&
|
||||
Simulation.Input.Stats.Level >= (Simulation.Input.Recipe.ClassJobLevel + 10) &&
|
||||
base.CanUse;
|
||||
|
||||
public override void UseSuccess() =>
|
||||
Simulation.IncreaseQualityRaw(Simulation.Input.Recipe.MaxQuality - Simulation.Quality);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class TrainedFinesse : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Quality;
|
||||
public override int Level => 90;
|
||||
public override uint ActionId => 100435;
|
||||
|
||||
public override int CPCost => 32;
|
||||
public override float Efficiency => 1.00f;
|
||||
public override bool IncreasesQuality => true;
|
||||
public override int DurabilityCost => 0;
|
||||
|
||||
public override bool CanUse =>
|
||||
(Simulation.GetEffect(EffectType.InnerQuiet)?.Strength ?? 0) == 10
|
||||
&& base.CanUse;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class TricksOfTheTrade : BaseAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Other;
|
||||
public override int Level => 13;
|
||||
public override uint ActionId => 100371;
|
||||
|
||||
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.HasEffect(EffectType.HeartAndSoul))
|
||||
&& base.CanUse;
|
||||
|
||||
public override void UseSuccess()
|
||||
{
|
||||
Simulation.RestoreCP(20);
|
||||
if (Simulation.Condition != Condition.Good && Simulation.Condition != Condition.Excellent)
|
||||
Simulation.RemoveEffect(EffectType.HeartAndSoul);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class Veneration : BaseBuffAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Buffs;
|
||||
public override int Level => 15;
|
||||
public override uint ActionId => 19297;
|
||||
|
||||
public override int CPCost => 18;
|
||||
public override int DurabilityCost => 0;
|
||||
|
||||
public override Effect Effect => new() { Type = EffectType.Veneration, Duration = 4 };
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class WasteNot : BaseBuffAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Durability;
|
||||
public override int Level => 15;
|
||||
public override uint ActionId => 4631;
|
||||
|
||||
public override int CPCost => 56;
|
||||
|
||||
public override Effect Effect => new() { Type = EffectType.WasteNot, Duration = 4 };
|
||||
public override EffectType[] ConflictingEffects => new[] { EffectType.WasteNot2 };
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Craftimizer.Simulator.Actions;
|
||||
|
||||
internal class WasteNot2 : BaseBuffAction
|
||||
{
|
||||
public override ActionCategory Category => ActionCategory.Durability;
|
||||
public override int Level => 47;
|
||||
public override uint ActionId => 4639;
|
||||
|
||||
public override int CPCost => 98;
|
||||
|
||||
public override Effect Effect => new() { Type = EffectType.WasteNot2, Duration = 8 };
|
||||
public override EffectType[] ConflictingEffects => new[] { EffectType.WasteNot };
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Craftimizer.Simulator;
|
||||
|
||||
public record CharacterStats
|
||||
{
|
||||
public int Craftsmanship { get; init; }
|
||||
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 { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace Craftimizer.Simulator;
|
||||
|
||||
public enum ClassJob
|
||||
{
|
||||
Carpenter,
|
||||
Blacksmith,
|
||||
Armorer,
|
||||
Goldsmith,
|
||||
Leatherworker,
|
||||
Weaver,
|
||||
Alchemist,
|
||||
Culinarian
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Craftimizer.Simulator;
|
||||
|
||||
public enum CompletionState
|
||||
{
|
||||
Incomplete,
|
||||
ProgressComplete,
|
||||
NoMoreDurability,
|
||||
|
||||
Other
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace Craftimizer.Simulator;
|
||||
|
||||
public enum Condition : ushort
|
||||
{
|
||||
Poor = 0x0008,
|
||||
Normal = 0x0001,
|
||||
Good = 0x0002,
|
||||
Excellent = 0x0004,
|
||||
|
||||
Centered = 0x0010,
|
||||
Sturdy = 0x0020,
|
||||
Pliant = 0x0040,
|
||||
Malleable = 0x0080,
|
||||
Primed = 0x0100,
|
||||
GoodOmen = 0x0200,
|
||||
}
|
||||
|
||||
public static class ConditionUtils
|
||||
{
|
||||
public static Condition[] GetPossibleConditions(ushort conditionsFlag) =>
|
||||
Enum.GetValues<Condition>().Where(c => ((Condition)conditionsFlag).HasFlag(c)).ToArray();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Craftimizer.Simulator;
|
||||
|
||||
public readonly record struct Effect
|
||||
{
|
||||
public EffectType Type { get; init; }
|
||||
public int? Duration { get; init; }
|
||||
public int? Strength { get; init; }
|
||||
|
||||
public bool HasDuration => Duration != null;
|
||||
public bool HasStrength => Strength != null;
|
||||
|
||||
public Effect DecrementDuration() => this with { Duration = Duration - 1 };
|
||||
public Effect IncrementStrength() => this with { Strength = Strength + 1 };
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Craftimizer.Simulator;
|
||||
|
||||
public enum EffectType
|
||||
{
|
||||
InnerQuiet,
|
||||
WasteNot,
|
||||
Veneration,
|
||||
GreatStrides,
|
||||
Innovation,
|
||||
FinalAppraisal,
|
||||
WasteNot2,
|
||||
MuscleMemory,
|
||||
Manipulation,
|
||||
HeartAndSoul,
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
namespace Craftimizer.Simulator;
|
||||
|
||||
public record RecipeInfo
|
||||
{
|
||||
public bool IsExpert { get; init; }
|
||||
public int ClassJobLevel { get; init; }
|
||||
public int RLvl { get; init; }
|
||||
public ushort ConditionsFlag { get; init; }
|
||||
public int MaxDurability { get; init; }
|
||||
public int MaxQuality { get; init; }
|
||||
public int MaxProgress { get; init; }
|
||||
|
||||
public int QualityModifier { get; init; }
|
||||
public int QualityDivider { get; init; }
|
||||
public int ProgressModifier { get; init; }
|
||||
public int ProgressDivider { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using System;
|
||||
|
||||
namespace Craftimizer.Simulator;
|
||||
|
||||
public readonly record struct SimulationInput
|
||||
{
|
||||
public CharacterStats Stats { get; init; }
|
||||
public RecipeInfo Recipe { get; init; }
|
||||
public Random Random { get; init; }
|
||||
|
||||
public Condition[] AvailableConditions => ConditionUtils.GetPossibleConditions(Recipe.ConditionsFlag);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Craftimizer.Simulator.Actions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Craftimizer.Simulator;
|
||||
|
||||
public readonly record struct SimulationState
|
||||
{
|
||||
public SimulationInput Input { get; init; }
|
||||
|
||||
public int ActionCount => ActionHistory.Count;
|
||||
|
||||
public int StepCount { get; init; }
|
||||
public int Progress { get; init; }
|
||||
public int Quality { get; init; }
|
||||
public int Durability { get; init; }
|
||||
public int CP { get; init; }
|
||||
public Condition Condition { get; init; }
|
||||
public List<Effect> ActiveEffects { get; init; }
|
||||
public List<ActionType> ActionHistory { get; init; }
|
||||
|
||||
// https://github.com/ffxiv-teamcraft/simulator/blob/0682dfa76043ff4ccb38832c184d046ceaff0733/src/model/tables.ts#L2
|
||||
private static readonly int[] HQPercentTable = {
|
||||
1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8,
|
||||
9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, 16, 16, 17, 17,
|
||||
17, 18, 18, 18, 19, 19, 20, 20, 21, 22, 23, 24, 26, 28, 31, 34, 38, 42, 47, 52, 58, 64, 68, 71,
|
||||
74, 76, 78, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 94, 96, 98, 100
|
||||
};
|
||||
public int HQPercent => HQPercentTable[(int)Math.Clamp((float)Quality / Input.Recipe.MaxQuality * 100, 0, 100)];
|
||||
|
||||
public bool IsFirstStep => StepCount == 0;
|
||||
|
||||
public SimulationState(SimulationInput input)
|
||||
{
|
||||
Input = input;
|
||||
|
||||
StepCount = 0;
|
||||
Progress = 0;
|
||||
Quality = 0;
|
||||
Durability = Input.Recipe.MaxDurability;
|
||||
CP = Input.Stats.CP;
|
||||
Condition = Condition.Normal;
|
||||
ActiveEffects = new();
|
||||
ActionHistory = new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,345 @@
|
||||
using Craftimizer.Simulator.Actions;
|
||||
|
||||
namespace Craftimizer.Simulator;
|
||||
|
||||
public class Simulator
|
||||
{
|
||||
public SimulationInput Input { get; private set; }
|
||||
public int StepCount { get; private set; }
|
||||
public int Progress { get; private set; }
|
||||
public int Quality { get; private set; }
|
||||
public int Durability { get; private set; }
|
||||
public int CP { get; private set; }
|
||||
public Condition Condition { get; private set; }
|
||||
public List<Effect> ActiveEffects { get; private set; }
|
||||
public List<ActionType> ActionHistory { get; private set; }
|
||||
|
||||
public bool IsFirstStep => StepCount == 0;
|
||||
|
||||
public CompletionState CompletionState
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Progress >= Input.Recipe.MaxProgress)
|
||||
return CompletionState.ProgressComplete;
|
||||
if (Durability <= 0)
|
||||
return CompletionState.NoMoreDurability;
|
||||
return CompletionState.Incomplete;
|
||||
}
|
||||
}
|
||||
public virtual bool IsComplete => CompletionState != CompletionState.Incomplete;
|
||||
|
||||
public IEnumerable<ActionType> AvailableActions => ActionUtils.AvailableActions(this);
|
||||
|
||||
#pragma warning disable CS8618 // Emplace sets all the fields already
|
||||
public Simulator(SimulationState state)
|
||||
#pragma warning restore CS8618
|
||||
{
|
||||
Emplace(state);
|
||||
}
|
||||
|
||||
private void Emplace(SimulationState state)
|
||||
{
|
||||
Input = state.Input;
|
||||
StepCount = state.StepCount;
|
||||
Progress = state.Progress;
|
||||
Quality = state.Quality;
|
||||
Durability = state.Durability;
|
||||
CP = state.CP;
|
||||
Condition = state.Condition;
|
||||
ActiveEffects = new(state.ActiveEffects);
|
||||
ActionHistory = new(state.ActionHistory);
|
||||
}
|
||||
|
||||
private SimulationState Displace() => new()
|
||||
{
|
||||
Input = Input,
|
||||
StepCount = StepCount,
|
||||
Progress = Progress,
|
||||
Quality = Quality,
|
||||
Durability = Durability,
|
||||
CP = CP,
|
||||
Condition = Condition,
|
||||
ActiveEffects = ActiveEffects!,
|
||||
ActionHistory = ActionHistory!,
|
||||
};
|
||||
|
||||
public (ActionResponse Response, SimulationState NewState) Execute(SimulationState state, ActionType action)
|
||||
{
|
||||
Emplace(state);
|
||||
return (Execute(action), Displace());
|
||||
}
|
||||
|
||||
private ActionResponse Execute(ActionType action)
|
||||
{
|
||||
if (IsComplete)
|
||||
return ActionResponse.SimulationComplete;
|
||||
|
||||
var baseAction = action.With(this);
|
||||
if (!baseAction.CanUse)
|
||||
{
|
||||
if (baseAction.Level > Input.Stats.Level)
|
||||
return ActionResponse.ActionNotUnlocked;
|
||||
if (baseAction.CPCost > CP)
|
||||
return ActionResponse.NotEnoughCP;
|
||||
return ActionResponse.CannotUseAction;
|
||||
}
|
||||
|
||||
baseAction.Use();
|
||||
ActionHistory!.Add(action);
|
||||
|
||||
for (var i = 0; i < ActiveEffects!.Count; ++i)
|
||||
{
|
||||
var effect = ActiveEffects[i].DecrementDuration();
|
||||
if (effect.Duration == 0)
|
||||
{
|
||||
ActiveEffects.RemoveAt(i);
|
||||
--i;
|
||||
}
|
||||
else
|
||||
ActiveEffects[i] = effect;
|
||||
}
|
||||
|
||||
return ActionResponse.UsedAction;
|
||||
}
|
||||
|
||||
private int GetEffectIdx(EffectType effect) =>
|
||||
ActiveEffects!.FindIndex(e => e.Type == effect);
|
||||
|
||||
public Effect? GetEffect(EffectType effect)
|
||||
{
|
||||
var idx = GetEffectIdx(effect);
|
||||
return idx == -1 ? null : ActiveEffects![idx];
|
||||
}
|
||||
|
||||
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
|
||||
if (duration != null)
|
||||
duration++;
|
||||
|
||||
var newEffect = new Effect { Type = effect, Duration = duration, Strength = strength };
|
||||
|
||||
var effectIdx = GetEffectIdx(effect);
|
||||
if (effectIdx != -1)
|
||||
ActiveEffects![effectIdx] = newEffect;
|
||||
else
|
||||
ActiveEffects!.Add(newEffect);
|
||||
}
|
||||
|
||||
public void StrengthenEffect(EffectType effect, int? duration = null)
|
||||
{
|
||||
if (duration != null)
|
||||
duration += 1;
|
||||
|
||||
var effectIdx = GetEffectIdx(effect);
|
||||
if (effectIdx != -1)
|
||||
{
|
||||
if (effect == EffectType.InnerQuiet && ActiveEffects![effectIdx].Strength < 10)
|
||||
ActiveEffects[effectIdx] = ActiveEffects[effectIdx].IncrementStrength();
|
||||
}
|
||||
else
|
||||
ActiveEffects!.Add(new Effect { Type = effect, Duration = duration, Strength = 1 });
|
||||
}
|
||||
|
||||
public void RemoveEffect(EffectType effect) =>
|
||||
ActiveEffects!.RemoveAll(e => e.Type == effect);
|
||||
|
||||
public bool HasEffect(EffectType effect) =>
|
||||
ActiveEffects!.Any(e => e.Type == effect);
|
||||
|
||||
public bool IsPreviousAction(ActionType action, int stepsBack = 1) =>
|
||||
ActionHistory!.Count >= stepsBack && ActionHistory[^stepsBack] == action;
|
||||
|
||||
public int CountPreviousAction(ActionType action) =>
|
||||
ActionHistory!.Count(a => a == action);
|
||||
|
||||
public virtual bool RollSuccessRaw(float successRate) =>
|
||||
successRate >= Input.Random.NextSingle();
|
||||
|
||||
public bool RollSuccess(float successRate) =>
|
||||
RollSuccessRaw(CalculateSuccessRate(successRate));
|
||||
|
||||
public void IncreaseStepCount()
|
||||
{
|
||||
StepCount++;
|
||||
StepCondition();
|
||||
}
|
||||
|
||||
private static float GetConditionChance(SimulationInput input, Condition condition) =>
|
||||
condition switch
|
||||
{
|
||||
Condition.Good => input.Recipe.IsExpert ? 0.12f : (input.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
|
||||
};
|
||||
|
||||
private Condition GetNextRandomCondition()
|
||||
{
|
||||
var conditionChance = Input.Random.NextSingle();
|
||||
|
||||
foreach (var condition in Input.AvailableConditions)
|
||||
if ((conditionChance -= GetConditionChance(Input, condition)) < 0)
|
||||
return condition;
|
||||
|
||||
return Condition.Normal;
|
||||
}
|
||||
|
||||
public virtual void StepCondition()
|
||||
{
|
||||
Condition = Condition switch
|
||||
{
|
||||
Condition.Poor => Condition.Normal,
|
||||
Condition.Good => Condition.Normal,
|
||||
Condition.Excellent => Condition.Poor,
|
||||
Condition.GoodOmen => Condition.Good,
|
||||
_ => GetNextRandomCondition()
|
||||
};
|
||||
}
|
||||
|
||||
public void RestoreDurability(int amount)
|
||||
{
|
||||
Durability += amount;
|
||||
|
||||
if (Durability > Input.Recipe.MaxDurability)
|
||||
Durability = Input.Recipe.MaxDurability;
|
||||
}
|
||||
|
||||
public void RestoreCP(int amount)
|
||||
{
|
||||
CP += amount;
|
||||
|
||||
if (CP > Input.Stats.CP)
|
||||
CP = Input.Stats.CP;
|
||||
}
|
||||
|
||||
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(EffectType.MuscleMemory))
|
||||
{
|
||||
buffModifier += 1.00f;
|
||||
if (!dryRun)
|
||||
RemoveEffect(EffectType.MuscleMemory);
|
||||
}
|
||||
if (HasEffect(EffectType.Veneration))
|
||||
buffModifier += 0.50f;
|
||||
|
||||
var conditionModifier = Condition switch
|
||||
{
|
||||
Condition.Malleable => 1.50f,
|
||||
_ => 1.00f
|
||||
};
|
||||
|
||||
// https://github.com/NotRanged/NotRanged.github.io/blob/0f4aee074f969fb05aad34feaba605057c08ffd1/app/js/ffxivcraftmodel.js#L88
|
||||
var baseIncrease = (Input.Stats.Craftsmanship * 10f / Input.Recipe.ProgressDivider) + 2;
|
||||
if (Input.Stats.CLvl <= Input.Recipe.RLvl)
|
||||
baseIncrease *= Input.Recipe.ProgressModifier / 100f;
|
||||
baseIncrease = MathF.Floor(baseIncrease);
|
||||
|
||||
var progressGain = (int)(baseIncrease * efficiency * conditionModifier * buffModifier);
|
||||
return progressGain;
|
||||
}
|
||||
|
||||
public int CalculateQualityGain(float efficiency, bool dryRun = true)
|
||||
{
|
||||
var buffModifier = 1.00f;
|
||||
if (HasEffect(EffectType.GreatStrides))
|
||||
{
|
||||
buffModifier += 1.00f;
|
||||
if (!dryRun)
|
||||
RemoveEffect(EffectType.GreatStrides);
|
||||
}
|
||||
if (HasEffect(EffectType.Innovation))
|
||||
buffModifier += 0.50f;
|
||||
|
||||
buffModifier *= 1 + ((GetEffect(EffectType.InnerQuiet)?.Strength ?? 0) * 0.10f);
|
||||
|
||||
var conditionModifier = Condition switch
|
||||
{
|
||||
Condition.Poor => 0.50f,
|
||||
Condition.Good => Input.Stats.HasRelic ? 1.75f : 1.50f,
|
||||
Condition.Excellent => 4.00f,
|
||||
_ => 1.00f,
|
||||
};
|
||||
|
||||
var baseIncrease = (Input.Stats.Control * 10f / Input.Recipe.QualityDivider) + 35;
|
||||
if (Input.Stats.CLvl <= Input.Recipe.RLvl)
|
||||
baseIncrease *= Input.Recipe.QualityModifier / 100f;
|
||||
baseIncrease = MathF.Floor(baseIncrease);
|
||||
|
||||
var qualityGain = (int)(baseIncrease * efficiency * conditionModifier * buffModifier);
|
||||
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(EffectType.FinalAppraisal) && Progress >= Input.Recipe.MaxProgress)
|
||||
{
|
||||
Progress = Input.Recipe.MaxProgress - 1;
|
||||
RemoveEffect(EffectType.FinalAppraisal);
|
||||
}
|
||||
}
|
||||
|
||||
public void IncreaseQualityRaw(int qualityGain)
|
||||
{
|
||||
Quality += qualityGain;
|
||||
|
||||
if (Input.Stats.Level >= 11)
|
||||
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, false));
|
||||
|
||||
public void IncreaseQuality(float efficiency) =>
|
||||
IncreaseQualityRaw(CalculateQualityGain(efficiency, false));
|
||||
}
|
||||
Reference in New Issue
Block a user