diff --git a/src/EVEMon.Common/Models/BaseCharacter.cs b/src/EVEMon.Common/Models/BaseCharacter.cs
index d9661dfad..99f4412d3 100644
--- a/src/EVEMon.Common/Models/BaseCharacter.cs
+++ b/src/EVEMon.Common/Models/BaseCharacter.cs
@@ -368,7 +368,7 @@ public TimeSpan GetTrainingTimeWithoutBoosters(StaticSkill skill, long level, Tr
///
///
///
- private static TimeSpan GetTrainingTime(long sp, float spPerHour)
+ internal static TimeSpan GetTrainingTime(long sp, float spPerHour)
=> Math.Abs(spPerHour) < float.Epsilon ? TimeSpan.FromDays(999.0) : TimeSpan.FromHours(sp / spPerHour);
///
diff --git a/src/EVEMon.Common/Models/QueuedSkill.cs b/src/EVEMon.Common/Models/QueuedSkill.cs
index 2431026ed..bba7dde05 100644
--- a/src/EVEMon.Common/Models/QueuedSkill.cs
+++ b/src/EVEMon.Common/Models/QueuedSkill.cs
@@ -182,6 +182,31 @@ public double SkillPointsPerHour
}
}
+ ///
+ /// Gets the training speed without boosters.
+ ///
+ ///
+ public double SkillPointsPerHourWithoutBoosters
+ {
+ get
+ {
+ double rate;
+ if (Skill == Skill.UnknownSkill)
+ {
+ // Based on estimated end time - start time
+ double time = EndTime.Subtract(StartTime).TotalHours;
+ if (time <= 0.0)
+ // Do not divide by zero
+ rate = 0.0;
+ else
+ rate = Math.Ceiling((EndSP - StartSP) / time);
+ }
+ else
+ rate = Skill.SkillPointsPerHourWithoutBoosters;
+ return rate;
+ }
+ }
+
///
/// Computes the remaining time.
///
@@ -212,11 +237,69 @@ public bool IsTraining
}
}
+ public TimeSpan BoosterDuration
+ {
+ get
+ {
+ var remainingTime = RemainingTime;
+ var queueTime = EndTime - StartTime;
+
+ var expectedTime = Owner.GetTimeSpanForPointsWithoutBoosters(Skill.StaticData, Level);
+
+ var actualSPRate = Owner.GetBaseSPPerHour(Skill.StaticData);
+ var expectedSPRate = Owner.GetBaseSPPerHourWithoutBoosters(Skill.StaticData);
+
+ if (expectedTime > remainingTime || !IsTraining && (expectedTime > queueTime))
+ {
+ // Booster detected!
+ var remainingSP = EndSP - CurrentSP;
+ var expectedSPInActualTime = IsTraining ?
+ Math.Round(remainingTime.TotalHours * expectedSPRate) : Math.Round(queueTime.TotalHours * expectedSPRate);
+
+ var spRateDiff = actualSPRate - expectedSPRate;
+
+ if (spRateDiff <= 0)
+ {
+ return TimeSpan.Zero;
+ }
+
+ var boosterHours = (remainingSP - expectedSPInActualTime) / spRateDiff;
+
+ return TimeSpan.FromHours(boosterHours);
+ }
+ return TimeSpan.Zero;
+ }
+ }
+
///
/// Gets true if the training has been completed, false otherwise.
///
public bool IsCompleted => EndTime <= DateTime.UtcNow;
+ ///
+ /// Calculate the time it will take to train a certain amount of skill points.
+ ///
+ /// The amount of skill points.
+ /// Time it will take.
+ public TimeSpan GetTimeSpanForPoints(long points)
+ {
+ if (BoosterDuration > TimeSpan.Zero)
+ {
+ var c = Owner as CCPCharacter;
+ if (c == null)
+ {
+ return TimeSpan.Zero;
+ }
+
+ var actualSPRate = c.GetBaseSPPerHour(Skill.StaticData);
+ var expectedSPRate = c.GetBaseSPPerHourWithoutBoosters(Skill.StaticData);
+
+ return Skill.GetTimeSpanForPoints(points, expectedSPRate, actualSPRate, BoosterDuration);
+ }
+
+ return Skill.GetTimeSpanForPoints(points);
+ }
+
public override bool Equals(object obj)
{
var other = obj as QueuedSkill;
diff --git a/src/EVEMon.Common/Models/Skill.cs b/src/EVEMon.Common/Models/Skill.cs
index a64b1ed2b..d26777bd2 100644
--- a/src/EVEMon.Common/Models/Skill.cs
+++ b/src/EVEMon.Common/Models/Skill.cs
@@ -501,9 +501,26 @@ public Skill ToCharacter(Character character)
///
/// The amount of skill points.
/// Time it will take.
- public TimeSpan GetTimeSpanForPoints(long points)
+ public TimeSpan GetTimeSpanForPoints(long points)
=> Character?.GetTimeSpanForPoints(this, points) ?? TimeSpan.Zero;
+ public TimeSpan GetTimeSpanForPoints(long points, float normalRate, float boostedRate, TimeSpan boostedDuration)
+ {
+ var boostedPoints = (long)Math.Round(boostedRate * boostedDuration.TotalHours);
+ var normalPoints = points - boostedPoints;
+
+ var boostedTime = BaseCharacter.GetTrainingTime(boostedPoints, boostedRate);
+ var normalTime = BaseCharacter.GetTrainingTime(normalPoints, normalRate);
+
+ if (normalPoints <= 0)
+ {
+ // Skill will complete training while booster is active!
+ return boostedTime;
+ }
+
+ return normalTime + boostedTime;
+ }
+
///
/// Calculates the cumulative points required to reach the given level of this skill, starting from the current SP.
///
diff --git a/src/EVEMon.Common/Models/SkillQueue.cs b/src/EVEMon.Common/Models/SkillQueue.cs
index d027dce01..3f0846252 100644
--- a/src/EVEMon.Common/Models/SkillQueue.cs
+++ b/src/EVEMon.Common/Models/SkillQueue.cs
@@ -64,35 +64,7 @@ internal void Dispose()
///
/// Gets the expected booster duration
///
- public TimeSpan BoosterDuration => !Items.Any() ? TimeSpan.Zero : TimeSpan.FromHours(Items.Select(i => {
- var remainingTime = i.RemainingTime;
- var queueTime = i.EndTime - i.StartTime;
-
- var expectedTime = m_character.GetTimeSpanForPointsWithoutBoosters(i.Skill.StaticData, i.Level);
-
- var actualSPRate = m_character.GetBaseSPPerHour(i.Skill.StaticData);
- var expectedSPRate = m_character.GetBaseSPPerHourWithoutBoosters(i.Skill.StaticData);
-
- if (expectedTime > remainingTime || !i.IsTraining && (expectedTime > queueTime))
- {
- // Booster detected!
- var remainingSP = i.EndSP - i.CurrentSP;
- var expectedSPInActualTime = i.IsTraining ?
- Math.Round(remainingTime.TotalHours * expectedSPRate) : Math.Round(queueTime.TotalHours * expectedSPRate);
-
- var spRateDiff = actualSPRate - expectedSPRate;
-
- if (spRateDiff <= 0)
- {
- return TimeSpan.Zero.TotalHours;
- }
-
- var boosterHours = (remainingSP - expectedSPInActualTime) / spRateDiff;
-
- return boosterHours;
- }
- return TimeSpan.Zero.TotalHours;
- }).Sum());
+ public TimeSpan BoosterDuration => !Items.Any() ? TimeSpan.Zero : TimeSpan.FromHours(Items.Select(i => i.BoosterDuration.TotalHours).Sum());
///
/// Gets the skill currently in training.
diff --git a/src/EVEMon/CharacterMonitoring/CharacterSkillsQueueList.cs b/src/EVEMon/CharacterMonitoring/CharacterSkillsQueueList.cs
index c5aab20a1..5a064d856 100644
--- a/src/EVEMon/CharacterMonitoring/CharacterSkillsQueueList.cs
+++ b/src/EVEMon/CharacterMonitoring/CharacterSkillsQueueList.cs
@@ -234,15 +234,15 @@ private void DrawItem(QueuedSkill skill, DrawItemEventArgs e)
skill.Skill.StaticData.GetPointsRequiredForLevel(Math.Min(skill.Level, 5));
long pointsLeft = skillPointsToNextLevel - skillPoints;
TimeSpan timeSpanFromPoints = !hasSkill ? skill.EndTime.Subtract(DateTime.UtcNow) :
- skill.Skill.GetTimeSpanForPoints(pointsLeft);
+ skill.GetTimeSpanForPoints(pointsLeft);
string remainingTimeText = timeSpanFromPoints.ToDescriptiveText(
DescriptiveTextOptions.SpaceBetween);
double fractionCompleted = e.Index == 0 ? skill.FractionCompleted : 0.0;
- string indexText = $"{e.Index + 1}. ";
+ string indexText = $"{e.Index + 1}. {(skill.BoosterDuration > TimeSpan.Zero ? "\u26a1" : "")}";
string rankText = $" (Rank {(skill.Skill == null ? 0 : skill.Rank)})";
- string spPerHourText = $" SP/Hour: {skill.SkillPointsPerHour}";
+ string spPerHourText = $" SP/Hour: {(skill.BoosterDuration > TimeSpan.Zero ? skill.SkillPointsPerHour : skill.SkillPointsPerHourWithoutBoosters)}";
string spText = $"SP: {skillPoints:N0}/{skillPointsToNextLevel:N0}";
string trainingTimeText = $" Training Time: {remainingTimeText}";
string levelText = $"Level {skill.Level}";
@@ -646,7 +646,7 @@ private static string GetTooltip(QueuedSkill skill)
long pointsLeft = nextLevelSP - sp;
TimeSpan timeSpanFromPoints = skill.Skill == Skill.UnknownSkill
? skill.EndTime.Subtract(DateTime.UtcNow)
- : skill.Skill.GetTimeSpanForPoints(pointsLeft);
+ : skill.GetTimeSpanForPoints(pointsLeft);
string remainingTimeText = timeSpanFromPoints.ToDescriptiveText(
DescriptiveTextOptions.IncludeCommas | DescriptiveTextOptions.UppercaseText);
@@ -769,7 +769,7 @@ private static void AddSkillBoilerPlate(StringBuilder toolTip, QueuedSkill skill
toolTip.AppendLine().AppendLine(skill.Skill.Description.WordWrap(100));
toolTip.Append("Primary: ").Append(skill.Skill.PrimaryAttribute).Append(", ");
toolTip.Append("Secondary: ").Append(skill.Skill.SecondaryAttribute).Append(" (");
- toolTip.AppendFormat("{0:F0}", skill.SkillPointsPerHour).Append(" SP/Hour)");
+ toolTip.AppendFormat("{0:F0}", skill.BoosterDuration > TimeSpan.Zero ? skill.SkillPointsPerHour : skill.SkillPointsPerHourWithoutBoosters).Append(" SP/Hour)");
}
///