static void SimpleApplyModifier(NetDictionary <DBReference <Tag>, FInt> dict, Tag source, Tag target, FInt change, Dictionary <Tag, float> multiplier, List <Multitype <long, Tag, FInt, bool> > changelog) { FInt changeMP = change * multiplier.MHGetValueOrDefault(target, 1f); if (dict.ContainsKey(target)) { dict[target] += changeMP; } else { dict[target] = changeMP; } if (changelog != null) { changelog.Add(new Multitype <long, Tag, FInt, bool>(DescriptionInfoUtils.GetInfoID(source.descriptionInfo), target, change, false)); } }
/// <summary> /// post processing attributes to ensure ability to influence some statistics can increase other. /// In effect this produces "Final" attributes which are result of "Base" attributes plus "Changes" /// Attribute Process: /// Base - permanent attributes of the item/character, which are not recalculated often, but can be change in game /// Changes - modifiers applied to the base. Eg traits, effects, item bonuses... /// Final - AttributesProcessing function which ensures there is a way to post process special connections between tags. /// </summary> /// <param name="a"></param> /// <returns></returns> static public object AttributesProcessing(Attributes a, Character c, bool returnChangelog) { a.finalAttributes = new NetDictionary <DBReference <Tag>, FInt>(); //from, to, how much change occurred. This is so that it can be later processed //for additional information on "extra info" popups //string - ID of the skill or item which influenced change //Tag - attribute changed by the process //FInt - amount of the change List <Multitype <long, Tag, FInt, bool> > changelog = null; if (returnChangelog) { changelog = new List <Multitype <long, Tag, FInt, bool> >(); } foreach (var pair in a.baseAttributes) { a.finalAttributes[pair.Key] = pair.Value; } Dictionary <Tag, FInt> addons = new Dictionary <Tag, FInt>(); Dictionary <Tag, float> multipliers = new Dictionary <Tag, float>(); if (c != null) { //this is a character therefore trigger passive skills which influence attributes //Process skills in slots foreach (var v in c.learnedSkills) { foreach (var k in v.source.Get().skillSubskills) { if (k.trigger.triggerGroup == ETriggerGroupType.Passive) { Globals.CallFunction(k.activation.scriptName, v.GetCurrentSkillAttributes()[k], addons, multipliers, DescriptionInfoUtils.GetInfoID(v.source.Get().descriptionInfo), changelog, c); } } } //Process skills in effects on character foreach (var v in c.effects) { foreach (var k in v.source.Get().skillSubskills) { if (k.trigger.triggerGroup == ETriggerGroupType.Passive) { Globals.CallFunction(k.activation.scriptName, v.GetCurrentSkillAttributes()[k], addons, multipliers, DescriptionInfoUtils.GetInfoID(v.source.Get().descriptionInfo), changelog, c); } } } //Process skills from items foreach (SkillInstance v in c.equipmentEffects) { foreach (Subskill k in v.source.Get().skillSubskills) { if (k.trigger.triggerGroup == ETriggerGroupType.Passive) { // string itemID = null; // foreach (var i in c.equipment) // { // if (i != null && // i.Get() != null && // i.Get().skill == v) // { // itemID = i.Get().GetDescriptionInfo().GetInfoID().ToString(); // break; // } // } Globals.CallFunction(k.activation.scriptName, v.GetCurrentSkillAttributes()[k], addons, multipliers, DescriptionInfoUtils.GetInfoID(v.source.Get().descriptionInfo), changelog, c); } } } foreach (var add in addons) { if (a.finalAttributes.ContainsKey(add.Key)) { a.finalAttributes[add.Key] += add.Value; } else { a.finalAttributes[add.Key] = add.Value; } } foreach (var mul in multipliers) { if (a.finalAttributes.ContainsKey(mul.Key)) { a.finalAttributes[mul.Key] = a.finalAttributes[mul.Key] * mul.Value; } } } //main attributes to secondary conversion //NOTE: attributes which gets processing should not be parent tags for any other tag as they are //add for this reason only at late stage Tag tag = Globals.GetInstanceFromDB <Tag>(TAG.WITS); if (a.finalAttributes.ContainsKey(tag)) { FInt wits = a.finalAttributes[tag]; //use special function to calculate delay FInt delay; if (wits.ToFloat() <= 16f) { delay = 4f - (wits / 4f); } else { // - Sqrt( x/2 - 8) / 2 //delay 0 at wits 16, //delay -1 at wits 24, = sqrt(4) / 2 //delay -2 at wits 48, = sqrt(16) / 2 //delay -3 at wits 88, = sqrt(36) / 2 //delay -4 at wits 144, = sqrt(64) / 2 //delay -5 at wits 216, = sqrt(100) / 2 delay = (FInt)Mathf.Pow(wits.ToFloat() * 0.5f - 8f, 0.5f) * (-0.5f); } SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.DELAY), delay, multipliers, changelog); } else { //write down without calculation if wits are not on character attribute list SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.DELAY), new FInt(4), multipliers, changelog); } tag = Globals.GetInstanceFromDB <Tag>(TAG.STRENGTH); if (a.finalAttributes.ContainsKey(tag)) { FInt value = a.finalAttributes[tag]; SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.PERSONAL_CARRY_LIMIT), GameplayUtils.GetCarryValue(value.ToInt()), multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.MAX_HEALTH_PHYSICAL), 1f * value, multipliers, changelog); } tag = Globals.GetInstanceFromDB <Tag>(TAG.PERCEPTION); if (a.finalAttributes.ContainsKey(tag)) { FInt value = a.finalAttributes[tag]; SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.MAX_HEALTH_PHYSICAL), 0.5f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.GATHERING), 0.4f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.LUCK), 0.2f * value, multipliers, changelog); } tag = Globals.GetInstanceFromDB <Tag>(TAG.INTELLIGENCE); if (a.finalAttributes.ContainsKey(tag)) { FInt value = a.finalAttributes[tag]; SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.MAX_HEALTH_MENTAL), 1f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.RESEARCH), 0.4f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.CRAFTING), 0.2f * value, multipliers, changelog); } tag = Globals.GetInstanceFromDB <Tag>(TAG.WISDOM); if (a.finalAttributes.ContainsKey(tag)) { FInt value = a.finalAttributes[tag]; SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.MAX_HEALTH_MENTAL), 0.5f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.LUCK), 0.1f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.RITUALS), 0.2f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.CRAFTING), 0.4f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.GATHERING), 0.2f * value, multipliers, changelog); } tag = Globals.GetInstanceFromDB <Tag>(TAG.MYSTICISM); if (a.finalAttributes.ContainsKey(tag)) { FInt value = a.finalAttributes[tag]; SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.MAX_HEALTH_SPIRIT), 1f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.RESEARCH), 0.2f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.RITUALS), 0.4f * value, multipliers, changelog); } tag = Globals.GetInstanceFromDB <Tag>(TAG.DESTINY); if (a.finalAttributes.ContainsKey(tag)) { FInt value = a.finalAttributes[tag]; SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.MAX_HEALTH_SPIRIT), .5f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.GATHERING), 0.1f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.RESEARCH), 0.1f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, (Tag)TAG.LUCK, 0.4f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.RITUALS), 0.1f * value, multipliers, changelog); SimpleApplyModifier(a.finalAttributes, tag, Globals.GetInstanceFromDB <Tag>(TAG.CRAFTING), 0.1f * value, multipliers, changelog); } //Apply all values to their parents so that they can be sampled easy later //we are using presorted list which ensures that we will process leafs first, therefore //no parent would be processed before its child to ensure that parent can influence //its own parents including this bonus without double processing List <Tag> tags = DBTypeUtils <Tag> .GetParentSortedType(); foreach (Tag t in tags) { if (a.finalAttributes.ContainsKey(t)) { FInt bonus = a.finalAttributes[t]; HashSet <Tag> parents = DBTypeUtils <Tag> .GetParentsOf(t); foreach (Tag parent in parents) { if (parent == null) { Debug.LogError("[ERROR]" + parent); } if (a.finalAttributes.ContainsKey(parent)) { a.finalAttributes[parent] += bonus; } else { a.finalAttributes[parent] = bonus; } } } } if (c != null) { c.MaxCarry = a.finalAttributes[(Tag)TAG.PERSONAL_CARRY_LIMIT]; } a.ClampAndScaleHP(); a.ClearDirty(); return(changelog); }