コード例 #1
0
 public static int CompareSkillGroups(SkillGroup objXGroup, SkillGroup objYGroup)
 {
     if (objXGroup == null)
     {
         if (objYGroup == null)
         {
             return(0);
         }
         return(-1);
     }
     return(objYGroup == null ? 1 : string.Compare(objXGroup.DisplayName, objYGroup.DisplayName, StringComparison.Ordinal));
 }
コード例 #2
0
ファイル: SkillGroup.cs プロジェクト: jedmitten/chummer5a
        internal static SkillGroup Load(Character character, XmlNode saved)
        {
            if (saved == null)
            {
                return(null);
            }
            saved.TryGetField("id", Guid.TryParse, out Guid g);
            SkillGroup group = new SkillGroup(character, saved["name"]?.InnerText, g);

            saved.TryGetInt32FieldQuickly("karma", ref group._skillFromKarma);
            saved.TryGetInt32FieldQuickly("base", ref group._skillFromSp);

            return(group);
        }
コード例 #3
0
ファイル: SkillGroup.cs プロジェクト: jedmitten/chummer5a
        internal static SkillGroup Get(Skill skill)
        {
            if (skill.SkillGroupObject != null)
            {
                return(skill.SkillGroupObject);
            }

            if (string.IsNullOrWhiteSpace(skill.SkillGroup))
            {
                return(null);
            }

            foreach (SkillGroup skillGroup in skill.CharacterObject.SkillsSection.SkillGroups)
            {
                if (skillGroup._groupName == skill.SkillGroup)
                {
                    if (!skillGroup._affectedSkills.Contains(skill))
                    {
                        skillGroup.Add(skill);
                    }
                    return(skillGroup);
                }
            }

            SkillGroup newGroup = new SkillGroup(skill.CharacterObject, skill.SkillGroup);

            newGroup.Add(skill);
            skill.CharacterObject.SkillsSection.SkillGroups.MergeInto(newGroup, (l, r) => String.Compare(l.DisplayName, r.DisplayName, StringComparison.Ordinal),
                                                                      (l, r) => { foreach (Skill x in r.SkillList.Where(y => !l.SkillList.Contains(y)))
                                                                                  {
                                                                                      l.SkillList.Add(x);
                                                                                  }
                                                                      });

            return(newGroup);
        }
コード例 #4
0
        public static SkillGroup Get(Skill objSkill)
        {
            if (objSkill.SkillGroupObject != null)
            {
                return(objSkill.SkillGroupObject);
            }

            if (string.IsNullOrWhiteSpace(objSkill.SkillGroup))
            {
                return(null);
            }

            foreach (SkillGroup objSkillGroup in objSkill.CharacterObject.SkillsSection.SkillGroups)
            {
                if (objSkillGroup.Name == objSkill.SkillGroup)
                {
                    if (!objSkillGroup.SkillList.Contains(objSkill))
                    {
                        objSkillGroup.Add(objSkill);
                    }
                    return(objSkillGroup);
                }
            }

            SkillGroup objNewGroup = new SkillGroup(objSkill.CharacterObject, objSkill.SkillGroup);

            objNewGroup.Add(objSkill);
            objSkill.CharacterObject.SkillsSection.SkillGroups.MergeInto(objNewGroup, (l, r) => string.Compare(l.DisplayName, r.DisplayName, StringComparison.Ordinal),
                                                                         (l, r) => { foreach (Skill x in r.SkillList.Where(y => !l.SkillList.Contains(y)))
                                                                                     {
                                                                                         l.SkillList.Add(x);
                                                                                     }
                                                                         });

            return(objNewGroup);
        }
