// I wrote this for animation but it turns out it isn't going to work how I expected //public static StateSave CombineBaseValuesAndClone(this StateSave stateSave) //{ // StateSave cloned = new StateSave(); // if (stateSave.ParentContainer == null) // { // // This thing doesn't have a parent container so we have no idea how to get the default and follow inheritance // cloned = stateSave.Clone(); // } // else // { // ElementSave parent = stateSave.ParentContainer; // if (parent.DefaultState == stateSave) // { // if (string.IsNullOrEmpty(parent.BaseType)) // { // cloned = stateSave.Clone(); // } // else // { // ElementSave baseOfParent = ObjectFinder.Self.GetElementSave(parent.BaseType); // if (baseOfParent == null) // { // cloned = stateSave.Clone(); // } // else // { // cloned = baseOfParent.DefaultState.CombineBaseValuesAndClone(); // cloned.MergeIntoThis(stateSave); // } // } // } // else // { // cloned = parent.DefaultState.CombineBaseValuesAndClone(); // cloned.MergeIntoThis(stateSave); // } // } // return cloned; //} public static void Merge(StateSave firstState, StateSave secondState, float otherRatio, List <VariableSaveValues> mergedValues) { #if DEBUG if (firstState == null || secondState == null) { throw new ArgumentNullException("States must not be null"); } #endif foreach (var secondVariable in secondState.Variables) { object secondValue = secondVariable.Value; VariableSave firstVariable = firstState.GetVariableSave(secondVariable.Name); // If this variable doesn't have a value, or if the variable doesn't set the variable // then we need to go recursive to see what the value is: bool needsValueFromBase = firstVariable == null || firstVariable.SetsValue == false; bool setsValue = secondVariable.SetsValue; object firstValue = null; if (firstVariable == null) { firstValue = secondVariable.Value; // Get the value recursively before adding it to the list if (needsValueFromBase) { var variableOnThis = firstState.GetVariableSave(secondVariable.Name); if (variableOnThis != null) { setsValue |= variableOnThis.SetsValue; } firstValue = firstState.GetValueRecursive(secondVariable.Name); } } else { firstValue = firstVariable.Value; } if (setsValue) { object interpolated = GetValueConsideringInterpolation(firstValue, secondValue, otherRatio); VariableSaveValues value = new VariableSaveValues(); value.Name = secondVariable.Name; value.Value = interpolated; mergedValues.Add(value); } } // todo: Handle lists? }
public static VariableSave GetVariableRecursive(this StateSave stateSave, string variableName) { VariableSave variableSave = stateSave.GetVariableSave(variableName); if (variableSave == null) { // Is this thing the default? ElementSave parent = stateSave.ParentContainer; if (parent != null && stateSave != parent.DefaultState) { variableSave = stateSave.GetVariableSave(variableName); if (variableSave == null) { variableSave = parent.DefaultState.GetVariableSave(variableName); } } if (variableSave == null && parent != null) { ElementSave baseElement = GetBaseElementFromVariable(variableName, parent); if (baseElement != null) { string nameInBase = variableName; if (variableName.Contains('.')) { // this variable is set on an instance, but we're going into the // base type, so we want to get the raw variable and not the variable // as tied to an instance. nameInBase = variableName.Substring(nameInBase.IndexOf('.') + 1); } return(baseElement.DefaultState.GetVariableRecursive(nameInBase)); } } return(variableSave); } else { return(variableSave); } }
public static void MergeIntoThis(this StateSave thisState, StateSave other, float otherRatio = 1) { #if DEBUG if (other == null) { throw new ArgumentNullException("other Statesave is null and it shouldn't be"); } #endif foreach (var variableSave in other.Variables) { // The first will use its default if one doesn't exist VariableSave whatToSet = thisState.GetVariableSave(variableSave.Name); // If this variable doesn't have a value, or if the variable doesn't set the variable // then we need to go recursive to see what the value is: bool needsValueFromBase = whatToSet == null || whatToSet.SetsValue == false; bool setsValue = variableSave.SetsValue; if (whatToSet == null) { whatToSet = variableSave.Clone(); // Get the value recursively before adding it to the list if (needsValueFromBase) { var variableOnThis = thisState.GetVariableSave(variableSave.Name); if (variableOnThis != null) { setsValue |= variableOnThis.SetsValue; } whatToSet.Value = thisState.GetValueRecursive(variableSave.Name); } thisState.Variables.Add(whatToSet); } whatToSet.SetsValue = setsValue; whatToSet.Value = GetValueConsideringInterpolation(whatToSet.Value, variableSave.Value, otherRatio); } // todo: Handle lists? }
public static void SubtractFromThis(this StateSave thisState, StateSave other) { foreach (var variableSave in other.Variables) { // The first will use its default if one doesn't exist VariableSave whatToSet = thisState.GetVariableSave(variableSave.Name); if (whatToSet != null && (whatToSet.SetsValue || variableSave.SetsValue)) { whatToSet.SetsValue = true; whatToSet.Value = SubtractValue(whatToSet, variableSave); } } // todo: Handle lists? }
/// <summary> /// Assigns a value to a variable. If the variable doesn't exist then the variable is instantiated, then the value is assigned. /// </summary> /// <param name="stateSave">The StateSave that contains the variable. The variable will be added to this StateSave if it doesn't exist.</param> /// <param name="variableName">The name of the variable to look for.</param> /// <param name="value">The value to assign to the variable.</param> /// <param name="instanceSave">The instance that owns this variable. This may be null.</param> /// <param name="variableType">The type of the variable. This is only needed if the value is null.</param> private static VariableSave AssignVariableSave(this StateSave stateSave, string variableName, object value, InstanceSave instanceSave, string variableType = null, bool isFile = false) { // Not a reserved variable, so use the State's variables VariableSave variableSave = stateSave.GetVariableSave(variableName); if (variableSave == null) { variableSave = new VariableSave(); // If the variableType is not null, give it priority if (!string.IsNullOrEmpty(variableType)) { variableSave.Type = variableType; } else if (value is bool) { variableSave.Type = "bool"; } else if (value is float) { variableSave.Type = "float"; } else if (value is int) { variableSave.Type = "int"; } else if (value is int?) { variableSave.Type = "int?"; } // account for enums else if (value is string) { variableSave.Type = "string"; } else if (value == null) { variableSave.Type = variableType; } else { variableSave.Type = value.GetType().ToString(); } variableSave.IsFile = isFile; variableSave.Name = variableName; stateSave.Variables.Add(variableSave); } // There seems to be // two ways to indicate // that a variable has a // source object. One is // to pass a InstanceSave to // this method, another is to // include a '.' in the name. If // an instanceSave is passed, then // a dot MUST be present. I don't think // we allow a dot to exist without a variable // representing a variable on an instance save, // so I'm not sure why we even require an InstanceSave. // Also, it seems like code (especially plugins) may not // know to pass an InstanceSave and may assume that the dot // is all that's needed. If so, we shouldn't be strict and require // a non-null InstanceSave. //if (instanceSave != null) // Update: We used to only check this when first creating a Variable, but // there's no harm in forcing the source object. Let's do that. // Update: Turns out we do need the instance so that we can get the base type // to find out if the variable IsFile or not. If the InstanceSave is null, but // we have a sourceObjectName that we determine by the presence of a dot, then let's // try to find the InstanceSave if (StringFunctions.ContainsNoAlloc(variableName, '.')) { string rootName = variableSave.Name.Substring(variableSave.Name.IndexOf('.') + 1); string sourceObjectName = variableSave.Name.Substring(0, variableSave.Name.IndexOf('.')); if (instanceSave == null && stateSave.ParentContainer != null) { instanceSave = stateSave.ParentContainer.GetInstance(sourceObjectName); } //ElementSave baseElement = ObjectFinder.Self.GetRootStandardElementSave(instanceSave); //VariableSave baseVariableSave = baseElement.DefaultState.GetVariableSave(rootName); if (instanceSave != null) { // can we get this from the base element? var instanceBase = ObjectFinder.Self.GetElementSave(instanceSave); bool found = false; if (instanceBase != null) { VariableSave baseVariableSave = instanceBase.DefaultState.Variables.FirstOrDefault(item => item.ExposedAsName == rootName || item.Name == rootName); if (baseVariableSave != null) { variableSave.IsFile = baseVariableSave.IsFile; found = true; } } if (!found) { VariableSave baseVariableSave = ObjectFinder.Self.GetRootStandardElementSave(instanceSave).DefaultState.GetVariableSave(rootName); if (baseVariableSave != null) { variableSave.IsFile = baseVariableSave.IsFile; } } } } variableSave.SetsValue = true; variableSave.Value = value; return(variableSave); }
public static void SetValue(this StateSave stateSave, string variableName, object value, InstanceSave instanceSave = null, string variableType = null) { bool isReservedName = TrySetReservedValues(stateSave, variableName, value, instanceSave); VariableSave variableSave = stateSave.GetVariableSave(variableName); var coreVariableDefinition = stateSave.GetVariableRecursive(variableName); string exposedVariableSourceName = null; if (!string.IsNullOrEmpty(coreVariableDefinition?.ExposedAsName) && instanceSave == null) { exposedVariableSourceName = coreVariableDefinition.Name; } string rootName = variableName; if (StringFunctions.ContainsNoAlloc(variableName, '.')) { rootName = variableName.Substring(variableName.IndexOf('.') + 1); } if (!isReservedName) { bool isFile = false; // Why might instanceSave be null? // The reason is because StateSaves // are used both for actual game data // as well as temporary variable containers. // If a StateSave is a temporary container then // instanceSave may (probably will be) null. if (instanceSave != null) { VariableSave temp = variableSave; if (variableSave == null) { temp = new VariableSave(); temp.Name = variableName; } isFile = temp.GetIsFileFromRoot(instanceSave); } else { VariableSave temp = variableSave; if (variableSave == null) { temp = new VariableSave(); temp.Name = variableName; } isFile = temp.GetIsFileFromRoot(stateSave.ParentContainer); } if (value != null && value is IList) { stateSave.AssignVariableListSave(variableName, value, instanceSave); } else { variableSave = stateSave.AssignVariableSave(variableName, value, instanceSave, variableType, isFile); variableSave.IsFile = isFile; if (!string.IsNullOrEmpty(exposedVariableSourceName)) { variableSave.ExposedAsName = variableName; variableSave.Name = exposedVariableSourceName; } stateSave.Variables.Sort((first, second) => first.Name.CompareTo(second.Name)); } if (isFile && value is string && !FileManager.IsRelative((string)value)) { string directoryToMakeRelativeTo = FileManager.GetDirectory(ObjectFinder.Self.GumProjectSave.FullFileName); const bool preserveCase = true; value = FileManager.MakeRelative((string)value, directoryToMakeRelativeTo, preserveCase); // re-assign the value using the relative name now var assignedVariable = stateSave.AssignVariableSave(variableName, value, instanceSave, variableType, isFile); } } }
/// <summary> /// Returns the first instance of an existing VariableSave recursively. /// </summary> /// <param name="stateSave">The possible state that contains the variable. If it doesn't, then the code will recursively go to base types.</param> /// <param name="variableName"></param> /// <returns></returns> public static VariableSave GetVariableRecursive(this StateSave stateSave, string variableName) { VariableSave variableSave = stateSave.GetVariableSave(variableName); if (variableSave == null) { // 1. Go to the default state if it's not a default bool shouldGoToDefaultState = false; // 2. Go to the base type if the variable is on the container itself, or if the instance is DefinedByBase bool shouldGoToBaseType = false; // 3. Go to the instance if it's on an instance and we're not going to the default state or base type bool shouldGoToInstanceComponent = false; // Is this thing the default? ElementSave elementContainingState = stateSave.ParentContainer; if (elementContainingState != null) { if (elementContainingState != null && stateSave != elementContainingState.DefaultState) { shouldGoToDefaultState = true; } var isVariableOnInstance = variableName.Contains('.'); InstanceSave instance = null; bool canGoToBase = false; var hasBaseType = !string.IsNullOrEmpty(elementContainingState.BaseType); var isVariableDefinedOnThisInheritanceLevel = false; var instanceName = VariableSave.GetSourceObject(variableName); instance = elementContainingState.Instances.FirstOrDefault(item => item.Name == instanceName); if (isVariableOnInstance && hasBaseType) { if (instance != null && instance.DefinedByBase == false) { isVariableDefinedOnThisInheritanceLevel = true; } } else if (!hasBaseType) { isVariableDefinedOnThisInheritanceLevel = true; } canGoToBase = isVariableOnInstance == false || isVariableDefinedOnThisInheritanceLevel == false; if (!shouldGoToDefaultState) { shouldGoToBaseType = canGoToBase; } if (!shouldGoToDefaultState && !shouldGoToBaseType) { shouldGoToInstanceComponent = isVariableOnInstance; } if (shouldGoToDefaultState) { variableSave = elementContainingState.DefaultState.GetVariableSave(variableName); if (variableSave == null) { shouldGoToBaseType = canGoToBase; } } if (shouldGoToBaseType) { var baseElement = ObjectFinder.Self.GetElementSave(elementContainingState.BaseType); if (baseElement != null) { variableSave = baseElement.DefaultState.GetVariableRecursive(variableName); } } else if (shouldGoToInstanceComponent) { ElementSave instanceElement = null; if (instance != null) { instanceElement = ObjectFinder.Self.GetElementSave(instance); } if (instanceElement != null) { variableSave = instanceElement.DefaultState.GetVariableRecursive(VariableSave.GetRootName(variableName)); } } } } return(variableSave); }
public static void SetValue(this StateSave stateSave, string variableName, object value, InstanceSave instanceSave = null, string variableType = null) { bool isReservedName = TrySetReservedValues(stateSave, variableName, value, instanceSave); VariableSave variableSave = stateSave.GetVariableSave(variableName); string exposedVariableSourceName = null; string rootName = variableName; if (variableName.Contains('.')) { rootName = variableName.Substring(variableName.IndexOf('.') + 1); } else if (stateSave.ParentContainer != null && stateSave.ParentContainer.DefaultState != stateSave) { // This isn't the default state, so let's ask the default state if this is an exposed variable... var defaultState = stateSave.ParentContainer.DefaultState; var found = defaultState.Variables.FirstOrDefault(item => item.ExposedAsName == variableName); if (found != null) { exposedVariableSourceName = found.Name; } } if (!isReservedName) { if (value != null && value is IList) { stateSave.AssignVariableListSave(variableName, value, instanceSave); } else { variableSave = stateSave.AssignVariableSave(variableName, value, instanceSave, variableType); if (!string.IsNullOrEmpty(exposedVariableSourceName)) { variableSave.ExposedAsName = variableName; variableSave.Name = exposedVariableSourceName; } stateSave.Variables.Sort((first, second) => first.Name.CompareTo(second.Name)); } bool isFile = false; // Why might instanceSave be null? // The reason is because StateSaves // are used both for actual game data // as well as temporary variable containers. // If a StateSave is a temporary container then // instanceSave may (probably will be) null. if (instanceSave != null) { isFile = variableSave.GetIsFileFromRoot(instanceSave); } else if (variableSave != null) { isFile = variableSave.IsFile; } if (isFile && value is string && !FileManager.IsRelative((string)value)) { string directoryToMakeRelativeTo = FileManager.GetDirectory(ObjectFinder.Self.GumProjectSave.FullFileName); const bool preserveCase = true; value = FileManager.MakeRelative((string)value, directoryToMakeRelativeTo, preserveCase); // re-assign the value using the relative name now stateSave.AssignVariableSave(variableName, value, instanceSave, variableType); } } }