internal void RemoveSkills(FilterOptions skills, bool createKnowledge = true) { string strCategory; switch (skills) { case FilterOptions.Magician: case FilterOptions.Sorcery: case FilterOptions.Conjuring: case FilterOptions.Enchanting: case FilterOptions.Adept: strCategory = "Magical Active"; break; case FilterOptions.Technomancer: strCategory = "Resonance Active"; break; default: return; } // Check for duplicates (we'd normally want to make sure it's enabled, but SpecialSkills doesn't process the Enabled property properly) foreach (Improvement objImprovement in _objCharacter.Improvements.Where(x => x.ImproveType == Improvement.ImprovementType.SpecialSkills)) { FilterOptions eLoopFilter = (FilterOptions)Enum.Parse(typeof(FilterOptions), objImprovement.ImprovedName); string strLoopCategory = string.Empty; switch (eLoopFilter) { case FilterOptions.Magician: case FilterOptions.Sorcery: case FilterOptions.Conjuring: case FilterOptions.Enchanting: case FilterOptions.Adept: strLoopCategory = "Magical Active"; break; case FilterOptions.Technomancer: strLoopCategory = "Resonance Active"; break; } if (strLoopCategory == strCategory) { return; } } for (int i = Skills.Count - 1; i >= 0; i--) { if (Skills[i].SkillCategory == strCategory) { Skill skill = Skills[i]; _dicSkillBackups.Add(skill.SkillId, skill); Skills.RemoveAt(i); SkillsDictionary.Remove(skill.IsExoticSkill ? skill.Name + " (" + skill.DisplaySpecializationMethod(GlobalOptions.DefaultLanguage) + ')' : skill.Name); if (_objCharacter.Created && skill.TotalBaseRating > 0 && createKnowledge) { KnowledgeSkill kno = new KnowledgeSkill(_objCharacter) { Type = skill.Name == "Arcana" ? "Academic" : "Professional", WriteableName = skill.Name, Base = skill.Base, Karma = skill.Karma }; kno.Specializations.AddRange(skill.Specializations); KnowledgeSkills.MergeInto(kno, (x, y) => string.Compare(x.Type, y.Type, StringComparison.Ordinal) == 0 ? CompareSkills(x, y) : (string.Compare(x.Type, y.Type, StringComparison.Ordinal) == -1 ? -1 : 1), (objExistSkill, objNewSkill) => { if (objNewSkill.Base > objExistSkill.Base) { objExistSkill.Base = objNewSkill.Base; } if (objNewSkill.Karma > objExistSkill.Karma) { objExistSkill.Karma = objNewSkill.Karma; } objExistSkill.Specializations.MergeInto(objNewSkill.Specializations, (x, y) => x.Free == y.Free ? string.Compare(x.DisplayName(GlobalOptions.Language), y.DisplayName(GlobalOptions.Language), StringComparison.Ordinal) : (x.Free ? 1 : -1)); }); } } } if (!_objCharacter.Created) { // zero out any skillgroups whose skills did not make the final cut foreach (SkillGroup objSkillGroup in SkillGroups) { if (!objSkillGroup.SkillList.Any(x => SkillsDictionary.ContainsKey(x.Name))) { objSkillGroup.Base = 0; objSkillGroup.Karma = 0; } } } }
internal void Load(XmlNode xmlSkillNode, bool blnLegacy = false) { if (xmlSkillNode == null) { return; } Timekeeper.Start("load_char_skills"); if (!blnLegacy) { Timekeeper.Start("load_char_skills_groups"); List <SkillGroup> lstLoadingSkillGroups = new List <SkillGroup>(); foreach (XmlNode xmlNode in xmlSkillNode.SelectNodes("groups/group")) { SkillGroup objGroup = new SkillGroup(_objCharacter); objGroup.Load(xmlNode); lstLoadingSkillGroups.Add(objGroup); } lstLoadingSkillGroups.Sort((i1, i2) => String.Compare(i2.DisplayName, i1.DisplayName, StringComparison.Ordinal)); foreach (SkillGroup skillgroup in lstLoadingSkillGroups) { SkillGroups.Add(skillgroup); } Timekeeper.Finish("load_char_skills_groups"); Timekeeper.Start("load_char_skills_normal"); //Load skills. Because sorting a BindingList is complicated we use a temporery normal list List <Skill> lstLoadingSkills = new List <Skill>(); foreach (XmlNode xmlNode in xmlSkillNode.SelectNodes("skills/skill")) { Skill objSkill = Skill.Load(_objCharacter, xmlNode); if (objSkill != null) { lstLoadingSkills.Add(objSkill); } } lstLoadingSkills.Sort(CompareSkills); foreach (Skill objSkill in lstLoadingSkills) { string strName = objSkill.IsExoticSkill ? $"{objSkill.Name} ({objSkill.DisplaySpecializationMethod(GlobalOptions.DefaultLanguage)})" : objSkill.Name; bool blnDoAddToDictionary = true; _lstSkills.MergeInto(objSkill, CompareSkills, (objExistSkill, objNewSkill) => { blnDoAddToDictionary = false; if (objNewSkill.Base > objExistSkill.Base) { objExistSkill.Base = objNewSkill.Base; } if (objNewSkill.Karma > objExistSkill.Karma) { objExistSkill.Karma = objNewSkill.Karma; } objExistSkill.Specializations.MergeInto(objNewSkill.Specializations, (x, y) => x.Free == y.Free ? String.Compare(x.DisplayName(GlobalOptions.Language), y.DisplayName(GlobalOptions.Language), StringComparison.Ordinal) : (x.Free ? 1 : -1)); }); if (blnDoAddToDictionary) { _dicSkills.Add(strName, objSkill); } } Timekeeper.Finish("load_char_skills_normal"); Timekeeper.Start("load_char_skills_kno"); foreach (XmlNode xmlNode in xmlSkillNode.SelectNodes("knoskills/skill")) { if (Skill.Load(_objCharacter, xmlNode) is KnowledgeSkill objSkill) { KnowledgeSkills.Add(objSkill); } } Timekeeper.Finish("load_char_skills_kno"); Timekeeper.Start("load_char_knowsoft_buffer"); // Knowsoft Buffer. foreach (XmlNode objXmlSkill in xmlSkillNode.SelectNodes("skilljackknowledgeskills/skill")) { string strName = string.Empty; if (objXmlSkill.TryGetStringFieldQuickly("name", ref strName)) { KnowsoftSkills.Add(new KnowledgeSkill(_objCharacter, strName)); } } Timekeeper.Finish("load_char_knowsoft_buffer"); } else { List <Skill> lstTempSkillList = new List <Skill>(); foreach (XmlNode xmlNode in xmlSkillNode.SelectNodes("skills/skill")) { Skill objSkill = Skill.LegacyLoad(_objCharacter, xmlNode); if (objSkill != null) { lstTempSkillList.Add(objSkill); } } if (lstTempSkillList.Count > 0) { List <Skill> lstUnsortedSkills = new List <Skill>(); //Variable/Anon method as to not clutter anywhere else. Not sure if clever or stupid bool OldSkillFilter(Skill skill) { if (skill.Rating > 0) { return(true); } if (skill.SkillCategory == "Resonance Active" && !_objCharacter.RESEnabled) { return(false); } //This could be more fine grained, but frankly i don't care if (skill.SkillCategory == "Magical Active" && !_objCharacter.MAGEnabled) { return(false); } return(true); } foreach (Skill objSkill in lstTempSkillList) { if (objSkill is KnowledgeSkill objKnoSkill) { KnowledgeSkills.Add(objKnoSkill); } else if (OldSkillFilter(objSkill)) { lstUnsortedSkills.Add(objSkill); } } lstUnsortedSkills.Sort(CompareSkills); foreach (Skill objSkill in lstUnsortedSkills) { _lstSkills.Add(objSkill); _dicSkills.Add(objSkill.IsExoticSkill ? objSkill.Name + " (" + objSkill.DisplaySpecializationMethod(GlobalOptions.DefaultLanguage) + ')' : objSkill.Name, objSkill); } UpdateUndoList(xmlSkillNode); } } //This might give subtle bugs in the future, //but right now it needs to be run once when upgrading or it might crash. //As some didn't they crashed on loading skills. //After this have run, it won't (for the crash i'm aware) //TODO: Move it to the other side of the if someday? if (!_objCharacter.Created) { // zero out any skillgroups whose skills did not make the final cut foreach (SkillGroup objSkillGroup in SkillGroups) { if (!objSkillGroup.SkillList.Any(x => SkillsDictionary.ContainsKey(x.Name))) { objSkillGroup.Base = 0; objSkillGroup.Karma = 0; } } } //Workaround for probably breaking compability between earlier beta builds if (xmlSkillNode["skillptsmax"] == null) { xmlSkillNode = xmlSkillNode.OwnerDocument["character"]; } int intTmp = 0; if (xmlSkillNode.TryGetInt32FieldQuickly("skillptsmax", ref intTmp)) { SkillPointsMaximum = intTmp; } if (xmlSkillNode.TryGetInt32FieldQuickly("skillgrpsmax", ref intTmp)) { SkillGroupPointsMaximum = intTmp; } Timekeeper.Finish("load_char_skills"); }