コード例 #5
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>();
                using (XmlNodeList xmlGroupsList = xmlSkillNode.SelectNodes("groups/group"))
                    if (xmlGroupsList != null)
                    {
                        foreach (XmlNode xmlNode in xmlGroupsList)
                        {
                            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>();
                using (XmlNodeList xmlSkillsList = xmlSkillNode.SelectNodes("skills/skill"))
                    if (xmlSkillsList != null)
                    {
                        foreach (XmlNode xmlNode in xmlSkillsList)
                        {
                            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);
                    }
                }
                // TODO: Skill groups don't refresh their CanIncrease property correctly when the last of their skills is being added, as the total basse rating will be zero. Call this here to force a refresh.
                foreach (SkillGroup g in SkillGroups)
                {
                    g.OnPropertyChanged(nameof(SkillGroup.SkillList));
                }
                Timekeeper.Finish("load_char_skills_normal");

                Timekeeper.Start("load_char_skills_kno");
                using (XmlNodeList xmlSkillsList = xmlSkillNode.SelectNodes("knoskills/skill"))
                    if (xmlSkillsList != null)
                    {
                        foreach (XmlNode xmlNode in xmlSkillsList)
                        {
                            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.
                using (XmlNodeList xmlSkillsList = xmlSkillNode.SelectNodes("skilljackknowledgeskills/skill"))
                    if (xmlSkillsList != null)
                    {
                        foreach (XmlNode xmlNode in xmlSkillsList)
                        {
                            string strName = string.Empty;
                            if (xmlNode.TryGetStringFieldQuickly("name", ref strName))
                            {
                                KnowsoftSkills.Add(new KnowledgeSkill(_objCharacter, strName));
                            }
                        }
                    }
                Timekeeper.Finish("load_char_knowsoft_buffer");
            }
            else
            {
                List <Skill> lstTempSkillList = new List <Skill>();
                using (XmlNodeList xmlSkillsList = xmlSkillNode.SelectNodes("skills/skill"))
                    if (xmlSkillsList != null)
                    {
                        foreach (XmlNode xmlNode in xmlSkillsList)
                        {
                            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");
        }
コード例 #6
0
ファイル: Skill.core.cs プロジェクト: jedmitten/chummer5a
        /// <summary>
        /// Karma price to upgrade. Returns negative if impossible
        /// </summary>
        /// <returns>Price in karma</returns>
        public virtual int UpgradeKarmaCost()
        {
            int intTotalBaseRating = TotalBaseRating;

            if (intTotalBaseRating >= RatingMaximum)
            {
                return(-1);
            }
            int upgrade        = 0;
            int intOptionsCost = 1;

            if (intTotalBaseRating == 0)
            {
                intOptionsCost = _character.Options.KarmaNewActiveSkill;
                upgrade       += intOptionsCost;
            }
            else
            {
                intOptionsCost = _character.Options.KarmaImproveActiveSkill;
                upgrade       += (intTotalBaseRating + 1) * intOptionsCost;
            }

            if (_character.Options.CompensateSkillGroupKarmaDifference)
            {
                SkillGroup objMySkillGroup = SkillGroupObject;
                if (objMySkillGroup != null)
                {
                    int intSkillGroupUpper = int.MaxValue;
                    foreach (Skill objSkillGroupMember in objMySkillGroup.SkillList)
                    {
                        if (objSkillGroupMember != this)
                        {
                            int intLoopTotalBaseRating = objSkillGroupMember.TotalBaseRating;
                            if (intLoopTotalBaseRating < intSkillGroupUpper)
                            {
                                intSkillGroupUpper = intLoopTotalBaseRating;
                            }
                        }
                    }
                    if (intSkillGroupUpper != int.MaxValue && intSkillGroupUpper > intTotalBaseRating)
                    {
                        int intGroupCost      = 0;
                        int intNakedSkillCost = objMySkillGroup.SkillList.Count;
                        if (intTotalBaseRating == 0)
                        {
                            intGroupCost       = _character.Options.KarmaNewSkillGroup;
                            intNakedSkillCost *= _character.Options.KarmaNewActiveSkill;
                        }
                        else
                        {
                            intGroupCost       = (intTotalBaseRating + 1) * _character.Options.KarmaImproveSkillGroup;
                            intNakedSkillCost *= (intTotalBaseRating + 1) * _character.Options.KarmaImproveActiveSkill;
                        }

                        upgrade += (intGroupCost - intNakedSkillCost);
                    }
                }
            }

            decimal decMultiplier = 1.0m;
            int     intExtra      = 0;

            foreach (Improvement objLoopImprovement in CharacterObject.Improvements)
            {
                if ((objLoopImprovement.Maximum == 0 || intTotalBaseRating <= objLoopImprovement.Maximum) && objLoopImprovement.Minimum <= intTotalBaseRating &&
                    (string.IsNullOrEmpty(objLoopImprovement.Condition) || (objLoopImprovement.Condition == "career") == _character.Created || (objLoopImprovement.Condition == "create") != _character.Created) && objLoopImprovement.Enabled)
                {
                    if (objLoopImprovement.ImprovedName == Name || string.IsNullOrEmpty(objLoopImprovement.ImprovedName))
                    {
                        if (objLoopImprovement.ImproveType == Improvement.ImprovementType.ActiveSkillKarmaCost)
                        {
                            intExtra += objLoopImprovement.Value;
                        }
                        else if (objLoopImprovement.ImproveType == Improvement.ImprovementType.ActiveSkillKarmaCostMultiplier)
                        {
                            decMultiplier *= objLoopImprovement.Value / 100.0m;
                        }
                    }
                    else if (objLoopImprovement.ImprovedName == SkillCategory)
                    {
                        if (objLoopImprovement.ImproveType == Improvement.ImprovementType.SkillCategoryKarmaCost)
                        {
                            intExtra += objLoopImprovement.Value;
                        }
                        else if (objLoopImprovement.ImproveType == Improvement.ImprovementType.SkillCategoryKarmaCostMultiplier)
                        {
                            decMultiplier *= objLoopImprovement.Value / 100.0m;
                        }
                    }
                }
            }
            if (decMultiplier != 1.0m)
            {
                upgrade = decimal.ToInt32(decimal.Ceiling(upgrade * decMultiplier));
            }
            upgrade += intExtra;

            int intMinCost = Math.Min(1, intOptionsCost);

            if (upgrade < intMinCost && !_character.Options.CompensateSkillGroupKarmaDifference)
            {
                upgrade = intMinCost;
            }
            return(upgrade);
        }
コード例 #7
0
ファイル: Skill.core.cs プロジェクト: jedmitten/chummer5a
        /// <summary>
        /// Calculate the karma cost of increasing a skill from lower to upper
        /// </summary>
        /// <param name="lower">Staring rating of skill</param>
        /// <param name="upper">End rating of the skill</param>
        /// <returns></returns>
        protected int RangeCost(int lower, int upper)
        {
            if (lower >= upper)
            {
                return(0);
            }

            int intLevelsModded = upper * (upper + 1); //cost if nothing else was there

            intLevelsModded -= lower * (lower + 1);    //remove "karma" costs from base + free

            intLevelsModded /= 2;                      //we get square, we need triangle

            int cost = 0;

            if (lower == 0)
            {
                cost = (intLevelsModded - 1) * _character.Options.KarmaImproveActiveSkill + _character.Options.KarmaNewActiveSkill;
            }
            else
            {
                cost = intLevelsModded * _character.Options.KarmaImproveActiveSkill;
            }

            if (_character.Options.CompensateSkillGroupKarmaDifference)
            {
                SkillGroup objMySkillGroup = SkillGroupObject;
                if (objMySkillGroup != null)
                {
                    int intSkillGroupUpper = int.MaxValue;
                    foreach (Skill objSkillGroupMember in objMySkillGroup.SkillList)
                    {
                        if (objSkillGroupMember != this)
                        {
                            int intLoopTotalBaseRating = objSkillGroupMember.TotalBaseRating;
                            if (intLoopTotalBaseRating < intSkillGroupUpper)
                            {
                                intSkillGroupUpper = intLoopTotalBaseRating;
                            }
                        }
                    }
                    if (intSkillGroupUpper != int.MaxValue && intSkillGroupUpper > lower)
                    {
                        if (intSkillGroupUpper > upper)
                        {
                            intSkillGroupUpper = upper;
                        }
                        int intGroupLevelsModded = intSkillGroupUpper * (intSkillGroupUpper + 1); //cost if nothing else was there
                        intGroupLevelsModded -= lower * (lower + 1);                              //remove "karma" costs from base + free

                        intGroupLevelsModded /= 2;                                                //we get square, we need triangle

                        int intGroupCost      = 0;
                        int intNakedSkillCost = objMySkillGroup.SkillList.Count;
                        if (lower == 0)
                        {
                            intGroupCost       = (intGroupLevelsModded - 1) * _character.Options.KarmaImproveSkillGroup + _character.Options.KarmaNewSkillGroup;
                            intNakedSkillCost *= (intGroupLevelsModded - 1) * _character.Options.KarmaImproveActiveSkill + _character.Options.KarmaNewActiveSkill;
                        }
                        else
                        {
                            intGroupCost       = intGroupLevelsModded * _character.Options.KarmaImproveSkillGroup;
                            intNakedSkillCost *= intGroupLevelsModded * _character.Options.KarmaImproveActiveSkill;
                        }

                        cost += (intGroupCost - intNakedSkillCost);
                    }
                }
            }

            decimal decMultiplier = 1.0m;
            int     intExtra      = 0;

            foreach (Improvement objLoopImprovement in CharacterObject.Improvements)
            {
                if (objLoopImprovement.Minimum <= lower &&
                    (string.IsNullOrEmpty(objLoopImprovement.Condition) || (objLoopImprovement.Condition == "career") == _character.Created || (objLoopImprovement.Condition == "create") != _character.Created) && objLoopImprovement.Enabled)
                {
                    if (objLoopImprovement.ImprovedName == Name || string.IsNullOrEmpty(objLoopImprovement.ImprovedName))
                    {
                        if (objLoopImprovement.ImproveType == Improvement.ImprovementType.ActiveSkillKarmaCost)
                        {
                            intExtra += objLoopImprovement.Value * (Math.Min(upper, objLoopImprovement.Maximum == 0 ? int.MaxValue : objLoopImprovement.Maximum) - Math.Max(lower, objLoopImprovement.Minimum - 1));
                        }
                        else if (objLoopImprovement.ImproveType == Improvement.ImprovementType.ActiveSkillKarmaCostMultiplier)
                        {
                            decMultiplier *= objLoopImprovement.Value / 100.0m;
                        }
                    }
                    else if (objLoopImprovement.ImprovedName == SkillCategory)
                    {
                        if (objLoopImprovement.ImproveType == Improvement.ImprovementType.SkillCategoryKarmaCost)
                        {
                            intExtra += objLoopImprovement.Value * (Math.Min(upper, objLoopImprovement.Maximum == 0 ? int.MaxValue : objLoopImprovement.Maximum) - Math.Max(lower, objLoopImprovement.Minimum - 1));
                        }
                        else if (objLoopImprovement.ImproveType == Improvement.ImprovementType.SkillCategoryKarmaCostMultiplier)
                        {
                            decMultiplier *= objLoopImprovement.Value / 100.0m;
                        }
                    }
                }
            }
            if (decMultiplier != 1.0m)
            {
                cost = decimal.ToInt32(decimal.Ceiling(cost * decMultiplier));
            }
            cost += intExtra;

            if (cost < 0 && !_character.Options.CompensateSkillGroupKarmaDifference)
            {
                cost = 0;
            }
            return(cost);
        }
コード例 #8
0
ファイル: SkillsSection.cs プロジェクト: Rempha/chummer5a
        internal void Load(XmlNode skillNode, bool legacy = false)
        {
            if (skillNode == null)
            {
                return;
            }
            Timekeeper.Start("load_char_skills");

            if (!legacy)
            {
                Timekeeper.Start("load_char_skills_groups");
                List <SkillGroup> loadingSkillGroups = new List <SkillGroup>();
                foreach (XmlNode node in skillNode.SelectNodes("groups/group"))
                {
                    SkillGroup skillgroup = SkillGroup.Load(_character, node);
                    if (skillgroup != null)
                    {
                        loadingSkillGroups.Add(skillgroup);
                    }
                }
                loadingSkillGroups.Sort((i1, i2) => String.Compare(i2.DisplayName, i1.DisplayName, StringComparison.Ordinal));
                foreach (SkillGroup skillgroup in loadingSkillGroups)
                {
                    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> loadingSkills = new List <Skill>();
                foreach (XmlNode node in skillNode.SelectNodes("skills/skill"))
                {
                    Skill skill = Skill.Load(_character, node);
                    if (skill != null)
                    {
                        loadingSkills.Add(skill);
                    }
                }
                loadingSkills.Sort(CompareSkills);


                foreach (Skill skill in loadingSkills)
                {
                    _skills.Add(skill);
                    _dicSkills.Add(skill.IsExoticSkill ? skill.Name + " (" + skill.DisplaySpecialization + ")" : skill.Name, skill);
                }
                Timekeeper.Finish("load_char_skills_normal");

                Timekeeper.Start("load_char_skills_kno");
                foreach (XmlNode node in skillNode.SelectNodes("knoskills/skill"))
                {
                    if (Skill.Load(_character, node) is KnowledgeSkill skill)
                    {
                        KnowledgeSkills.Add(skill);
                    }
                }
                Timekeeper.Finish("load_char_skills_kno");

                Timekeeper.Start("load_char_knowsoft_buffer");
                // Knowsoft Buffer.
                foreach (XmlNode objXmlSkill in skillNode.SelectNodes("skilljackknowledgeskills/skill"))
                {
                    string strName = string.Empty;
                    if (objXmlSkill.TryGetStringFieldQuickly("name", ref strName))
                    {
                        KnowsoftSkills.Add(new KnowledgeSkill(_character, strName));
                    }
                }
                Timekeeper.Finish("load_char_knowsoft_buffer");
            }
            else
            {
                List <Skill> tempSkillList = new List <Skill>();
                foreach (XmlNode node in skillNode.SelectNodes("skills/skill"))
                {
                    Skill skill = Skill.LegacyLoad(_character, node);
                    if (skill != null)
                    {
                        tempSkillList.Add(skill);
                    }
                }

                if (tempSkillList.Count > 0)
                {
                    List <Skill> unsoredSkills = 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" && !_character.RESEnabled)
                        {
                            return(false);
                        }

                        //This could be more fine grained, but frankly i don't care
                        if (skill.SkillCategory == "Magical Active" && !_character.MAGEnabled)
                        {
                            return(false);
                        }

                        return(true);
                    }

                    foreach (Skill skill in tempSkillList)
                    {
                        if (skill is KnowledgeSkill knoSkill)
                        {
                            KnowledgeSkills.Add(knoSkill);
                        }
                        else if (oldSkillFilter(skill))
                        {
                            unsoredSkills.Add(skill);
                        }
                    }

                    unsoredSkills.Sort(CompareSkills);

                    foreach (Skill objSkill in unsoredSkills)
                    {
                        _skills.Add(objSkill);
                        _dicSkills.Add(objSkill.IsExoticSkill ? objSkill.Name + " (" + objSkill.DisplaySpecialization + ")" : objSkill.Name, objSkill);
                    }

                    UpdateUndoList(skillNode);
                }
            }

            //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 (!_character.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 (skillNode["skillptsmax"] == null)
            {
                skillNode = skillNode.OwnerDocument["character"];
            }

            int intTmp = 0;

            if (skillNode.TryGetInt32FieldQuickly("skillptsmax", ref intTmp))
            {
                SkillPointsMaximum = intTmp;
            }
            if (skillNode.TryGetInt32FieldQuickly("skillgrpsmax", ref intTmp))
            {
                SkillGroupPointsMaximum = intTmp;
            }

            Timekeeper.Finish("load_char_skills");
        }