/// <summary> /// Updates the statistics of the entries in the same way the given character would train this plan. /// </summary> /// <param name="scratchpad">The scratchpad.</param> /// <param name="applyRemappingPoints">if set to <c>true</c> [apply remapping points].</param> /// <param name="trainSkills">When true, the character will train every skill, increasing SP, etc.</param> /// <exception cref="System.ArgumentNullException">scratchpad</exception> public void UpdateStatistics(CharacterScratchpad scratchpad, bool applyRemappingPoints, bool trainSkills) { scratchpad.ThrowIfNull(nameof(scratchpad)); CharacterScratchpad scratchpadWithoutImplants = scratchpad.Clone(); scratchpadWithoutImplants.ClearImplants(); DateTime time = DateTime.Now; // Update the statistics foreach (PlanEntry entry in Items) { // Apply the remapping if (applyRemappingPoints && entry.Remapping != null && entry.Remapping.Status == RemappingPointStatus.UpToDate) { scratchpad.Remap(entry.Remapping); scratchpadWithoutImplants.Remap(entry.Remapping); } // Update entry's statistics entry.UpdateStatistics(scratchpad, scratchpadWithoutImplants, ref time); // Update the scratchpad if (!trainSkills) { continue; } scratchpad.Train(entry.Skill, entry.Level); scratchpadWithoutImplants.Train(entry.Skill, entry.Level); } }
/// <summary> /// Updates controls on the form. /// </summary> /// <param name="remapping">An <see cref="AttributesOptimizer.Remapping"/> object</param> /// <param name="remappingList">List of remappings</param> private void UpdateForm(AttributesOptimizer.RemappingResult remapping, List <AttributesOptimizer.RemappingResult> remappingList) { // If the thread has been canceled, we stop right now to prevent an exception if (m_thread == null) { return; } // Hide the throbber and the waiting message this.throbber.State = ThrobberState.Stopped; this.panelWait.Visible = false; // Update the attributes if (remapping != null) { m_statisticsScratchpad = remapping.BestScratchpad.Clone(); UpdateForRemapping(remapping); } else { UpdateForRemappingList(remappingList); } // Update the plan order's column if (m_planEditor != null && (remapping != null || remappingList.Count != 0)) { this.m_planEditor.ShowWithPluggable(this); } m_thread = null; }
/// <summary> /// Gets a character scratchpad representing this character after the provided skill levels trainings. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="trainings"></param> /// <returns></returns> public CharacterScratchpad After <T>(IEnumerable <T> trainings) where T : ISkillLevel { CharacterScratchpad scratchpad = new CharacterScratchpad(this); scratchpad.Train(trainings); return(scratchpad); }
/// <summary> /// Constructor without any remapping point associated. /// </summary> /// <param name="baseScratchpad">The base scratchpad.</param> /// <exception cref="System.ArgumentNullException">baseScratchpad</exception> public RemappingResult(CharacterScratchpad baseScratchpad) { baseScratchpad.ThrowIfNull(nameof(baseScratchpad)); Skills = new Collection <ISkillLevel>(); BaseScratchpad = baseScratchpad; StartTime = BaseScratchpad.TrainingTime; }
/// <summary> /// Constructor without any remapping point associated. /// </summary> /// <param name="baseScratchpad">The base scratchpad.</param> /// <exception cref="System.ArgumentNullException">baseScratchpad</exception> public RemappingResult(CharacterScratchpad baseScratchpad) { baseScratchpad.ThrowIfNull(nameof(baseScratchpad)); Skills = new Collection<ISkillLevel>(); BaseScratchpad = baseScratchpad; StartTime = BaseScratchpad.TrainingTime; }
/// <summary> /// Constructor for a result bound to a remapping point. /// </summary> /// <param name="point">Associated remapping point, may be null.</param> /// <param name="baseScratchpad"></param> public RemappingResult(RemappingPoint point, CharacterScratchpad baseScratchpad) : this(baseScratchpad) { if (point == null) return; Point = point; }
/// <summary> /// Constructor for a manually edited result from a base result. /// </summary> /// <param name="result">Associated remapping point, may be null.</param> /// <param name="bestScratchpad">The best scratchpad.</param> /// <exception cref="System.ArgumentNullException">result</exception> public RemappingResult(RemappingResult result, CharacterScratchpad bestScratchpad) : this(result?.Point, result?.BaseScratchpad) { result.ThrowIfNull(nameof(result)); Skills.AddRange(result.Skills); BestScratchpad = bestScratchpad; }
/// <summary> /// Constructor for a result bound to a remapping point. /// </summary> /// <param name="point">Associated remapping point, may be null.</param> /// <param name="baseScratchpad"></param> public RemappingResult(RemappingPoint point, CharacterScratchpad baseScratchpad) : this(baseScratchpad) { if (point == null) { return; } Point = point; }
/// <summary> /// Updates the statistics for the plan editor. /// </summary> /// <param name="plan"></param> /// <param name="areRemappingPointsActive"></param> /// <exception cref="System.ArgumentNullException">plan</exception> public void UpdateStatistics(BasePlan plan, out bool areRemappingPointsActive) { plan.ThrowIfNull(nameof(plan)); areRemappingPointsActive = true; CharacterScratchpad scratchpad = CreateModifiedScratchpad(m_character.After(m_plan.ChosenImplantSet)); plan.UpdateStatistics(scratchpad, true, true); plan.UpdateOldTrainingTimes(); }
/// <summary> /// Updates the labels on the right of the numeric box. /// </summary> /// <param name="attrib"></param> /// <param name="myValue"></param> /// <param name="lblAdjust"></param> /// <param name="lblEffectiveAttribute"></param> private void UpdateAttributeLabels(EveAttribute attrib, int myValue, Control lblAdjust, Control lblEffectiveAttribute) { CharacterScratchpad characterScratchpad = m_plan.Character.After(m_plan.ChosenImplantSet); long baseAttr = characterScratchpad[attrib].EffectiveValue - characterScratchpad[attrib].ImplantBonus; long adjust = myValue - baseAttr; lblAdjust.ForeColor = adjust >= 0 ? SystemColors.ControlText : Color.Red; lblAdjust.Text = $"{(adjust >= 0 ? "+" : string.Empty)}{adjust}"; lblEffectiveAttribute.Text = characterScratchpad[attrib].EffectiveValue.ToNumericString(0); }
/// <summary> /// Gets a character scratchpad representing this character after a switch to the provided implant set. /// </summary> /// <param name="set">The set.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">set</exception> public CharacterScratchpad After(ImplantSet set) { set.ThrowIfNull(nameof(set)); CharacterScratchpad scratchpad = new CharacterScratchpad(this); for (int i = 0; i < 5; i++) { EveAttribute attribute = (EveAttribute)i; scratchpad[attribute].ImplantBonus = set[attribute].Bonus; } return(scratchpad); }
/// <summary> /// Creates a scratchpad with the new implants values. /// </summary> /// <returns></returns> private CharacterScratchpad CreateModifiedScratchpad(BaseCharacter character) { // Creates a scratchpad with new implants CharacterScratchpad scratchpad = new CharacterScratchpad(character); scratchpad.Memory.ImplantBonus += (int)nudMemory.Value - character.Memory.EffectiveValue; scratchpad.Charisma.ImplantBonus += (int)nudCharisma.Value - character.Charisma.EffectiveValue; scratchpad.Intelligence.ImplantBonus += (int)nudIntelligence.Value - character.Intelligence.EffectiveValue; scratchpad.Perception.ImplantBonus += (int)nudPerception.Value - character.Perception.EffectiveValue; scratchpad.Willpower.ImplantBonus += (int)nudWillpower.Value - character.Willpower.EffectiveValue; return(scratchpad); }
/// <summary> /// Creates a scrahcpad with the new implants values. /// </summary> /// <returns></returns> private CharacterScratchpad CreateModifiedScratchpad() { // Creates a scratchpad with new implants CharacterScratchpad scratchpad = new CharacterScratchpad(m_character); scratchpad.Memory.ImplantBonus += (int)this.nudMemory.Value - m_character.Memory.PreLearningEffectiveAttribute; scratchpad.Charisma.ImplantBonus += (int)this.nudCharisma.Value - m_character.Charisma.PreLearningEffectiveAttribute; scratchpad.Intelligence.ImplantBonus += (int)this.nudIntelligence.Value - m_character.Intelligence.PreLearningEffectiveAttribute; scratchpad.Perception.ImplantBonus += (int)this.nudPerception.Value - m_character.Perception.PreLearningEffectiveAttribute; scratchpad.Willpower.ImplantBonus += (int)this.nudWillpower.Value - m_character.Willpower.PreLearningEffectiveAttribute; return(scratchpad); }
/// <summary> /// Gets the total training time for this plan, using the given scratchpad. /// </summary> /// <param name="scratchpad">The scratchpad to use for the computation, may be null.</param> /// <param name="applyRemappingPoints"></param> /// <returns></returns> public TimeSpan GetTotalTime(CharacterScratchpad scratchpad, bool applyRemappingPoints) { // No scratchpad ? Let's create one if (scratchpad == null) { scratchpad = new CharacterScratchpad(Character); } // Train entries TimeSpan time = TimeSpan.Zero; scratchpad.TrainEntries(Items, applyRemappingPoints); return(scratchpad.TrainingTime - time); }
/// <summary> /// Racalculating plan and summary page after change of a <see cref="AttributesOptimizationControl"/>. /// </summary> /// <param name="control"></param> /// <param name="remapping"></param> void AttributesOptimizationControl_AttributeChanged(AttributesOptimizationControl control, AttributesOptimizer.RemappingResult remapping) { // Update the plan order's column if (m_planEditor != null) { if (m_strategy == Strategy.RemappingPoints) { m_remappingDictionary[control] = remapping; UpdateSummaryInformation(m_remappingDictionary.Values); } m_statisticsScratchpad = remapping.BestScratchpad.Clone(); this.m_planEditor.ShowWithPluggable(this); } }
/// <summary> /// Updates the form controls to reflect the status of the Plan specified by the Plan property. /// </summary> private void UpdatePlanStatus() { if (m_loadoutFormat == LoadoutFormat.None) { return; } // Compute the skills to add m_skillsToAdd.Clear(); CharacterScratchpad scratchpad = new CharacterScratchpad(m_character); // Compute the training time for the prerequisites foreach (Item obj in m_objects) { scratchpad.Train(obj.Prerequisites.Where(x => m_character.Skills[x.Skill.ID].Level < x.Level)); } m_skillsToAdd.AddRange(scratchpad.TrainedSkills); // All skills already trained ? if (m_skillsToAdd.Count == 0) { AddToPlanButton.Enabled = false; PlanedLabel.Visible = true; PlanedLabel.Text = @"All skills already trained."; TrainTimeLabel.Visible = false; } // Are skills already planned ? else if (m_plan.AreSkillsPlanned(m_skillsToAdd)) { AddToPlanButton.Enabled = false; PlanedLabel.Visible = true; PlanedLabel.Text = @"All skills already trained or planned."; TrainTimeLabel.Visible = false; } // There is at least one untrained or non-planned skill else { AddToPlanButton.Enabled = true; PlanedLabel.Text = String.Empty; PlanedLabel.Visible = false; TrainTimeLabel.Visible = true; // Compute training time TimeSpan trainingTime = m_character.GetTrainingTimeToMultipleSkills(m_skillsToAdd); TrainTimeLabel.Text = trainingTime.ToDescriptiveText( DescriptiveTextOptions.IncludeCommas | DescriptiveTextOptions.SpaceText); } }
/// <summary> /// Recalculating plan and summary page after change of a <see cref="AttributesOptimizerControl"/>. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="AttributeChangedEventArgs"/> instance containing the event data.</param> private void AttributesOptimizationControl_AttributeChanged(object sender, AttributeChangedEventArgs e) { AttributesOptimizerControl control = (AttributesOptimizerControl)sender; if (m_strategy == AttributeOptimizationStrategy.RemappingPoints) { m_remappingDictionary[control] = e.Remapping; UpdateSummaryInformation(m_remappingDictionary.Values); } m_statisticsScratchpad = e.Remapping.BestScratchpad.Clone(); m_remapping = e.Remapping; // Update the plan order's column m_planEditor.ShowWithPluggable(this); }
/// <summary> /// Update the plan status : training time, skills already trained, etc /// </summary> private void UpdatePlanningControls() { // Are all the prerequisites trained ? if (m_prerequisites.All(x => m_character.GetSkillLevel(x.Skill) >= x.Level)) { btnPlan.Enabled = false; lblPlanned.Visible = true; lblPlanned.Text = "All skills already trained."; lblTrainTime.Visible = false; return; } // Are all the prerequisites planned ? if (m_plan.AreSkillsPlanned(m_prerequisites.Where(x => m_character.Skills[x.Skill].Level < x.Level))) { btnPlan.Enabled = false; lblPlanned.Visible = true; lblPlanned.Text = "All skills already trained or planned."; lblTrainTime.Visible = false; return; } // Compute the training time var scratchpad = new CharacterScratchpad(m_character); foreach (var entry in m_plan) { scratchpad.Train(entry); } var startTime = scratchpad.TrainingTime; foreach (var prereq in m_prerequisites) { scratchpad.Train(prereq); } var trainingTime = scratchpad.TrainingTime.Subtract(startTime); // update the labels btnPlan.Enabled = true; lblPlanned.Text = String.Empty; lblPlanned.Visible = false; lblTrainTime.Visible = true; lblTrainTime.Text = trainingTime.ToDescriptiveText( DescriptiveTextOptions.IncludeCommas | DescriptiveTextOptions.SpaceText); }
/// <summary> /// Update all the numeric boxes /// </summary> private async Task UpdateContentAsync() { gbAttributes.Text = $"Attributes of \"{m_plan.ChosenImplantSet.Name}\""; CharacterScratchpad characterScratchpad = m_plan.Character.After(m_plan.ChosenImplantSet); nudCharisma.Value = characterScratchpad.Charisma.EffectiveValue; nudWillpower.Value = characterScratchpad.Willpower.EffectiveValue; nudIntelligence.Value = characterScratchpad.Intelligence.EffectiveValue; nudPerception.Value = characterScratchpad.Perception.EffectiveValue; nudMemory.Value = characterScratchpad.Memory.EffectiveValue; // If the implant set isn't the active one we notify the user lblNotice.Visible = m_plan.ChosenImplantSet != m_character.ImplantSets.Current; // Update all the times on the right pane await UpdateTimesAsync(); }
/// <summary> /// Gets a string representation of the attribute. /// </summary> /// <param name="attrib">The attribute.</param> /// <param name="oldScratchpad">The old scratchpad.</param> /// <param name="newScratchpad">The new scratchpad.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException"> /// oldScratchpad /// or /// newScratchpad /// </exception> public static string GetStringForAttribute(EveAttribute attrib, CharacterScratchpad oldScratchpad, CharacterScratchpad newScratchpad) { oldScratchpad.ThrowIfNull(nameof(oldScratchpad)); newScratchpad.ThrowIfNull(nameof(newScratchpad)); long bonusDifference = newScratchpad[attrib].Base - oldScratchpad[attrib].Base; if (bonusDifference == 0) { return(newScratchpad[attrib].ToString("%N (0) = %e = (%B + %r + %i)")); } return(newScratchpad[attrib].ToString(bonusDifference > 0 ? $"%N (+{bonusDifference}) = %e = (%B + %r + %i)" : $"%N ({bonusDifference}) = %e = (%B + %r + %i)")); }
/// <summary> /// Update the plan status : training time, skills already known, etc /// </summary> private void UpdatePlanningControls() { // Are all the prerequisites known ? if (m_prerequisites.All(x => m_character.GetSkillLevel(x.Skill) > x.Level)) { btnPlan.Enabled = false; lblPlanned.Visible = true; lblPlanned.Text = "All skills already known."; lblTrainTime.Visible = false; return; } // Are all the prequisites planned if (m_plan.AreSkillsPlanned(m_prerequisites)) { btnPlan.Enabled = false; lblPlanned.Visible = true; lblPlanned.Text = "All skills already known or planned."; lblTrainTime.Visible = false; return; } // Compute the training time var scratchpad = new CharacterScratchpad(m_character); foreach (var entry in m_plan) { scratchpad.Train(entry); } var startTime = scratchpad.TrainingTime; foreach (var prereq in m_prerequisites) { scratchpad.Train(prereq); } var trainingTime = scratchpad.TrainingTime - startTime; // update the labels btnPlan.Enabled = true; lblPlanned.Text = ""; lblPlanned.Visible = false; lblTrainTime.Visible = true; lblTrainTime.Text = Skill.TimeSpanToDescriptiveText(trainingTime, DescriptiveTextOptions.IncludeCommas | DescriptiveTextOptions.SpaceText); }
/// <summary> /// Update all the times on the right pane (base time, best time, etc). /// </summary> private async Task UpdateTimesAsync() { // Current (with implants) TimeSpan currentSpan = await UpdateTimesForCharacter(m_character.After(m_plan.ChosenImplantSet)); // Current (without implants) ImplantSet noneImplantSet = m_character.ImplantSets.None; TimeSpan baseSpan = await UpdateTimesForCharacter(m_character.After(noneImplantSet)); // This CharacterScratchpad scratchpad = CreateModifiedScratchpad(m_character.After(m_plan.ChosenImplantSet)); TimeSpan thisSpan = await UpdateTimesForCharacter(scratchpad); lblCurrentSpan.Text = currentSpan.ToDescriptiveText(DescriptiveTextOptions.IncludeCommas); lblCurrentDate.Text = DateTime.Now.Add(currentSpan).ToString(CultureConstants.DefaultCulture); lblBaseSpan.Text = baseSpan.ToDescriptiveText(DescriptiveTextOptions.IncludeCommas); lblBaseDate.Text = DateTime.Now.Add(baseSpan).ToString(CultureConstants.DefaultCulture); lblThisSpan.Text = thisSpan.ToDescriptiveText(DescriptiveTextOptions.IncludeCommas); lblThisDate.Text = DateTime.Now.Add(thisSpan).ToString(CultureConstants.DefaultCulture); // Are the new attributes better than current (without implants) ? lblComparedToBase.ForeColor = thisSpan > baseSpan ? Color.Red : thisSpan < baseSpan ? Color.Green : SystemColors.ControlText; lblComparedToBase.Text = thisSpan > baseSpan ? $"{thisSpan.Subtract(baseSpan).ToDescriptiveText(DescriptiveTextOptions.IncludeCommas)} slower than current base" : thisSpan < baseSpan ? $"{baseSpan.Subtract(thisSpan).ToDescriptiveText(DescriptiveTextOptions.IncludeCommas)} faster than current base" : @"No time difference than current base"; // Are the new attributes better than current (with implants) ? lblComparedToCurrent.ForeColor = thisSpan > currentSpan ? Color.DarkRed : thisSpan < currentSpan ? Color.DarkGreen : SystemColors.ControlText; lblComparedToCurrent.Text = thisSpan > currentSpan ? $"{thisSpan.Subtract(currentSpan).ToDescriptiveText(DescriptiveTextOptions.IncludeCommas)} slower than current" : thisSpan < currentSpan ? $"{currentSpan.Subtract(thisSpan).ToDescriptiveText(DescriptiveTextOptions.IncludeCommas)} faster than current" : @"No time difference than current base"; // Update the plan's pluggable column m_planEditor?.ShowWithPluggable(this); }
/// <summary> /// Performs the sort. /// </summary> /// <returns></returns> public IEnumerable<PlanEntry> Sort() { int initialCount = m_entries.Count(); // Apply first pass (priorities grouping) // We split the entries into multiple priority groups if that selection is made List<PlanScratchpad> groupedPlan = new List<PlanScratchpad>(); var scratchpad = new CharacterScratchpad(m_character); if (m_groupByPriority) { foreach (var group in m_entries.GroupBy(x => x.Priority).OrderBy(x => x.Key)) { groupedPlan.Add(new PlanScratchpad(scratchpad, group)); } } else { groupedPlan.Add(new PlanScratchpad(scratchpad, m_entries)); } // Apply second pass (sorts) // We sort every group, and merge them once they're sorted List<PlanEntry> list = new List<PlanEntry>(); foreach (var group in groupedPlan) { group.UpdateStatistics(scratchpad, false, false); group.SimpleSort(m_sort, m_reverseOrder); list.AddRange(group); } // Fix prerequisites order FixPrerequisitesOrder(list); // Check we didn't mess up anything if (initialCount != list.Count) throw new UnauthorizedAccessException("The sort algorithm messed up and deleted items"); // Return return list; }
/// <summary> /// Updates the controls with the values from the current remapping point. /// </summary> /// <param name="point"></param> /// <exception cref="System.ArgumentNullException">point</exception> public void UpdateValuesFrom(RemappingPoint point) { point.ThrowIfNull(nameof(point)); // Creates a scratchpad with the base values from the provided point. CharacterScratchpad scratchpad = new CharacterScratchpad(m_character.After(m_plan.ChosenImplantSet)); for (int i = 0; i < 5; i++) { scratchpad[(EveAttribute)i].Base = point[(EveAttribute)i]; } RemappingResult remapping = new RemappingResult(m_remapping, scratchpad); remapping.Update(); // Update the controls UpdateControls(m_character, m_plan, remapping, m_description); // Fires the event AttributeChanged?.ThreadSafeInvoke(this, new AttributeChangedEventArgs(remapping)); }
/// <summary> /// Autonomously updates the status bar with the plan's training time. /// </summary> internal void UpdateStatusBar() { // Training time CharacterScratchpad scratchpad; if (m_plan.ChosenImplantSet != null) { scratchpad = m_plan.Character.After(m_plan.ChosenImplantSet); } else { scratchpad = new CharacterScratchpad(Character); } TimeSpan totalTime = planEditor.DisplayPlan.GetTotalTime(scratchpad, true); UpdateSkillStatusLabel(false, m_plan.Count, m_plan.UniqueSkillsCount); UpdateTimeStatusLabel(m_plan.Count, totalTime); UpdateCostStatusLabel(false, m_plan.TotalBooksCost, m_plan.NotKnownSkillBooksCost); }
/// <summary> /// Calculates new remapping from values of controls. /// </summary> private void Recalculate() { CharacterScratchpad scratchpad = m_remapping.BaseScratchpad.Clone(); scratchpad.Memory.Base = pbMEMRemappable.Value + EveConstants.CharacterBaseAttributePoints; scratchpad.Charisma.Base = pbCHARemappable.Value + EveConstants.CharacterBaseAttributePoints; scratchpad.Willpower.Base = pbWILRemappable.Value + EveConstants.CharacterBaseAttributePoints; scratchpad.Perception.Base = pbPERRemappable.Value + EveConstants.CharacterBaseAttributePoints; scratchpad.Intelligence.Base = pbINTRemappable.Value + EveConstants.CharacterBaseAttributePoints; // Get remapping for provided attributes RemappingResult manualRemapping = new RemappingResult(m_remapping, scratchpad); manualRemapping.Update(); UpdateControls(m_character, m_plan, manualRemapping, m_description); // Notify the changes AttributeChanged?.ThreadSafeInvoke(this, new AttributeChangedEventArgs(manualRemapping)); }
/// <summary> /// Updates the form controls to reflect the status of the Plan specified by the Plan property. /// </summary> private void UpdatePlanStatus() { // Compute the skills to add m_skillsToAdd.Clear(); var scratchpad = new CharacterScratchpad(m_character); foreach (var obj in m_objects) { scratchpad.Train(obj.Prerequisites); } m_skillsToAdd.AddRange(scratchpad.TrainedSkills); // All skills already known ? if (m_skillsToAdd.Count == 0) { AddToPlanButton.Enabled = false; PlanedLabel.Visible = true; PlanedLabel.Text = "All skills already known."; TrainTimeLabel.Visible = false; } // Are skills already planned ? else if (m_plan.AreSkillsPlanned(m_skillsToAdd)) { AddToPlanButton.Enabled = false; PlanedLabel.Visible = true; PlanedLabel.Text = "All skills already known or planned."; TrainTimeLabel.Visible = false; } // There is at least one unknown and non-planned skill else { AddToPlanButton.Enabled = true; PlanedLabel.Text = ""; PlanedLabel.Visible = false; TrainTimeLabel.Visible = true; // Compute training time TimeSpan trainingTime = m_character.GetTrainingTimeToMultipleSkills(m_skillsToAdd); TrainTimeLabel.Text = Skill.TimeSpanToDescriptiveText(trainingTime, DescriptiveTextOptions.IncludeCommas | DescriptiveTextOptions.SpaceText); } }
/// <summary> /// Will set the provided character scratchpad's base attributes as the target values to remap. /// </summary> /// <param name="newScratchpad">The scratchpad with the target base values to assign to this point</param> /// <param name="oldScratchpad">The scratchpad before we remapped</param> internal void SetBaseAttributes(CharacterScratchpad newScratchpad, CharacterScratchpad oldScratchpad) { // Update the status Status = RemappingPointStatus.UpToDate; // Initialize the string StringBuilder builder = new StringBuilder(); // Scroll through attributes for (int i = 0; i < 5; i++) { // Compute the new base attribute EveAttribute attrib = (EveAttribute)i; m_attributes[i] = newScratchpad[attrib].Base; // Update description builder.AppendLine().Append(GetStringForAttribute(attrib, oldScratchpad, newScratchpad)); } // Return the final string m_description = builder.ToString(); }
/// <summary> /// Updates controls on the form. /// </summary> /// <param name="remapping">An <see cref="RemappingResult"/> object</param> /// <param name="remappingList">List of remappings</param> private void UpdateForm(RemappingResult remapping, ICollection <RemappingResult> remappingList) { // Update the attributes if (remapping != null) { m_statisticsScratchpad = remapping.BestScratchpad.Clone(); UpdateForRemapping(remapping); } else { UpdateForRemappingList(remappingList); } // Update the plan order's column if ((remapping != null) || (remappingList.Count != 0)) { m_planEditor.ShowWithPluggable(this); } // Hide the throbber and the waiting message panelWait.Hide(); }
/// <summary> /// Updates the controls with the values from the current remapping point. /// </summary> /// <param name="point"></param> public void UpdateValuesFrom(RemappingPoint point) { // Creates a scratchpad with the base values from the provided point. var scratchpad = new CharacterScratchpad(m_character); for (int i = 0; i < 5; i++) { scratchpad[(EveAttribute)i].Base = point[(EveAttribute)i]; } var remapping = new AttributesOptimizer.RemappingResult(m_remapping, scratchpad); remapping.Update(); // Update the controls UpdateControls(m_character, remapping); // Fires the event if (AttributeChanged != null) { AttributeChanged(this, remapping); } }
/// <summary> /// On load, fill the list and update the labels. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void SuggestionWindow_Load(object sender, EventArgs e) { if (this.DesignMode) { return; } // Update the suggestions list lbSkills.Items.Clear(); foreach (var entry in m_suggestions) { lbSkills.Items.Add(entry.ToString()); } // Update the times labels CharacterScratchpad character = m_plan.Character.After(m_plan.ChosenImplantSet); TimeSpan preTime = m_plan.GetTotalTime(character, true); TimeSpan postTime = preTime.Subtract(m_suggestions.TimeBenefit); lblBeforeTime.Text = preTime.ToDescriptiveText(DescriptiveTextOptions.IncludeCommas); lblAfterTime.Text = postTime.ToDescriptiveText(DescriptiveTextOptions.IncludeCommas); lblDiffTime.Text = m_suggestions.TimeBenefit.ToDescriptiveText(DescriptiveTextOptions.IncludeCommas); }
/// <summary> /// Updates the statistics of the entries in the same way the given character would train this plan. /// </summary> /// <param name="scratchpad">The scratchpad.</param> /// <param name="applyRemappingPoints">if set to <c>true</c> [apply remapping points].</param> /// <param name="trainSkills">When true, the character will train every skill, increasing SP, etc.</param> /// <exception cref="System.ArgumentNullException">scratchpad</exception> public void UpdateOldTrainingTimes(CharacterScratchpad scratchpad, bool applyRemappingPoints, bool trainSkills) { scratchpad.ThrowIfNull(nameof(scratchpad)); // Update the statistics foreach (PlanEntry entry in Items) { // Apply the remapping if (applyRemappingPoints && entry.Remapping != null && entry.Remapping.Status == RemappingPointStatus.UpToDate) { scratchpad.Remap(entry.Remapping); } // Update entry's statistics entry.UpdateOldTrainingTime(scratchpad); // Update the scratchpad if (trainSkills) { scratchpad.Train(entry.Skill, entry.Level); } } }
/// <summary> /// Completes the items initialization or refresh them. Especially their columns values. /// </summary> private void UpdateListViewItems() { // When there is a pluggable (implants calculator or attributes optimizer) // This one provides us the scratchpad to update training times. m_areRemappingPointsActive = true; if (m_pluggable != null) { m_pluggable.UpdateStatistics(m_displayPlan, out m_areRemappingPointsActive); } else { var scratchpad = new CharacterScratchpad(m_character); if (m_plan.ChosenImplantSet != null) scratchpad = scratchpad.After(m_plan.ChosenImplantSet); m_displayPlan.UpdateStatistics(scratchpad, true, true); } // Start updating the list lvSkills.BeginUpdate(); try { NumberFormatInfo nfi = NumberFormatInfo.CurrentInfo; // Scroll through entries for (int i = 0; i < lvSkills.Items.Count; i++) { ListViewItem lvi = lvSkills.Items[i]; var entry = lvi.Tag as PlanEntry; // Add enough subitems to match the number of columns while (lvi.SubItems.Count < lvSkills.Columns.Count) { lvi.SubItems.Add(String.Empty); } if (entry != null) { // Checks if this entry has not prereq-met if (!entry.CharacterSkill.IsKnown) lvi.ForeColor = Color.Red; // Checks if this entry is a non-public skill if (!entry.CharacterSkill.IsPublic) lvi.ForeColor = Color.DarkRed; // Checks if this entry is not known but has prereq-met if (!entry.CharacterSkill.IsKnown && entry.CharacterSkill.IsPublic && entry.CharacterSkill.ArePrerequisitesMet) lvi.ForeColor = Color.LightSlateGray; // Checks if this entry is partially trained bool level = (entry.Level == entry.CharacterSkill.Level + 1); if (Settings.UI.PlanWindow.HighlightPartialSkills) { bool partiallyTrained = (entry.CharacterSkill.FractionCompleted > 0 && entry.CharacterSkill.FractionCompleted < 1); if (level && partiallyTrained) lvi.ForeColor = Color.Green; } HighlightQueuedSkills(lvi, entry); // Checks if this entry is currently training (even if it's paused) if (entry.CharacterSkill.IsTraining && level) { lvi.BackColor = Color.LightSteelBlue; lvi.ForeColor = Color.Black; } // Checks whether this entry will be blocked string blockingEntry = String.Empty; if (Settings.UI.PlanWindow.HighlightConflicts) { bool isBlocked = Scheduler.SkillIsBlockedAt(entry.EndTime, out blockingEntry); if (isBlocked) { lvi.ForeColor = Color.Red; lvi.BackColor = Color.LightGray; } } // Update every column lvi.UseItemStyleForSubItems = (m_pluggable == null); for (int columnIndex = 0; columnIndex < lvSkills.Columns.Count; columnIndex++) { // Regular columns (not pluggable-dependent) if (lvSkills.Columns[columnIndex].Tag != null) { var columnSettings = (PlanColumnSettings)lvSkills.Columns[columnIndex].Tag; lvi.SubItems[columnIndex].BackColor = lvi.BackColor; lvi.SubItems[columnIndex].ForeColor = lvi.ForeColor; lvi.SubItems[columnIndex].Text = GetColumnTextForItem(entry, columnSettings.Column, blockingEntry, nfi); } // Training time differences else { TimeSpan timeDifference; string result = String.Empty; if (entry.OldTrainingTime < entry.TrainingTime) { result = "+"; timeDifference = entry.TrainingTime - entry.OldTrainingTime; lvi.SubItems[columnIndex].BackColor = lvi.BackColor; lvi.SubItems[columnIndex].ForeColor = Color.DarkRed; } else if (entry.OldTrainingTime > entry.TrainingTime) { result = "-"; timeDifference = entry.OldTrainingTime - entry.TrainingTime; lvi.SubItems[columnIndex].BackColor = lvi.BackColor; lvi.SubItems[columnIndex].ForeColor = Color.DarkGreen; } else { timeDifference = TimeSpan.Zero; lvi.SubItems[columnIndex].BackColor = lvi.BackColor; lvi.SubItems[columnIndex].ForeColor = lvi.ForeColor; } result += timeDifference.ToDescriptiveText(DescriptiveTextOptions.IncludeCommas); lvi.SubItems[columnIndex].Text = result; } } } // The item represents a remapping point else { var point = (RemappingPoint)lvi.Tag; for (int columnIndex = 0; columnIndex < lvSkills.Columns.Count; columnIndex++) { var columnSettings = (PlanColumnSettings)lvSkills.Columns[columnIndex].Tag; lvi.SubItems[columnIndex].Text = String.Empty; lvi.SubItems[columnIndex].BackColor = m_remappingBackColor; lvi.SubItems[columnIndex].ForeColor = m_remappingForeColor; // We display the text in the SkillName column for better visibility if (columnSettings != null && columnSettings.Column == PlanColumn.SkillName) lvi.SubItems[columnIndex].Text = (m_areRemappingPointsActive ? point.ToString() : "Remapping (ignored)"); } } } } finally { lvSkills.EndUpdate(); UpdateStatusBar(); } }
/// <summary> /// Gets a string representation of the attribute. /// </summary> /// <param name="attrib">The attribute.</param> /// <param name="oldScratchpad">The old scratchpad.</param> /// <param name="newScratchpad">The new scratchpad.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException"> /// oldScratchpad /// or /// newScratchpad /// </exception> public static string GetStringForAttribute(EveAttribute attrib, CharacterScratchpad oldScratchpad, CharacterScratchpad newScratchpad) { oldScratchpad.ThrowIfNull(nameof(oldScratchpad)); newScratchpad.ThrowIfNull(nameof(newScratchpad)); Int64 bonusDifference = newScratchpad[attrib].Base - oldScratchpad[attrib].Base; if (bonusDifference == 0) return newScratchpad[attrib].ToString("%N (0) = %e = (%B + %r + %i)"); return newScratchpad[attrib].ToString(bonusDifference > 0 ? $"%N (+{bonusDifference}) = %e = (%B + %r + %i)" : $"%N ({bonusDifference}) = %e = (%B + %r + %i)"); }
/// <summary> /// Will set the provided character scratchpad's base attributes as the target values to remap. /// </summary> /// <param name="newScratchpad">The scratchpad with the target base values to assign to this point</param> /// <param name="oldScratchpad">The scratchpad before we remapped</param> internal void SetBaseAttributes(CharacterScratchpad newScratchpad, CharacterScratchpad oldScratchpad) { // Update the status this.m_status = PointStatus.UpToDate; // Initialize the string StringBuilder builder = new StringBuilder(); // Scroll through attributes for (int i = 0; i < 5; i++) { // Compute the new base attribute EveAttribute attrib = (EveAttribute)i; m_attributes[i] = newScratchpad[attrib].Base; // Update description builder.AppendLine().Append(GetStringForAttribute(attrib, oldScratchpad, newScratchpad)); } // Return the final string this.m_description = builder.ToString(); }
/// <summary> /// Checks for pluggable. /// </summary> private void CheckForPluggable() { // When there is a pluggable (implants calculator or attributes optimizer) // This one provides us the scratchpad to update training times m_areRemappingPointsActive = true; if (m_pluggable != null) { bool areRemappingPointsActive; m_pluggable.UpdateStatistics(DisplayPlan, out areRemappingPointsActive); m_areRemappingPointsActive = areRemappingPointsActive; } else { CharacterScratchpad scratchpad = new CharacterScratchpad(m_character); if (m_plan.ChosenImplantSet != null) scratchpad = scratchpad.After(m_plan.ChosenImplantSet); DisplayPlan.UpdateStatistics(scratchpad, true, true); } }
/// <summary> /// Sends a mail alert for a skill completion /// </summary> /// <param name="queueList">Current Skill Queue</param> /// <param name="skill">Skill that has just completed</param> /// <param name="character">Character affected</param> /// <exception cref="System.ArgumentNullException"> /// </exception> public static void SendSkillCompletionMail(IList<QueuedSkill> queueList, QueuedSkill skill, Character character) { s_isTestMail = false; queueList.ThrowIfNull(nameof(queueList)); skill.ThrowIfNull(nameof(skill)); CCPCharacter ccpCharacter = character as CCPCharacter; // Current character isn't a CCP character, so can't have a Queue. if (ccpCharacter == null) return; string skillLevelText = $"{skill.SkillName} {Skill.GetRomanFromInt(skill.Level)}"; string subjectText = $"{character.Name} has finished training {skillLevelText}."; // Message's first line StringBuilder body = new StringBuilder(); body .AppendLine(subjectText) .AppendLine(); // Next skills in queue if (queueList[0] != null) { string plural = queueList.Count > 1 ? "s" : String.Empty; body.AppendLine($"Next skill{plural} in queue:"); foreach (QueuedSkill qskill in queueList) { body.AppendLine($"- {qskill}"); } body.AppendLine(); } else body .AppendLine("Character is not training.") .AppendLine(); // Skill queue less than a day if (ccpCharacter.SkillQueue.LessThanWarningThreshold) { TimeSpan skillQueueEndTime = ccpCharacter.SkillQueue.EndTime.Subtract(DateTime.UtcNow); TimeSpan timeLeft = SkillQueue.WarningThresholdTimeSpan.Subtract(skillQueueEndTime); // Skill queue empty? if (timeLeft > SkillQueue.WarningThresholdTimeSpan) body.AppendLine("Skill queue is empty."); else { string timeLeftText = skillQueueEndTime < TimeSpan.FromMinutes(1) ? skillQueueEndTime.ToDescriptiveText(DescriptiveTextOptions.IncludeCommas) : skillQueueEndTime.ToDescriptiveText(DescriptiveTextOptions.IncludeCommas, false); body.AppendLine($"Queue ends in {timeLeftText}."); } } // Short format (also for SMS) if (Settings.Notifications.UseEmailShortFormat) { SendMail(Settings.Notifications, $"[STC] {character.Name} :: {skillLevelText}", body.ToString()); return; } // Long format if (character.Plans.Count > 0) { body.AppendLine("Next skills listed in plans:") .AppendLine(); } foreach (Plan plan in character.Plans) { if (plan.Count <= 0) continue; // Print plan name CharacterScratchpad scratchpad = new CharacterScratchpad(character); body.AppendLine($"{plan.Name}:"); // Scroll through entries int i = 0; int minDays = 1; foreach (PlanEntry entry in plan) { TimeSpan trainTime = scratchpad.GetTrainingTime(entry.Skill, entry.Level, TrainingOrigin.FromPreviousLevelOrCurrent); // Only print the first three skills, and the very long skills // (first limit is one day, then we add skills duration) if (++i > 3 && trainTime.Days <= minDays) continue; if (i > 3) { // Print long message once if (minDays == 1) body.AppendLine().Append($"Longer skills from {plan.Name}:").AppendLine(); minDays = trainTime.Days + minDays; } body.Append($"\t{entry}"); // Notes if (!string.IsNullOrEmpty(entry.Notes)) body.Append($" ({entry.Notes})"); // Training time body .Append(trainTime.Days > 0 ? $" - {trainTime.Days}d, {trainTime}" : $" - {trainTime}") .AppendLine(); } body.AppendLine(); } SendMail(Settings.Notifications, subjectText, body.ToString()); }
/// <summary> /// Updates the controls with the values from the current remapping point. /// </summary> /// <param name="point"></param> public void UpdateValuesFrom(RemappingPoint point) { // Creates a scratchpad with the base values from the provided point. var scratchpad = new CharacterScratchpad(m_character.After(m_plan.ChosenImplantSet)); for (int i = 0; i < 5; i++) { scratchpad[(EveAttribute)i].Base = point[(EveAttribute)i]; } var remapping = new AttributesOptimizer.RemappingResult(m_remapping, scratchpad); remapping.Update(); // Update the controls UpdateControls(m_character, m_plan, remapping, m_description); // Fires the event if (AttributeChanged != null) AttributeChanged(this, remapping); }
/// <summary> /// Exports the character's selected skills as plan. /// </summary> /// <param name="character">The character.</param> /// <param name="selectedSkills">The selected skills.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException"></exception> public static async Task ExportCharacterSkillsAsPlanAsync(Character character, IEnumerable<Skill> selectedSkills = null) { character.ThrowIfNull(nameof(character)); // Create a character without any skill CharacterScratchpad scratchpad = new CharacterScratchpad(character); scratchpad.ClearSkills(); // Create a new plan Plan plan = new Plan(scratchpad) { Name = "Skills Plan" }; IEnumerable<Skill> skills = selectedSkills ?? character.Skills.Where(skill => skill.IsPublic); // Add all trained skill levels that the character has trained so far foreach (Skill skill in skills) { plan.PlanTo(skill, skill.Level); } await ExportPlanAsync(plan, character); }
/// <summary> /// Updates controls on the form. /// </summary> /// <param name="remapping">An <see cref="RemappingResult"/> object</param> /// <param name="remappingList">List of remappings</param> private void UpdateForm(RemappingResult remapping, ICollection<RemappingResult> remappingList) { // Update the attributes if (remapping != null) { m_statisticsScratchpad = remapping.BestScratchpad.Clone(); UpdateForRemapping(remapping); } else UpdateForRemappingList(remappingList); // Update the plan order's column if ((remapping != null) || (remappingList.Count != 0)) m_planEditor.ShowWithPluggable(this); // Hide the throbber and the waiting message panelWait.Hide(); }
/// <summary> /// Updates the pie chart display /// </summary> private void UpdatePieChart() { // Prevents updating before OnLoad() completed. if (planSelector.SelectedIndex < 0) return; int groupCount = StaticSkills.AllGroups.Count(); CharacterScratchpad scratchpad = new CharacterScratchpad(m_character); // Retrieve the selected Plan Plan plan = null; if (planSelector.SelectedIndex > 0) { plan = m_character.Plans[planSelector.SelectedIndex - 1]; // Updates the scratchpad foreach (var entry in plan) scratchpad.Train(entry); } // Get group to index map and groups list List<SkillGroup> groups = new List<SkillGroup>(); Dictionary<StaticSkillGroup, int> indices = new Dictionary<StaticSkillGroup, int>(); foreach (var group in StaticSkills.AllGroups) { indices[group] = groups.Count; groups.Add(m_character.SkillGroups[group.Name]); } // Get start SP, before plan decimal[] srcSkillPoints = new decimal[groupCount]; foreach(var skillGroup in groups) { int groupIndex = indices[skillGroup.StaticData]; srcSkillPoints[groupIndex] = skillGroup.TotalSP; } // Get target SP and skills count, after plan int[] skillCounts = new int[groupCount]; decimal[] targetSkillPoints = new decimal[groupCount]; foreach (StaticSkill skill in StaticSkills.AllSkills) { int sp = scratchpad.GetSkillPoints(skill); int groupIndex = indices[skill.Group]; targetSkillPoints[groupIndex] += sp; if (sp != 0) skillCounts[groupIndex]++; } // Get groups names and descriptions string[] names = new string[groupCount]; string[] descriptions = new string[groupCount]; for(int i=0; i<srcSkillPoints.Length; i++) { names[i] = groups[i].Name; descriptions[i] = groups[i].Name; decimal srcSP = srcSkillPoints[i]; decimal destSP = targetSkillPoints[i]; StringBuilder description = new StringBuilder(); description.AppendFormat(CultureConstants.DefaultCulture, "{0} ({1} skills, {2:#,###} skillpoints", names[i], skillCounts[i], srcSP); if (srcSP != destSP) { description.AppendFormat(CultureConstants.DefaultCulture, " / {0:#,###} after plan completion", destSP); } description.Append(")"); descriptions[i] = description.ToString(); } // Merge minor groups if (mergeMinorCheck.Checked) { Merge(ref targetSkillPoints, ref names, ref descriptions); } // Compute the slices displacements int tinyGroups = 0; float[] slicesDiscplacements = new float[targetSkillPoints.Length]; for(int i=0; i < targetSkillPoints.Length; i++) { slicesDiscplacements[i] = (targetSkillPoints[i] < 100000) ? 0.06F + (0.008F * ++tinyGroups) : 0.05F; } // Assign and sort skillPieChartControl.Values = targetSkillPoints; skillPieChartControl.Texts = names; skillPieChartControl.ToolTips = descriptions; skillPieChartControl.SliceRelativeDisplacements = slicesDiscplacements; skillPieChartControl.OrderSlices(sortBySizeCheck.Checked); }
/// <summary> /// Updates the form controls to reflect the status of the Plan specified by the Plan property. /// </summary> private void UpdatePlanStatus() { // Compute the skills to add m_skillsToAdd.Clear(); CharacterScratchpad scratchpad = new CharacterScratchpad(m_character); Character character = m_character as Character; foreach (var obj in m_objects) { scratchpad.Train(obj.Prerequisites.Where(x => character.Skills[x.Skill].Level < x.Level)); } m_skillsToAdd.AddRange(scratchpad.TrainedSkills); // All skills already trained ? if (m_skillsToAdd.Count == 0) { AddToPlanButton.Enabled = false; PlanedLabel.Visible = true; PlanedLabel.Text = "All skills already trained."; TrainTimeLabel.Visible = false; } // Are skills already planned ? else if (m_plan.AreSkillsPlanned(m_skillsToAdd)) { AddToPlanButton.Enabled = false; PlanedLabel.Visible = true; PlanedLabel.Text = "All skills already trained or planned."; TrainTimeLabel.Visible = false; } // There is at least one untrained or non-planned skill else { AddToPlanButton.Enabled = true; PlanedLabel.Text = String.Empty; PlanedLabel.Visible = false; TrainTimeLabel.Visible = true; // Compute training time TimeSpan trainingTime = m_character.GetTrainingTimeToMultipleSkills(m_skillsToAdd); TrainTimeLabel.Text = trainingTime.ToDescriptiveText( DescriptiveTextOptions.IncludeCommas | DescriptiveTextOptions.SpaceText); } }
/// <summary> /// Update the plan status : training time, skills already trained, etc /// </summary> private void UpdatePlanningControls() { btnPlan.Visible = m_plan != null; lblTrainTime.Visible = TrainingTimeLabel.Visible = m_character != null; if (m_character == null) return; btnPlan.Enabled = false; if (!m_prerequisites.Any()) return; // Are all the prerequisites trained ? if (m_prerequisites.All(x => m_character.GetSkillLevel(x.Skill) >= x.Level)) { lblPlanned.Show(); lblPlanned.Text = @"All skills already trained."; lblTrainTime.Hide(); return; } if (m_plan == null) { lblPlanned.Show(); lblPlanned.Text = @"Some skills need training."; lblTrainTime.Hide(); return; } // Are all the prerequisites planned ? if (m_plan.AreSkillsPlanned(m_prerequisites.Where(x => m_character.Skills[x.Skill.ID].Level < x.Level))) { lblPlanned.Show(); lblPlanned.Text = @"All skills already trained or planned."; lblTrainTime.Hide(); return; } // Compute the training time CharacterScratchpad scratchpad = new CharacterScratchpad(m_character); foreach (PlanEntry entry in m_plan) { scratchpad.Train(entry); } TimeSpan startTime = scratchpad.TrainingTime; foreach (StaticSkillLevel prereq in m_prerequisites) { scratchpad.Train(prereq); } TimeSpan trainingTime = scratchpad.TrainingTime.Subtract(startTime); // Update the labels lblTrainTime.Text = trainingTime.ToDescriptiveText( DescriptiveTextOptions.IncludeCommas | DescriptiveTextOptions.SpaceText); lblTrainTime.Show(); lblPlanned.Text = String.Empty; lblPlanned.Hide(); btnPlan.Enabled = true; }
/// <summary> /// Updates the pie chart display /// </summary> private void UpdatePieChart() { // Prevents updating before OnLoad() completed. if (planSelector.SelectedIndex < 0) { return; } int groupCount = StaticSkills.AllGroups.Count(); CharacterScratchpad scratchpad = new CharacterScratchpad(m_character); // Retrieve the selected Plan if (planSelector.SelectedIndex > 0) { Plan plan = m_character.Plans[planSelector.SelectedIndex - 1]; // Updates the scratchpad foreach (PlanEntry entry in plan) { scratchpad.Train(entry); } } // Get group to index map and groups list List <SkillGroup> groups = new List <SkillGroup>(); Dictionary <StaticSkillGroup, int> indices = new Dictionary <StaticSkillGroup, int>(); foreach (StaticSkillGroup group in StaticSkills.AllGroups) { indices[group] = groups.Count; groups.Add(m_character.SkillGroups[group.ID]); } // Get start SP, before plan decimal[] srcSkillPoints = new decimal[groupCount]; foreach (SkillGroup skillGroup in groups) { int groupIndex = indices[skillGroup.StaticData]; srcSkillPoints[groupIndex] = skillGroup.TotalSP; } // Get target SP and skills count, after plan int[] skillCounts = new int[groupCount]; decimal[] targetSkillPoints = new decimal[groupCount]; foreach (StaticSkill skill in StaticSkills.AllSkills) { Int64 sp = scratchpad.GetSkillPoints(skill); int groupIndex = indices[skill.Group]; targetSkillPoints[groupIndex] += sp; if (sp != 0) { skillCounts[groupIndex]++; } } // Get groups names and descriptions string[] names = new string[groupCount]; string[] descriptions = new string[groupCount]; for (int i = 0; i < srcSkillPoints.Length; i++) { names[i] = groups[i].Name; descriptions[i] = groups[i].Name; decimal srcSP = srcSkillPoints[i]; decimal destSP = targetSkillPoints[i]; StringBuilder description = new StringBuilder(); description.Append($"{names[i]} ({skillCounts[i]} skills, {srcSP:N0} skillpoints"); if (srcSP != destSP) { description.Append($" / {destSP:N0} after plan completion"); } description.Append(")"); descriptions[i] = description.ToString(); } // Merge minor groups if (mergeMinorCheck.Checked) { Merge(ref targetSkillPoints, ref names, ref descriptions); } // Compute the slices displacements int tinyGroups = 0; float[] slicesDiscplacements = new float[targetSkillPoints.Length]; for (int i = 0; i < targetSkillPoints.Length; i++) { slicesDiscplacements[i] = targetSkillPoints[i] < 100000 ? 0.06F + 0.008F * ++tinyGroups : 0.05F; } // Assign and sort skillPieChartControl.Values(targetSkillPoints); skillPieChartControl.Texts(names); skillPieChartControl.ToolTips(descriptions); skillPieChartControl.SliceRelativeDisplacements(slicesDiscplacements); skillPieChartControl.OrderSlices(sortBySizeCheck.Checked); }
/// <summary> /// Sends a mail alert for a skill completion /// </summary> /// <param name="queueList">Current Skill Queue</param> /// <param name="skill">Skill that has just completed</param> /// <param name="character">Character affected</param> /// <returns></returns> public static bool SendSkillCompletionMail(IList<QueuedSkill> queueList, QueuedSkill skill, Character character) { CCPCharacter ccpCharacter = character as CCPCharacter; // Current character isn't a CCP character, so can't have a Queue. if (ccpCharacter == null) return false; string charName = character.Name; string skillName = skill.SkillName; string skillLevelString = Skill.GetRomanForInt(skill.Level); var skillQueueEndTime = ccpCharacter.SkillQueue.EndTime; bool freeTime = skillQueueEndTime < DateTime.UtcNow.AddHours(24); TimeSpan timeLeft = DateTime.UtcNow.AddHours(24).Subtract(skillQueueEndTime); string timeLeftText = timeLeft.ToDescriptiveText(DescriptiveTextOptions.IncludeCommas, false); // Message's first line StringBuilder body = new StringBuilder(); body.AppendFormat(CultureConstants.DefaultCulture, "{0} has finished training {1} {2}.{3}{3}", charName, skillName, skillLevelString, Environment.NewLine); // Next skills in queue if (queueList[0] != null) { body.AppendFormat(CultureConstants.DefaultCulture, "Next skill{0} in queue:{1}", (queueList.Count > 1 ? "s" : String.Empty), Environment.NewLine); foreach (var qskill in queueList) { body.AppendFormat(CultureConstants.DefaultCulture, "- {0}{1}", qskill, Environment.NewLine); } body.AppendLine(); } else { body.AppendFormat(CultureConstants.DefaultCulture, "Character is not training.{0}{0}", Environment.NewLine); } // Free room in skill queue if (freeTime) body.AppendFormat(CultureConstants.DefaultCulture, "There is also {0} free room in skill queue.{1}", timeLeftText, Environment.NewLine); // Short format (also for SMS) if (Settings.Notifications.UseEmailShortFormat) { return SendMail(Settings.Notifications, String.Format(CultureConstants.DefaultCulture, "[STC] {0} :: {1} {2}", charName, skillName, skillLevelString), body.ToString()); } // Long format if (character.Plans.Count > 0) body.AppendFormat(CultureConstants.DefaultCulture, "Next skills listed in plans:{0}{0}", Environment.NewLine); foreach (var plan in character.Plans) { if (plan.Count > 0) { // Print plan name CharacterScratchpad scratchpad = new CharacterScratchpad(character); body.AppendFormat(CultureConstants.DefaultCulture, "{0}:{1}", plan.Name, Environment.NewLine); // Scroll through entries int i = 0; int minDays = 1; foreach (PlanEntry entry in plan) { TimeSpan trainTime = scratchpad.GetTrainingTime(entry.Skill, entry.Level, TrainingOrigin.FromPreviousLevelOrCurrent); // Only print the first three skills, and the very long skills // (first limit is one day, then we add skills duration) if (++i <= 3 || trainTime.Days > minDays) { if (i > 3) { // Print long message once if (minDays == 1) { body.AppendFormat(CultureConstants.DefaultCulture, "{1}Longer skills from {0}:{1}", plan.Name, Environment.NewLine); } minDays = trainTime.Days + minDays; } body.AppendFormat(CultureConstants.DefaultCulture, "\t{0}", entry); // Notes if (entry.Notes != null && entry.Notes.Length > 0) { body.AppendFormat(CultureConstants.DefaultCulture, " ({0})", entry.Notes); } // Training time if (trainTime.Days > 0) { body.AppendFormat(CultureConstants.DefaultCulture, " - {0}d, {1}", trainTime.Days, trainTime); } else { body.AppendFormat(CultureConstants.DefaultCulture, " - {0}", trainTime); } body.AppendLine(); } } body.AppendLine(); } } string subject = String.Format(CultureConstants.DefaultCulture, "{0} has finished training {1} {2}", charName, skillName, skillLevelString); return SendMail(Settings.Notifications, subject, body.ToString()); }
/// <summary> /// Updates controls on the form. /// </summary> /// <param name="remapping">An <see cref="AttributesOptimizer.Remapping"/> object</param> /// <param name="remappingList">List of remappings</param> private void UpdateForm(AttributesOptimizer.RemappingResult remapping, List<AttributesOptimizer.RemappingResult> remappingList) { // If the thread has been canceled, we stop right now to prevent an exception if (m_thread == null) return; // Hide the throbber and the waiting message this.throbber.State = ThrobberState.Stopped; this.panelWait.Visible = false; this.tabControl.Controls.Clear(); // Update the attributes if (remapping != null) { m_statisticsScratchpad = remapping.BestScratchpad.Clone(); UpdateForRemapping(remapping); } else { UpdateForRemappingList(remappingList); } // Update the plan order's column if (m_planEditor != null && (remapping != null || remappingList.Count != 0)) this.m_planEditor.ShowWithPluggable(this); }
/// <summary> /// Racalculating plan and summary page after change of a <see cref="AttributesOptimizationControl"/>. /// </summary> /// <param name="control"></param> /// <param name="remapping"></param> void AttributesOptimizationControl_AttributeChanged(AttributesOptimizationControl control, AttributesOptimizer.RemappingResult remapping) { // Update the plan order's column if (m_planEditor != null) { if (m_strategy == Strategy.RemappingPoints) { m_remappingDictionary[control] = remapping; UpdateSummaryInformation(m_remappingDictionary.Values); } m_statisticsScratchpad = remapping.BestScratchpad.Clone(); this.m_planEditor.ShowWithPluggable(this); m_remapping = remapping; } }
/// <summary> /// Creates a scratchpad with the new implants values. /// </summary> /// <returns></returns> private CharacterScratchpad CreateModifiedScratchpad() { // Creates a scratchpad with new implants m_characterScratchpad = m_character.After(m_set); CharacterScratchpad scratchpad = new CharacterScratchpad(m_characterScratchpad); scratchpad.Memory.ImplantBonus += (int)nudMemory.Value - m_characterScratchpad.Memory.EffectiveValue; scratchpad.Charisma.ImplantBonus += (int)nudCharisma.Value - m_characterScratchpad.Charisma.EffectiveValue; scratchpad.Intelligence.ImplantBonus += (int)nudIntelligence.Value - m_characterScratchpad.Intelligence.EffectiveValue; scratchpad.Perception.ImplantBonus += (int)nudPerception.Value - m_characterScratchpad.Perception.EffectiveValue; scratchpad.Willpower.ImplantBonus += (int)nudWillpower.Value - m_characterScratchpad.Willpower.EffectiveValue; return scratchpad; }
/// <summary> /// Gets a string representation of the attribute /// </summary> /// <param name="attrib"></param> /// <param name="oldScratchpad"></param> /// <param name="newScratchpad"></param> /// <returns></returns> public static string GetStringForAttribute(EveAttribute attrib, CharacterScratchpad oldScratchpad, CharacterScratchpad newScratchpad) { int bonusDifference = newScratchpad[attrib].Base - oldScratchpad[attrib].Base; if (bonusDifference == 0) { return newScratchpad[attrib].ToString("%N (0) = %e = (%B + %r + %i)"); } else if (bonusDifference > 0) { return newScratchpad[attrib].ToString(String.Format("%N (+{0}) = %e = (%B + %r + %i)", bonusDifference)); } else { return newScratchpad[attrib].ToString(String.Format("%N ({0}) = %e = (%B + %r + %i)", bonusDifference)); } }