internal static SkillGroup Load(Character character, XmlNode saved) { if (saved == null) return null; Guid g; saved.TryGetField("id", Guid.TryParse, out g); SkillGroup group = new SkillGroup(character, saved["name"]?.InnerText, g); saved.TryGetInt32FieldQuickly("karma", ref group._skillFromKarma); saved.TryGetInt32FieldQuickly("base", ref group._skillFromSp); return group; }
internal static SkillGroup Load(Character character, XmlNode saved) { Guid g; saved.TryGetField("id", Guid.TryParse, out g); SkillGroup group = new SkillGroup(character, saved["name"].InnerText, g); saved.TryGetField("karma", out group._skillFromKarma); saved.TryGetField("base", out group._skillFromSp); return(group); }
public SkillGroupControl(SkillGroup skillGroup) { _skillGroup = skillGroup; InitializeComponent(); //This is apparently a factor 30 faster than placed in load. NFI why Stopwatch sw = Stopwatch.StartNew(); lblName.DataBindings.Add("Text", _skillGroup, "DisplayName"); tipToolTip.SetToolTip(lblName, _skillGroup.ToolTip); if (_skillGroup.Character.Created) { nudKarma.Visible = false; nudSkill.Visible = false; btnCareerIncrease.Visible = true; btnCareerIncrease.DataBindings.Add("Enabled", _skillGroup, nameof(SkillGroup.CareerCanIncrease), false, DataSourceUpdateMode.OnPropertyChanged); tipToolTip.SetToolTip(btnCareerIncrease, skillGroup.UpgradeToolTip); lblGroupRating.Visible = true; lblGroupRating.DataBindings.Add("Text", _skillGroup, nameof(SkillGroup.DisplayRating), false, DataSourceUpdateMode.OnPropertyChanged); } else { nudKarma.DataBindings.Add("Value", _skillGroup, "Karma", false, DataSourceUpdateMode.OnPropertyChanged); nudKarma.DataBindings.Add("Enabled", _skillGroup, "KarmaUnbroken", false, DataSourceUpdateMode.OnPropertyChanged); nudSkill.DataBindings.Add("Value", _skillGroup, "Base", false, DataSourceUpdateMode.OnPropertyChanged); nudSkill.DataBindings.Add("Enabled", _skillGroup, "BaseUnbroken", false, DataSourceUpdateMode.OnPropertyChanged); if (_skillGroup.Character.BuildMethod == CharacterBuildMethod.Karma || _skillGroup.Character.BuildMethod == CharacterBuildMethod.LifeModule) { nudSkill.Enabled = false; } } sw.TaskEnd("Create skillgroup"); }
internal static SkillGroup Get(Skill skill) { if (skill.SkillGroupObject != null) { return(skill.SkillGroupObject); } if (skill.SkillGroup == null) { 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); } } if (string.IsNullOrWhiteSpace(skill.SkillGroup)) { return(null); } 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)); return(newGroup); }
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); } Timekeeper.Finish("load_char_skills_normal"); Timekeeper.Start("load_char_skills_kno"); foreach (XmlNode node in skillNode.SelectNodes("knoskills/skill")) { KnowledgeSkill skill = Skill.Load(_character, node) as KnowledgeSkill; if (skill != null) { 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); } } List <Skill> unsoredSkills = new List <Skill>(); //Variable/Anon method as to not clutter anywhere else. Not sure if clever or stupid Predicate <Skill> oldSkillFilter = 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) { KnowledgeSkill knoSkill = skill as KnowledgeSkill; if (knoSkill != null) { KnowledgeSkills.Add(knoSkill); } else if (oldSkillFilter(skill)) { unsoredSkills.Add(skill); } } unsoredSkills.Sort(CompareSkills); unsoredSkills.ForEach(x => _skills.Add(x)); 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? //remove skillgroups whose skills did not make the final cut for (var i = SkillGroups.Count - 1; i >= 0; i--) { if (!SkillGroups[i].GetEnumerable().Any(x => Skills.Contains(x))) { SkillGroups.RemoveAt(i); i--; } } //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; } skillNode.TryGetBoolFieldQuickly("uneducated", ref _blnUneducated); skillNode.TryGetBoolFieldQuickly("uncouth", ref _blnUncouth); skillNode.TryGetBoolFieldQuickly("schoolofhardknocks", ref _blnSchoolOfHardKnocks); skillNode.TryGetBoolFieldQuickly("collegeeducation", ref _blnCollegeEducation); skillNode.TryGetBoolFieldQuickly("jackofalltrades", ref _blnJackOfAllTrades); skillNode.TryGetBoolFieldQuickly("techschool", ref _blnTechSchool); skillNode.TryGetBoolFieldQuickly("linguist", ref _blnLinguist); Timekeeper.Finish("load_char_skills"); }
/// <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; List <Skill> objMySkillGroupSkills = objMySkillGroup.SkillList; foreach (Skill objSkillGroupMember in objMySkillGroupSkills) { 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); }
/// <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; List <Skill> objMySkillGroupSkills = objMySkillGroup.SkillList; foreach (Skill objSkillGroupMember in objMySkillGroupSkills) { 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); }
internal void Load(XmlNode skillNode, bool legacy = false) { Timekeeper.Start("load_char_skills"); if (!legacy) { Timekeeper.Start("load_char_skills_groups"); (from XmlNode node in skillNode.SelectNodes("groups/group") let @group = SkillGroup.Load(_character, node) where @group != null orderby @group.DisplayName descending select @group).ForEach(x => SkillGroups.Add(x)); 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 = (from XmlNode node in skillNode.SelectNodes("skills/skill") let skill = Skill.Load(_character, node) where skill != null select skill).ToList(); loadingSkills.Sort(CompareSkills); foreach (Skill skill in loadingSkills) { _skills.Add(skill); } Timekeeper.Finish("load_char_skills_normal"); Timekeeper.Start("load_char_skills_kno"); List <KnowledgeSkill> knoSkills = (from XmlNode node in skillNode.SelectNodes("knoskills/skill") let skill = (KnowledgeSkill)Skill.Load(_character, node) where skill != null select skill).ToList(); foreach (KnowledgeSkill skill in knoSkills) { KnowledgeSkills.Add(skill); } Timekeeper.Finish("load_char_skills_kno"); Timekeeper.Start("load_char_knowsoft_buffer"); // Knowsoft Buffer. XmlNodeList objXmlKnowsoftBuffer = skillNode.SelectNodes("skilljackknowledgeskills/skill"); foreach (XmlNode objXmlSkill in objXmlKnowsoftBuffer) { string strName = objXmlSkill["name"].InnerText; KnowsoftSkills.Add(new KnowledgeSkill(_character, strName)); } Timekeeper.Finish("load_char_knowsoft_buffer"); } else { XmlNodeList oldskills = skillNode.SelectNodes("skills/skill"); List <Skill> tempoerySkillList = (from XmlNode node in oldskills let skill = Skill.LegacyLoad(_character, node) where skill != null select skill).ToList(); List <Skill> unsoredSkills = new List <Skill>(); //Variable/Anon method as to not clutter anywhere else. Not sure if clever or stupid Predicate <Skill> oldSkillFilter = 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 tempoerySkillList) { KnowledgeSkill knoSkill = skill as KnowledgeSkill; if (knoSkill != null) { KnowledgeSkills.Add(knoSkill); } else if (oldSkillFilter(skill)) { unsoredSkills.Add(skill); } } unsoredSkills.Sort(CompareSkills); unsoredSkills.ForEach(x => _skills.Add(x)); UpdateUndoList(skillNode); //remove skillgroups whose skills did not make the final cut for (var i = SkillGroups.Count - 1; i >= 0; i--) { if (SkillGroups[i].GetEnumerable().Any(x => Skills.Contains(x))) { continue; } SkillGroups.RemoveAt(i); } } //Workaround for probably breaking compability between earlier beta builds if (skillNode["skillptsmax"] == null) { skillNode = skillNode.OwnerDocument["character"]; } SkillPointsMaximum = Convert.ToInt32(skillNode["skillptsmax"].InnerText); SkillGroupPointsMaximum = Convert.ToInt32(skillNode["skillgrpsmax"].InnerText); skillNode.TryGetField("uneducated", out _blnUneducated); skillNode.TryGetField("uncouth", out _blnUncouth); skillNode.TryGetField("schoolofhardknocks", out _blnSchoolOfHardKnocks); skillNode.TryGetField("collegeeducation", out _blnCollegeEducation); skillNode.TryGetField("jackofalltrades", out _blnJackOfAllTrades); skillNode.TryGetField("techschool", out _blnTechSchool); skillNode.TryGetField("linguist", out _blnLinguist); Timekeeper.Finish("load_char_skills"); }
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")) { KnowledgeSkill skill = Skill.Load(_character, node) as KnowledgeSkill; if (skill != null) { 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 Predicate <Skill> oldSkillFilter = 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) { KnowledgeSkill knoSkill = skill as KnowledgeSkill; if (knoSkill != null) { 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"); }