/// <summary> /// Imports data from the given skills information. /// </summary> /// <param name="skills">The serialized character skill information</param> internal void Import(EsiAPISkills skills, EsiAPISkillQueue queue) { var newSkills = new LinkedList <SerializableCharacterSkill>(); DateTime uselessDate = DateTime.UtcNow; FreeSkillPoints = skills.UnallocatedSP; // Keep track of the current skill queue's completed skills, as ESI does not // transfer them to the skills list until you login var dict = new Dictionary <long, SerializableQueuedSkill>(); if (queue != null) { foreach (var queuedSkill in queue.CreateSkillQueue()) { // If the skill is completed or currently training, we need it later to // copy the progress over to the imported skills if (queuedSkill.IsCompleted || queuedSkill.IsTraining) { if (!dict.ContainsKey(queuedSkill.ID)) { dict.Add(queuedSkill.ID, queuedSkill); } else { dict[queuedSkill.ID] = queuedSkill; } } } } // Convert skills to EVE format foreach (var skill in skills.Skills) { // Check if the skill is in the queue, and completed at a higher level or has // higher current SP if (dict.ContainsKey(skill.ID)) { var queuedSkill = dict[skill.ID]; if (queuedSkill.IsCompleted) { // Queued skill is completed, so make sure the imported skill is // updated skill.ActiveLevel = Math.Max(skill.ActiveLevel, queuedSkill.Level); skill.Level = Math.Max(skill.Level, queuedSkill.Level); skill.Skillpoints = Math.Max(skill.Skillpoints, queuedSkill.EndSP); } else if (queuedSkill.IsTraining) { // Queued skill is currently training - use QueuedSkill class to // calculate the CurrentSP of the skill var tempSkill = new QueuedSkill(this, queuedSkill, ref uselessDate); skill.Skillpoints = Math.Max(skill.Skillpoints, tempSkill.CurrentSP); } } newSkills.AddLast(skill.ToXMLItem()); } Skills.Import(newSkills, true); UpdateMasteries(); }
/// <summary> /// Imports data from the given skills information. /// </summary> /// <param name="skills">The serialized character skill information</param> internal void Import(EsiAPISkills skills, EsiAPISkillQueue queue) { var newSkills = new LinkedList <SerializableCharacterSkill>(); FreeSkillPoints = skills.UnallocatedSP; // Keep track of the current skill queue's completed skills, as ESI doesn't transfer them to the skills list until you login Dictionary <long, QueuedSkill> dict = new Dictionary <long, QueuedSkill>(); if (queue != null && IsTraining) { DateTime uselessDate = DateTime.UtcNow; foreach (var serialSkill in queue.ToXMLItem().Queue) { var queuedSkill = new QueuedSkill(this, serialSkill, ref uselessDate); if (!dict.ContainsKey(queuedSkill.Skill.ID)) { dict.Add(queuedSkill.Skill.ID, queuedSkill); } else if (queuedSkill.Level > dict[queuedSkill.Skill.ID].Level) { dict[queuedSkill.Skill.ID] = queuedSkill; } } } // Convert skills to EVE format foreach (var skill in skills.Skills) { // Check if the skill is in the queue, and completed at a higher level or has higher current SP if (dict.ContainsKey(skill.ID)) { var queuedSkill = dict[skill.ID]; if (queuedSkill.IsCompleted) { skill.ActiveLevel = Math.Max(skill.ActiveLevel, queuedSkill.Level); skill.Level = Math.Max(skill.Level, queuedSkill.Level); skill.Skillpoints = Math.Max(skill.Skillpoints, queuedSkill.EndSP); } else { skill.Skillpoints = Math.Max(skill.Skillpoints, queuedSkill.CurrentSP); } } newSkills.AddLast(skill.ToXMLItem()); } Skills.Import(newSkills, true); }
/// <summary> /// Updates the skill to match the one from the queue /// </summary> internal void UpdateSkillProgress(QueuedSkill queuedSkill) { m_known = true; if (queuedSkill.IsCompleted) { m_level = Math.Min(queuedSkill.Level, s_maxLevel); SkillPoints = queuedSkill.EndSP; } else { m_level = queuedSkill.Level - 1; SkillPoints = queuedSkill.CurrentSP; } }
/// <summary> /// Process the queue into MS Outlook. /// </summary> /// <param name="queuedSkill">The queued skill.</param> /// <param name="queuePosition">The queue position.</param> /// <param name="lastSkillInQueue">if set to <c>true</c> skill is the last in queue.</param> private static async Task DoOutlookAppointmentAsync(QueuedSkill queuedSkill, int queuePosition, bool lastSkillInQueue) { // Get the calendar if (!OutlookCalendarEvent.OutlookCalendarExist(Settings.Calendar.UseOutlookDefaultCalendar)) { MessageBox.Show(@"Outlook calendar does not exist. Please check your settings.", @"Outlook Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // Set the subject to the character name and the skill and level in queue for uniqueness sake OutlookCalendarEvent outlookAppointmentFilter = new OutlookCalendarEvent { StartDate = DateTime.Now.AddDays(-40), EndDate = DateTime.Now.AddDays(100), Subject = String.Format( CultureConstants.DefaultCulture, "{0} - {1} {2}", queuedSkill.Owner.Name, queuedSkill.SkillName, Skill.GetRomanFromInt(queuedSkill.Level)) }; // Pull the list of appointments, hopefully we should either get 1 or none back await outlookAppointmentFilter.ReadEventsAsync(); // If there is an appointment, get the first one bool foundAppointment = false; if (outlookAppointmentFilter.ItemCount > 0) foundAppointment = outlookAppointmentFilter.GetEvent(); // Update the appointment we may have pulled or the new one // Set the appointment length to 5 minutes, starting at the estimated completion date and time // Reminder value was already validated // Use the values from the screen as these may differ what the user has set for defaults outlookAppointmentFilter.StartDate = queuedSkill.EndTime.ToLocalTime(); outlookAppointmentFilter.EndDate = queuedSkill.EndTime.ToLocalTime().AddMinutes(5); outlookAppointmentFilter.ItemReminder = Settings.Calendar.UseReminding; outlookAppointmentFilter.AlternateReminder = Settings.Calendar.UseAlternateReminding; outlookAppointmentFilter.EarlyReminder = Settings.Calendar.EarlyReminding; outlookAppointmentFilter.LateReminder = Settings.Calendar.LateReminding; outlookAppointmentFilter.Minutes = Settings.Calendar.RemindingInterval; await outlookAppointmentFilter.AddOrUpdateEventAsync(foundAppointment, queuePosition, lastSkillInQueue); }
/// <summary> /// Process the queue into Google calendar. /// </summary> /// <param name="queuedSkill">The queued skill.</param> /// <param name="queuePosition">The queue position.</param> /// <param name="lastSkillInQueue">if set to <c>true</c> skill is the last in queue.</param> private static async Task DoGoogleAppointmentAsync(QueuedSkill queuedSkill, int queuePosition, bool lastSkillInQueue) { try { // Set the subject to the character name and the skill and level in queue for uniqueness sakes GoogleCalendarEvent googleAppointmentFilter = new GoogleCalendarEvent { StartDate = DateTime.Now.AddDays(-40), EndDate = DateTime.Now.AddDays(100), Subject = String.Format( CultureConstants.DefaultCulture, "{0} - {1} {2}", queuedSkill.Owner.Name, queuedSkill.SkillName, Skill.GetRomanFromInt(queuedSkill.Level)) }; // Pull the list of appointments, hopefully we should either get 1 or none back await googleAppointmentFilter.ReadEventsAsync(); // If there is are appointments, see if any match the subject bool foundAppointment = false; if (googleAppointmentFilter.ItemCount > 0) foundAppointment = googleAppointmentFilter.GetEvent(); // Update the appointment we may have pulled or the new one // Set the appointment length to 5 minutes, starting at the estimated completion date and time // Reminder interval was already validated // Use the values from the screen as these may differ what the user has set for defaults googleAppointmentFilter.StartDate = queuedSkill.EndTime.ToLocalTime(); googleAppointmentFilter.EndDate = queuedSkill.EndTime.ToLocalTime().AddMinutes(5); googleAppointmentFilter.ItemReminder = Settings.Calendar.UseReminding; googleAppointmentFilter.AlternateReminder = Settings.Calendar.UseAlternateReminding; googleAppointmentFilter.EarlyReminder = Settings.Calendar.EarlyReminding; googleAppointmentFilter.LateReminder = Settings.Calendar.LateReminding; googleAppointmentFilter.Minutes = Settings.Calendar.RemindingInterval; googleAppointmentFilter.ReminderMethod = Settings.Calendar.GoogleEventReminder; await googleAppointmentFilter.AddOrUpdateEventAsync(foundAppointment, queuePosition, lastSkillInQueue); } catch (TokenResponseException ex) { MessageBox.Show(ex.Error.ErrorDescription, @"Google Calendar"); } catch (GoogleApiException ex) { MessageBox.Show(ex.Error.Message, @"Google Calendar"); } catch (APIException ex) { MessageBox.Show(ex.Message, ex.ErrorCode ?? @"Google Calendar"); } catch (Exception ex) { MessageBox.Show(ex.Message, @"Google Calendar"); } }
/// <summary> /// Check if the input level is higher than the current level /// </summary> /// <param name="newLevel"></param> /// <returns></returns> internal bool HasBeenCompleted(QueuedSkill queuedSkill) { return(Math.Min(queuedSkill.Level, s_maxLevel) > m_level); }
/// <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> /// Displays the skill tool tip. /// </summary> /// <param name="skillRect">The skill rect.</param> /// <param name="skill">The skill.</param> private void DisplaySkillToolTip(RectangleF skillRect, QueuedSkill skill) { const string Format = "{0} {1}\n Start{2}\t{3}\n Ends\t{4}"; string skillName = skill.SkillName; string skillLevel = Skill.GetRomanFromInt(skill.Level); string skillStart = skill.Owner.IsTraining ? skill.StartTime.ToLocalTime().ToAbsoluteDateTimeDescription(DateTimeKind.Local) : "Paused"; string skillEnd = skill.Owner.IsTraining ? skill.EndTime.ToLocalTime().ToAbsoluteDateTimeDescription(DateTimeKind.Local) : "Paused"; string startText = skill.StartTime < DateTime.UtcNow ? "ed" : "s"; string text = String.Format(CultureConstants.DefaultCulture, Format, skillName, skillLevel, startText, skillStart, skillEnd); Size textSize = TextRenderer.MeasureText(text, Font); Size toolTipSize = new Size(textSize.Width + 13, textSize.Height + 11); Point tipPoint = new Point((int)(Math.Min(skillRect.Right, Width) + skillRect.Left) / 2 - toolTipSize.Width / 2, -toolTipSize.Height); tipPoint.Offset(0, -21); m_toolTip.Show(text, tipPoint); }