/// <summary> /// Constructor for use in code /// </summary> /// <param name="skill">The skill we want to analyze</param> /// <param name="planWindow">The plan window</param> public SkillExplorerWindow(Skill skill, PlanWindow planWindow) : this() { m_planWindow = planWindow; Skill = skill; EveClient.PlanChanged += new EventHandler<PlanChangedEventArgs>(EveClient_PlanChanged); EveClient.CharacterChanged += new EventHandler<CharacterChangedEventArgs>(EveClient_CharacterChanged); }
/// <summary> /// Checks whether those prerequisites contains the provided skill, returning the need level /// </summary> /// <param name="skill"></param> /// <param name="neededLevel"></param> /// <returns></returns> public static bool Contains(this IEnumerable<SkillLevel> src, Skill skill, out int neededLevel) { neededLevel = 0; foreach (var prereq in src) { if (prereq.Skill == skill) { neededLevel = prereq.Level; return true; } } return false; }
/// <summary> /// Checks whether the given skill level can be planned. Used to enable or disable the "Plan To N" and "Remove" menu options. /// </summary> /// <param name="plan"></param> /// <param name="skill"></param> /// <param name="level">A integer between 0 (remove all entries for this skill) and 5.</param> /// <param name="operation"></param> /// <returns></returns> public static bool EnablePlanTo(Plan plan, Skill skill, int level) { // The entry actually wants to remove the item if (level == 0) { return plan.IsPlanned(skill); } // The entry is already known else if (skill.Level >= level) { return false; } // The entry is already planned at this very level else if (plan.GetPlannedLevel(skill) == level) { return false; } // Ok return true; }
/// <summary> /// Deserialization constructor /// </summary> /// <param name="character">The character for this training</param> /// <param name="serial">The serialization object for this training</param> /// <param name="isPaused">When true, the training is currently paused.</param> /// <param name="startTimeWhenPaused">Training starttime when the queue is actually paused. Indeed, in such case, CCP returns empty start and end time, so we compute a "what if we start now" scenario.</param> internal QueuedSkill(Character character, SerializableQueuedSkill serial, bool isPaused, ref DateTime startTimeWhenPaused) { m_owner = character; m_startSP = serial.StartSP; m_endSP = serial.EndSP; m_level = serial.Level; m_skill = character.Skills[serial.ID]; if (!isPaused) { // Not paused, we should trust CCP m_startTime = serial.StartTime; m_endTime = serial.EndTime; } else { // StartTime and EndTime were empty on the serialization object if the skill was paused // So we compute a "what if we start now" scenario m_startTime = startTimeWhenPaused; if (m_skill != null) startTimeWhenPaused += m_skill.GetLeftTrainingTimeForLevelOnly(m_level); m_endTime = startTimeWhenPaused; } }
/// <summary> /// Show the given skill in the skill explorer. /// </summary> /// <param name="skill"></param> public void ShowSkillInExplorer(Skill skill) { var planWindow = WindowsFactory<PlanWindow>.GetByTag(m_plan); var skillExplorer = WindowsFactory<SkillExplorerWindow>.ShowByTag(planWindow, (window) => new SkillExplorerWindow(skill, window)); skillExplorer.Skill = skill; }
/// <summary> /// Sklll context menu > Show me what this skill unlocks /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void tvSkills_DoubleClick(object sender, EventArgs e) { Skill skill = GetSelectedSkill(); if (skill == null) return; Skill = skill; }
/// <summary> /// Skill context menu > Show me what this skill unlocks. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void tsSwitch_Click(object sender, EventArgs e) { Skill skill = GetSelectedSkill(); if (skill == null) return; Skill = skill; }
/// <summary> /// We want to go look at a skill in the history list again /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void cbHistory_SelectedIndexChanged(object sender, EventArgs e) { string skillName = cbHistory.Items[cbHistory.SelectedIndex] as string; Skill = m_character.Skills[skillName]; }
/// <summary> /// Gets the tooltip text for the given skill /// </summary> /// <param name="skill"></param> private string GetTooltip(Skill skill) { int sp = skill.SkillPoints; int nextLevel = Math.Min(5, skill.Level + 1); int nextLevelSP = skill.StaticData.GetPointsRequiredForLevel(nextLevel); int pointsLeft = skill.GetLeftPointsRequiredToLevel(nextLevel); string remainingTimeText = skill.GetLeftTrainingTimeToLevel(nextLevel).ToDescriptiveText( DescriptiveTextOptions.IncludeCommas | DescriptiveTextOptions.UppercaseText); if (sp < skill.StaticData.GetPointsRequiredForLevel(1)) { // Training hasn't got past level 1 yet StringBuilder untrainedToolTip = new StringBuilder(); untrainedToolTip.AppendFormat(CultureConstants.DefaultCulture, "Not yet trained to Level I ({0}%)\n", Math.Floor(skill.PercentCompleted)); untrainedToolTip.AppendFormat(CultureConstants.DefaultCulture, "Next level I: {0:#,##0} skill points remaining\n", pointsLeft); untrainedToolTip.AppendFormat(CultureConstants.DefaultCulture, "Training time remaining: {0}", remainingTimeText); AddSkillBoilerPlate(untrainedToolTip, skill); return untrainedToolTip.ToString(); } // So, it's a left click on a skill, we display the tool tip // Partially trained skill ? if (skill.IsPartiallyTrained) { StringBuilder partiallyTrainedToolTip = new StringBuilder(); partiallyTrainedToolTip.AppendFormat(CultureConstants.DefaultCulture, "Partially Completed ({0}%)\n", Math.Floor(skill.PercentCompleted)); partiallyTrainedToolTip.AppendFormat(CultureConstants.DefaultCulture, "Training to level {0}: {1:#,##0} skill points remaining\n", Skill.GetRomanForInt(nextLevel), pointsLeft); partiallyTrainedToolTip.AppendFormat(CultureConstants.DefaultCulture, "Training time remaining: {0}", remainingTimeText); AddSkillBoilerPlate(partiallyTrainedToolTip, skill); return partiallyTrainedToolTip.ToString(); } // We've completed all the skill points for the current level if (!skill.IsPartiallyTrained) { if (skill.Level != 5) { StringBuilder levelCompleteToolTip = new StringBuilder(); levelCompleteToolTip.AppendFormat(CultureConstants.DefaultCulture, "Completed Level {0}: {1:#,##0}/{2:#,##0}\n", Skill.GetRomanForInt(skill.Level), sp, nextLevelSP); levelCompleteToolTip.AppendFormat(CultureConstants.DefaultCulture, "Next level {0}: {1:#,##0} skill points required\n", Skill.GetRomanForInt(nextLevel), pointsLeft); levelCompleteToolTip.AppendFormat(CultureConstants.DefaultCulture, "Training Time: {0}", remainingTimeText); AddSkillBoilerPlate(levelCompleteToolTip, skill); return levelCompleteToolTip.ToString(); } // Lv 5 completed StringBuilder lv5ToolTip = new StringBuilder(); lv5ToolTip.AppendFormat(CultureConstants.DefaultCulture, "Level V Complete: {0:#,##0}/{1:#,##0}\n", sp, nextLevelSP); lv5ToolTip.Append("No further training required\n"); AddSkillBoilerPlate(lv5ToolTip, skill); return lv5ToolTip.ToString(); } // Error in calculating SkillPoints StringBuilder calculationErrorToolTip = new StringBuilder(); calculationErrorToolTip.AppendLine("Partially Trained (Could not cacluate all skill details)"); calculationErrorToolTip.AppendFormat(CultureConstants.DefaultCulture, "Next level {0}: {1:#,##0} skill points remaining\n",nextLevel, pointsLeft); calculationErrorToolTip.AppendFormat(CultureConstants.DefaultCulture, "Training time remaining: {0}", remainingTimeText); AddSkillBoilerPlate(calculationErrorToolTip, skill); return calculationErrorToolTip.ToString(); }
/// <summary> /// Gets the skill properties of a merged skill with a plan entry, if one is provided. /// If no plan is provided, the skill properties are returned unmodified. /// </summary> /// <param name="plan"></param> /// <param name="skill"></param> /// <returns>The skill properties after the merge</returns> private static SerializableCharacterSkill GetMergedSkill(Plan plan, Skill skill) { var mergedSkill = new SerializableCharacterSkill(); mergedSkill.ID = skill.ID; mergedSkill.Name = skill.Name; mergedSkill.IsKnown = skill.IsKnown; mergedSkill.OwnsBook = skill.IsOwned; mergedSkill.Level = skill.Level; mergedSkill.Skillpoints = skill.SkillPoints; if (plan != null) plan.Merge(mergedSkill); return mergedSkill; }
/// <summary> /// Opens this skill in the skill browser and switches to this tab. /// </summary> /// <param name="gs"></param> public void ShowSkillInBrowser(Skill gs) { tabControl.SelectedTab = tpSkillBrowser; skillBrowser.SelectedSkill = gs; }
/// <summary> /// Whenever the user right-click the skill tree on the left, we display the context menu. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void skillTreeDisplay_SkillClicked(object sender, SkillClickedEventArgs e) { if (e.Button == MouseButtons.Right) { bool isPlanned = false; isPlanned |= PlanHelper.UpdatesRegularPlanToMenu(miPlanTo0, m_plan, e.Skill, 0); isPlanned |= PlanHelper.UpdatesRegularPlanToMenu(miPlanTo1, m_plan, e.Skill, 1); isPlanned |= PlanHelper.UpdatesRegularPlanToMenu(miPlanTo2, m_plan, e.Skill, 2); isPlanned |= PlanHelper.UpdatesRegularPlanToMenu(miPlanTo3, m_plan, e.Skill, 3); isPlanned |= PlanHelper.UpdatesRegularPlanToMenu(miPlanTo4, m_plan, e.Skill, 4); isPlanned |= PlanHelper.UpdatesRegularPlanToMenu(miPlanTo5, m_plan, e.Skill, 5); cmsSkillContext.Show(skillTreeDisplay, e.Location); } else { SelectedSkill = e.Skill; } }
/// <summary> /// Updates the given "Plan to N" menu item. /// </summary> /// <param name="menu"></param> /// <param name="skill"></param> /// <param name="level"></param> /// <returns></returns> private bool UpdatePlanToMenuItem(ToolStripMenuItem menu, Skill skill, int level) { bool isKnown = (level <= skill.Level); bool isPlanned = m_plan.IsPlanned(skill, level); bool isTraining = skill.IsTraining; menu.Tag = skill; // Already planned ? if (isPlanned) { menu.Enabled = false; return true; } // Level already known if (isKnown) { menu.Enabled = false; return false; } // Output with prerequisites : "Plan to level V (5d 3h 15m)" menu.Enabled = true; return false; }
/// <summary> /// Whenever the selection changes, we update the selected skill /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void skillSelectControl_SelectedSkillChanged(object sender, EventArgs e) { SelectedSkill = skillSelectControl.SelectedSkill; }
/// <summary> /// Updates the skills tree. /// </summary> private void UpdateTree() { //Reset selected object SelectedSkill = null; // Update the image list choice int iconGroupIndex = Settings.UI.SkillBrowser.IconsGroupIndex; if (iconGroupIndex == 0) iconGroupIndex = 1; tvItems.ImageList = GetIconSet(iconGroupIndex); tvItems.ImageList.ColorDepth = ColorDepth.Depth32Bit; // Rebuild the nodes int numberOfItems = 0; tvItems.BeginUpdate(); try { tvItems.Nodes.Clear(); foreach (var group in m_skills.GroupBy(x => x.Group).ToArray().OrderBy(x => x.Key.Name)) { int index = tvItems.ImageList.Images.IndexOfKey("book"); TreeNode groupNode = new TreeNode() { Text = group.Key.Name, ImageIndex = index, SelectedImageIndex = index, Tag = group.Key }; // Add nodes for skills in this group foreach (var skill in group) { // Choose image index int imageIndex = -1; if (skill.Level != 0) { imageIndex = tvItems.ImageList.Images.IndexOfKey("lvl" + skill.Level); } else if (skill.IsKnown) { imageIndex = tvItems.ImageList.Images.IndexOfKey("lvl0"); } else if (skill.IsOwned) { imageIndex = tvItems.ImageList.Images.IndexOfKey("Book"); } else if (skill.ArePrerequisitesMet) // prereqs met { imageIndex = tvItems.ImageList.Images.IndexOfKey("PrereqsMet"); } else { imageIndex = tvItems.ImageList.Images.IndexOfKey("PrereqsNOTMet"); } // Create node and adds it TreeNode node = new TreeNode() { Text = String.Format("{0} ({1})", skill.Name, skill.Rank), ImageIndex = imageIndex, SelectedImageIndex = imageIndex, Tag = skill }; // We color some nodes if (!skill.IsPublic && Settings.UI.SkillBrowser.ShowNonPublicSkills) node.ForeColor = Color.DarkRed; if (skill.IsPartiallyTrained && Settings.UI.PlanWindow.HighlightPartialSkills) node.ForeColor = Color.Green; if (skill.IsQueued && !skill.IsTraining && Settings.UI.PlanWindow.HighlightQueuedSkills) node.ForeColor = Color.RoyalBlue; if (skill.IsTraining) { node.BackColor = Color.LightSteelBlue; node.ForeColor = Color.Black; } numberOfItems++; groupNode.Nodes.Add(node); } // Add group when not empty tvItems.Nodes.Add(groupNode); } } finally { tvItems.EndUpdate(); m_allExpanded = false; // If the filtered set is small enough to fit all nodes on screen, call expandAll() if (numberOfItems < (tvItems.DisplayRectangle.Height / tvItems.ItemHeight)) { tvItems.ExpandAll(); m_allExpanded = true; } } }
/// <summary> /// When the selection of the tree changes, updates the control's selection and fires the event. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void tvSkillList_AfterSelect(object sender, TreeViewEventArgs e) { TreeNode tn = e.Node; Skill gs = tn.Tag as Skill; SelectedSkill = gs; }
/// <summary> /// When the selection of the listview changes, updates the control's selection and fires the event. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void lvSortedSkillList_SelectedIndexChanged(object sender, EventArgs e) { if (lvSortedSkillList.SelectedItems.Count == 0) { SelectedSkill = null; } else { ListViewItem lvi = lvSortedSkillList.SelectedItems[0]; SelectedSkill = lvi.Tag as Skill; } }
/// <summary> /// When the selection of the listbox changes, updates the control's selection and fires the event. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void lbSearchList_SelectedIndexChanged(object sender, EventArgs e) { SelectedSkill = (lbSearchList.SelectedIndex >= 0 ? (Skill) lbSearchList.Items[lbSearchList.SelectedIndex] : null); }
/// <summary> /// Opens this skill in the skill explorer and switches to this tab. /// </summary> /// <param name="gs"></param> public void ShowSkillInExplorer(Skill gs) { skillBrowser.ShowSkillInExplorer(gs); }
/// <summary> /// Creates a plan entry and a list view item for it, from the given skill. /// </summary> /// <param name="gs"></param> /// <returns></returns> private ListViewItem CreatePlanItemForSkill(Skill skill) { // Gets the planned level of the skill. int newLevel = m_plan.GetPlannedLevel(skill) + 1; if (skill.Level >= newLevel) newLevel = skill.Level + 1; // Quits if already on lv5 if (newLevel > 5) return null; // Creates the plan entry and list item for this level PlanEntry newEntry = new PlanEntry(m_plan, skill, newLevel); ListViewItem newItem = new ListViewItem(newEntry.ToString()); newItem.Tag = newEntry; return newItem; }
/// <summary> /// Updates a regular "Plan to X" menu : text, tag, enable/disable. /// </summary> /// <param name="menu"></param> /// <param name="plan"></param> /// <param name="skill"></param> /// <param name="level"></param> public static bool UpdatesRegularPlanToMenu(ToolStripItem menu, Plan plan, Skill skill, int level) { if (level == 0) { menu.Text = "Remove"; } else { menu.Text = "Level " + level.ToString(); } menu.Enabled = EnablePlanTo(plan, skill, level); if (menu.Enabled) { var operation = plan.TryPlanTo(skill, level); menu.Tag = operation; if (RequiresWindow(operation)) menu.Text += "..."; } var menuItem = menu as ToolStripMenuItem; if (menuItem != null) { menuItem.Checked = (plan.GetPlannedLevel(skill) == level); } return menu.Enabled; }
/// <summary> /// Checks whether the provided skill is an immediate prerequisite /// </summary> /// <param name="skill">The skill to test</param> /// <param name="neededLevel">When this skill is an immediate prerequisite, this parameter will held the required level</param> /// <returns></returns> public bool HasAsImmediatePrerequisite(Skill skill, out int neededLevel) { return m_prereqSkills.Contains(skill, out neededLevel); }
/// <summary> /// Constructor from the skill object /// </summary> /// <param name="skill"></param> /// <param name="level"></param> public SkillLevel(Skill skill, int level) : this() { this.Skill = skill; this.Level = level; }
/// <summary> /// Completes the initialization once all the character's skills have been initialized /// </summary> /// <param name="skills">The array of the character's skills.</param> internal void CompleteInitialization(Skill[] skills) { m_prereqs.AddRange(m_staticData.Prerequisites.Select(x => new SkillLevel(skills[x.Skill.ArrayIndex], x.Level))); }
/// <summary> /// Adds the skill boiler plate. /// </summary> /// <param name="toolTip">The tool tip.</param> /// <param name="skill">The skill.</param> private static void AddSkillBoilerPlate(StringBuilder toolTip, Skill skill) { toolTip.Append("\n\n"); toolTip.AppendLine(skill.DescriptionNL); toolTip.AppendFormat(CultureConstants.DefaultCulture, "\nPrimary: {0}, ", skill.PrimaryAttribute); toolTip.AppendFormat(CultureConstants.DefaultCulture, "Secondary: {0} ", skill.SecondaryAttribute); toolTip.AppendFormat(CultureConstants.DefaultCulture, "({0:#,##0} SP/hour)", skill.SkillPointsPerHour); }
/// <summary> /// Draws the list item for the given skill /// </summary> /// <param name="skill"></param> /// <param name="e"></param> public void DrawItem(Skill skill, DrawItemEventArgs e) { Graphics g = e.Graphics; // Draw background if (skill.IsTraining) { // In training g.FillRectangle(Brushes.LightSteelBlue, e.Bounds); } else if ((e.Index % 2) == 0) { // Not in training - odd g.FillRectangle(Brushes.White, e.Bounds); } else { // Not in training - even g.FillRectangle(Brushes.LightGray, e.Bounds); } // Measure texts TextFormatFlags format = TextFormatFlags.NoPadding | TextFormatFlags.NoClipping; int skillPointsToNextLevel = skill.StaticData.GetPointsRequiredForLevel(Math.Min(skill.Level + 1, 5)); string rankText = String.Format(CultureConstants.DefaultCulture, " (Rank {0})", skill.Rank); string spText = String.Format(CultureConstants.DefaultCulture, "SP: {0:#,##0}/{1:#,##0}", skill.SkillPoints, skillPointsToNextLevel); string levelText = String.Format(CultureConstants.DefaultCulture, "Level {0}", skill.Level); string pctText = String.Format(CultureConstants.DefaultCulture, "{0}% Done", Math.Floor(skill.PercentCompleted)); Size skillNameSize = TextRenderer.MeasureText(g, skill.Name, m_boldSkillsFont, Size.Empty, format); Size rankTextSize = TextRenderer.MeasureText(g, rankText, m_skillsFont, Size.Empty, format); Size levelTextSize = TextRenderer.MeasureText(g, levelText, m_skillsFont, Size.Empty, format); Size spTextSize = TextRenderer.MeasureText(g, spText, m_skillsFont, Size.Empty, format); Size pctTextSize = TextRenderer.MeasureText(g, pctText, m_skillsFont, Size.Empty, format); // Draw texts Color highlightColor = Color.Black; if (!skill.IsKnown) highlightColor = Color.Red; if (!skill.IsPublic) highlightColor = Color.DarkRed; if (skill.ArePrerequisitesMet && skill.IsPublic && !skill.IsKnown) highlightColor = Color.SlateGray; if (Settings.UI.MainWindow.HighlightPartialSkills && skill.IsPartiallyTrained && !skill.IsTraining) highlightColor = Color.Green; if (Settings.UI.MainWindow.HighlightQueuedSkills && skill.IsQueued && !skill.IsTraining) highlightColor = Color.RoyalBlue; TextRenderer.DrawText(g, skill.Name, m_boldSkillsFont, new Rectangle( e.Bounds.Left + PadLeft, e.Bounds.Top + PadTop, skillNameSize.Width + PadLeft, skillNameSize.Height), highlightColor); TextRenderer.DrawText(g, rankText, m_skillsFont, new Rectangle( e.Bounds.Left + PadLeft + skillNameSize.Width, e.Bounds.Top + PadTop, rankTextSize.Width + PadLeft, rankTextSize.Height), highlightColor); TextRenderer.DrawText(g, spText, m_skillsFont, new Rectangle( e.Bounds.Left + PadLeft, e.Bounds.Top + PadTop + skillNameSize.Height + LineVPad, spTextSize.Width + PadLeft, spTextSize.Height), highlightColor); // Boxes g.DrawRectangle(Pens.Black, new Rectangle( e.Bounds.Right - BoxWidth - PadRight, e.Bounds.Top + PadTop, BoxWidth, BoxHeight)); int levelBoxWidth = (BoxWidth - 4 - 3) / 5; for (int level = 1; level <= 5; level++) { Rectangle brect = new Rectangle( e.Bounds.Right - BoxWidth - PadRight + 2 + (levelBoxWidth * (level - 1)) + (level - 1), e.Bounds.Top + PadTop + 2, levelBoxWidth, BoxHeight - 3); if (level <= skill.Level) { g.FillRectangle(Brushes.Black, brect); } else { g.FillRectangle(Brushes.DarkGray, brect); } // Color indicator for a queued level CCPCharacter ccpCharacter = Character as CCPCharacter; if (ccpCharacter != null) { SkillQueue skillQueue = ccpCharacter.SkillQueue; foreach (var qskill in skillQueue) { if ((!skill.IsTraining && skill == qskill.Skill && level == qskill.Level) || (skill.IsTraining && skill == qskill.Skill && level == qskill.Level && level > skill.Level + 1)) g.FillRectangle(Brushes.RoyalBlue, brect); } } // Blinking indicator of skill in training level if (skill.IsTraining && level == skill.Level + 1) { if (m_count == 0) g.FillRectangle(Brushes.White, brect); if (m_count == 1) m_count = -1; m_count ++; } } // Draw progression bar g.DrawRectangle(Pens.Black, new Rectangle(e.Bounds.Right - BoxWidth - PadRight, e.Bounds.Top + PadTop + BoxHeight + BoxVPad, BoxWidth, LowerBoxHeight)); Rectangle pctBarRect = new Rectangle(e.Bounds.Right - BoxWidth - PadRight + 2, e.Bounds.Top + PadTop + BoxHeight + BoxVPad + 2, BoxWidth - 3, LowerBoxHeight - 3); g.FillRectangle(Brushes.DarkGray, pctBarRect); int fillWidth = (int)(pctBarRect.Width * skill.FractionCompleted); if (fillWidth > 0) { Rectangle fillRect = new Rectangle(pctBarRect.X, pctBarRect.Y, fillWidth, pctBarRect.Height); g.FillRectangle(Brushes.Black, fillRect); } // Draw level and percent texts TextRenderer.DrawText(g, levelText, m_skillsFont, new Rectangle( e.Bounds.Right - BoxWidth - PadRight - BoxHPad - levelTextSize.Width, e.Bounds.Top + PadTop, levelTextSize.Width + PadRight, levelTextSize.Height), Color.Black); TextRenderer.DrawText(g, pctText, m_skillsFont, new Rectangle( e.Bounds.Right - BoxWidth - PadRight - BoxHPad - pctTextSize.Width, e.Bounds.Top + PadTop + levelTextSize.Height + LineVPad, pctTextSize.Width + PadRight, pctTextSize.Height), Color.Black); }