/// <summary> /// Analyzes an attribute and returns whether or not it is valid. /// </summary> /// <param name="character">The character to analyze.</param> /// <param name="attribute">The characters attribute to check.</param> /// <param name="dependency">The dependency to check with.</param> /// <returns>A string array containing useful error messages. If empty, no errors were found.</returns> public static string[] ValidateAttribute(Character character, NumAttribute attribute) { List <string> errors = new List <string>(); attribute.setMax(FindHighestPossible(character, attribute)); attribute.setMin(FindLowestPossible(character, attribute)); if (attribute.getMax() < attribute.getMin()) { errors.Add(attribute.Name + " has a higher minimum value than its maximum value, meaning it can never be correct."); } foreach (NumDependency d in attribute.Dependancies) { if (d.type == Operand.QuotiantOf) { if (d.v2IsRef) { if (Zeroable(character, (NumAttribute)character.Attributes[d.v2Ref])) { errors.Add(attribute.Name + " is derived by dividing by zero, or could potentially be derived by dividing by zero.\n" + "Ensure that no attributes depended upon by " + attribute.Name + " can potentially be zero."); } } else if (d.Value2 == 0) { errors.Add(attribute.Name + " is derived by dividing by zero, or could potentially be derived by dividing by zero.\n" + "Ensure that no attributes depended upon by " + attribute.Name + " can potentially be zero."); } } } return(errors.ToArray()); }
/// <summary> /// Takes an instance of character and a specific attribute within that character, and returns the highest value that attribute can potentially have. /// </summary> /// <param name="character">The instance of character your attribute is stored within.</param> /// <param name="attribute">The attribute you are validating</param> /// <returns>The highest number the attribute is allowed to be based on its dependencies</returns> private static decimal FindHighestPossible(Character character, NumAttribute attribute) { decimal highestPossible = decimal.MaxValue; //This method is recursive. To save on memory, if it runs once it'll store the value it returns in the attribute's max value. //If this method is called on an attribute it has already been called on, it will simply return the already stored max value. //In order to ensure we can validate attributes when a player updates them, we should set max and min values to null in any attribute that is edited. if (attribute.getMax() != null) { highestPossible = (decimal)attribute.getMax(); } else { foreach (NumDependency d in attribute.Dependancies) { // >Inb4 longest switch statement I've ever written. // >It's super ugly, but super functional. // >I feel fulfilled and disgusted simultaneously // >Mfw: http://i.imgur.com/c65CEFK.png switch (d.type) { case Operand.Equals: if (d.v1IsRef) { highestPossible = FindHighestPossible(character, (NumAttribute)character.Attributes[d.v1Ref]); } else { highestPossible = d.Value1; } attribute.setMax(highestPossible); break; case Operand.DifferenceOf: decimal x = d.v1IsRef ? FindHighestPossible(character, (NumAttribute)character.Attributes[d.v1Ref]) : d.Value1; decimal y = d.v2IsRef ? FindHighestPossible(character, (NumAttribute)character.Attributes[d.v2Ref]) : d.Value2; highestPossible = x - y; attribute.setMax(highestPossible); break; case Operand.LessThan: case Operand.LessOrEqualTo: if (d.v1IsRef) { NumAttribute n = (NumAttribute)character.Attributes[d.v1Ref]; highestPossible = FindHighestPossible(character, (NumAttribute)character.Attributes[d.v1Ref]) - (d.type == Operand.LessOrEqualTo ? 0 : 1); } else { highestPossible = d.Value1 - (d.type == Operand.LessOrEqualTo ? 0 : 1); } break; case Operand.ProductOf: x = d.v1IsRef ? FindHighestPossible(character, (NumAttribute)character.Attributes[d.v1Ref]) : d.Value1; y = d.v2IsRef ? FindHighestPossible(character, (NumAttribute)character.Attributes[d.v2Ref]) : d.Value2; highestPossible = x * y; attribute.setMax(highestPossible); break; case Operand.QuotiantOf: x = d.v1IsRef ? FindHighestPossible(character, (NumAttribute)character.Attributes[d.v1Ref]) : d.Value1; y = d.v2IsRef ? FindHighestPossible(character, (NumAttribute)character.Attributes[d.v2Ref]) : d.Value2; highestPossible = x / y; attribute.setMax(highestPossible); break; case Operand.SumOf: x = d.v1IsRef ? FindHighestPossible(character, (NumAttribute)character.Attributes[d.v1Ref]) : d.Value1; y = d.v2IsRef ? FindHighestPossible(character, (NumAttribute)character.Attributes[d.v2Ref]) : d.Value2; highestPossible = x + y; attribute.setMax(highestPossible); break; } } } attribute.setMax(highestPossible); return(highestPossible); }