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)); }
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); }
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); }
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); }
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"); }
/// <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); }
/// <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); }
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"); }