/// <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> /// Generate a trainings array from the skills already know by a character. /// </summary> /// <param name="character">The character.</param> /// <param name="plan">The plan.</param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">character or plan</exception> public static RemappingResult OptimizeFromCharacter(Character character, BasePlan plan) { character.ThrowIfNull(nameof(character)); plan.ThrowIfNull(nameof(plan)); // Create a character without any skill CharacterScratchpad scratchpad = new CharacterScratchpad(character.After(plan.ChosenImplantSet)); scratchpad.ClearSkills(); // Create a new plan Plan newPlan = new Plan(scratchpad); // Add all trained skill levels that the character has trained so far foreach (Skill skill in character.Skills) { newPlan.PlanTo(skill, skill.Level); } // Create a new remapping RemappingResult remapping = new RemappingResult(scratchpad); // Add those skills to the remapping foreach (PlanEntry entry in newPlan) { remapping.Skills.Add(entry); } // Optimize remapping.Optimize(TimeSpan.MaxValue); return(remapping); }
/// <summary> /// Gets the list of remapping results from a plan. /// </summary> /// <param name="plan"></param> /// <returns></returns> /// <exception cref="System.ArgumentNullException">plan</exception> public static Collection <RemappingResult> GetResultsFromRemappingPoints(BasePlan plan) { plan.ThrowIfNull(nameof(plan)); CharacterScratchpad scratchpad = new CharacterScratchpad(plan.Character.After(plan.ChosenImplantSet)); Collection <RemappingResult> remappingList = new Collection <RemappingResult>(); Collection <ISkillLevel> list = new Collection <ISkillLevel>(); // Scroll through the entries and split it into remappings foreach (PlanEntry entry in plan) { // Ends the current remapping and start a new one if (entry.Remapping != null) { // Creates a new remapping RemappingResult remapping = new RemappingResult(entry.Remapping, scratchpad.Clone()); remappingList.Add(remapping); list = remapping.Skills; } // Add this skill to the training list scratchpad.Train(entry); list.Add(entry); } // Return return(remappingList); }
/// <summary> /// Resets the scratchpad from the <see cref="BaseCharacter"/> it was built upon. /// </summary> public void Reset() { CharacterScratchpad character = m_character as CharacterScratchpad; if (character != null) { Reset(character); } else { ResetFromCharacter(); } }
/// <summary> /// Resets this scratchpad using the provided scratchpad. /// </summary> /// <param name="scratchpad"></param> private void Reset(CharacterScratchpad scratchpad) { m_skillPoints = scratchpad.m_skillPoints; TrainingTime = scratchpad.TrainingTime; TrainedSkills.Clear(); TrainedSkills.AddRange(scratchpad.TrainedSkills); for (int i = 0; i < m_attributes.Length; i++) { m_attributes[i].Reset(scratchpad.m_attributes[i]); } scratchpad.m_skillSP.CopyTo(m_skillSP, 0); scratchpad.m_skillLevels.CopyTo(m_skillLevels, 0); }
/// <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>(); CharacterScratchpad scratchpad = new CharacterScratchpad(m_character); if (m_groupByPriority) { groupedPlan.AddRange( m_entries.GroupBy(x => x.Priority).OrderBy(x => x.Key).Select(group => 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 (PlanScratchpad 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> /// Compute the best possible attributes to fulfill the given trainings array /// </summary> /// <param name="skills"></param> /// <param name="baseScratchpad"></param> /// <param name="maxDuration"></param> /// <returns></returns> internal static CharacterScratchpad Optimize <T>(IEnumerable <T> skills, CharacterScratchpad baseScratchpad, TimeSpan maxDuration) where T : ISkillLevel { CharacterScratchpad bestScratchpad = new CharacterScratchpad(baseScratchpad); CharacterScratchpad tempScratchpad = new CharacterScratchpad(baseScratchpad); TimeSpan baseTime = baseScratchpad.TrainingTime; TimeSpan bestTime = TimeSpan.MaxValue; int bestSkillCount = 0; // Now, we have the points to spend, let's perform all the // combinations (less than 11^4 = 14,641) for (int per = 0; per <= EveConstants.MaxRemappablePointsPerAttribute; per++) { // WIL int maxWillpower = EveConstants.SpareAttributePointsOnRemap - per; for (int will = 0; will <= maxWillpower && will <= EveConstants.MaxRemappablePointsPerAttribute; will++) { // INT int maxIntelligence = maxWillpower - will; for (int intell = 0; intell <= maxIntelligence && intell <= EveConstants.MaxRemappablePointsPerAttribute; intell++) { // MEM int maxMemory = maxIntelligence - intell; for (int mem = 0; mem <= maxMemory && mem <= EveConstants.MaxRemappablePointsPerAttribute; mem++) { // CHA int cha = maxMemory - mem; // Reject invalid combinations if (cha > EveConstants.MaxRemappablePointsPerAttribute) { continue; } // Resets the scratchpad tempScratchpad.Reset(); // Set new attributes tempScratchpad.Memory.Base = mem + EveConstants.CharacterBaseAttributePoints; tempScratchpad.Charisma.Base = cha + EveConstants.CharacterBaseAttributePoints; tempScratchpad.Willpower.Base = will + EveConstants.CharacterBaseAttributePoints; tempScratchpad.Perception.Base = per + EveConstants.CharacterBaseAttributePoints; tempScratchpad.Intelligence.Base = intell + EveConstants.CharacterBaseAttributePoints; // Train skills int tempSkillCount = 0; foreach (T skill in skills) { tempSkillCount++; tempScratchpad.Train(skill); // Did it go over max duration ? if (tempScratchpad.TrainingTime - baseTime > maxDuration) { break; } // Did it go over the best time so far without training more skills ? if (tempSkillCount <= bestSkillCount && tempScratchpad.TrainingTime > bestTime) { break; } } // Did it manage to train more skills before the max duration, // or did it train the same number of skills in a lesser time ? if (tempSkillCount <= bestSkillCount && (tempSkillCount != bestSkillCount || tempScratchpad.TrainingTime >= bestTime)) { continue; } bestScratchpad.Reset(); bestScratchpad.Memory.Base = tempScratchpad.Memory.Base; bestScratchpad.Charisma.Base = tempScratchpad.Charisma.Base; bestScratchpad.Willpower.Base = tempScratchpad.Willpower.Base; bestScratchpad.Perception.Base = tempScratchpad.Perception.Base; bestScratchpad.Intelligence.Base = tempScratchpad.Intelligence.Base; bestTime = tempScratchpad.TrainingTime; bestSkillCount = tempSkillCount; } } } } // Return the best scratchpad found return(bestScratchpad); }
/// <summary> /// Allow to store the state of this scratchpad to restore it later. /// The method returns an <see cref="IDisposable"/> object which, once disposed, will restore the state. /// </summary> /// <remarks>Use it in a <c>using</c> block to enforce an automatic restoration of the object even when exceptions are throw.</remarks> /// <returns>A disposable object which, once disposed, will restore the state of the </returns> public IDisposable BeginTemporaryChanges() { CharacterScratchpad clone = Clone(); return(new DisposableWithCallback(() => Reset(clone))); }