private static void Bootup()
        {
            if (Egcb_MutableMutations.bStarted == true)
            {
                return;
            }
            Egcb_MutableMutations.bStarted             = true;
            Egcb_MutableMutations.OptionsList          = new List <string>();
            Egcb_MutableMutations.CachedOptionValues   = new Dictionary <string, string>();
            Egcb_MutableMutations.MutationData         = new List <MutationCategory>();
            Egcb_MutableMutations.MutationCategoryDict = new Dictionary <string, MutationCategory>();

            //Load mutation data from the default Mutations.xml file. Only vanilla mods are supported for now.
            //  While I considered loading data from other mods, it seems like that could
            //  cause issues if the user uninstalled one of those mods later and MutableMutations then tried to load data for that mod's mutations.
            //  A possible solution later would be to directly update the Mutations.xml file in that mod's folder, instead of generating an override
            //  (that would probably be necessary anyway because I don't think we can guarantee the order Mods are loaded.)
            Egcb_MutableMutations.LoadVanillaMutations();

            //Create our custom option list for each mutation, adding them to the game's Option list
            Egcb_MutableMutations.CreateOptionsList();

            //Generate a custom Mutations.xml file in our mod's directory. This is unfortunately necessary because the game reloads Mutations.xml
            //files every time that the player re-enters the Character Generation screen, so we can't just change the Costs in MutationFactory
            Egcb_MutableMutations.ApplyMutationOptions(true);

            //start the MonoBehavior Coroutine to poll for the options menu. We'll update Mutations.xml every time the user changes mutations options
            Egcb_MutableMutations.StartOptionsMonitor();
        }
        private static MutationCategory LoadCategoryNode(XmlTextReader stream)
        {
            MutationCategory mutationCategory = new MutationCategory
            {
                Name          = stream.GetAttribute("Name"),
                DisplayName   = stream.GetAttribute("DisplayName"),
                Help          = stream.GetAttribute("Help"),
                Stat          = stream.GetAttribute("Stat"),
                Property      = stream.GetAttribute("Property"),
                ForceProperty = stream.GetAttribute("ForceProperty")
            };

            while (stream.Read())
            {
                if (stream.Name == "mutation")
                {
                    MutationEntry mutationEntry = Egcb_MutableMutations.LoadMutationNode(stream);
                    mutationEntry.Category = mutationCategory;
                    if (!mutationEntry.Prerelease || Options.GetOption("OptionEnablePrereleaseContent", string.Empty) == "Yes")
                    {
                        mutationCategory.Entries.Add(mutationEntry);
                    }
                }
                if (stream.NodeType == XmlNodeType.EndElement && (stream.Name == string.Empty || stream.Name == "category"))
                {
                    return(mutationCategory);
                }
            }
            return(mutationCategory);
        }
 public Egcb_MutableMutations()
 {
     if (Options.OptionsByCategory.Count <= 0 || Options.OptionsByID.Count <= 0)
     {
         Debug.Log("Mutable Mutations Mod - Failed to initialize mod: Options dictionary does not exist.");
         return;
     }
     Egcb_MutableMutations.Bootup();
 }
 public static void ResetMutationCosts()
 {
     foreach (MutationCategory mc in Egcb_MutableMutations.MutationData)
     {
         foreach (MutationEntry me in mc.Entries)
         {
             //reset all mutation costs to the value specified in the core Mutations.xml file
             string opName = Egcb_MutableMutations.GetOptionNameForMutationCost(me);
             Options.SetOption(opName, Egcb_MutableMutations.GetDefaultCostForMutationValueArray(me));
         }
     }
     Debug.Log("Mutable Mutations Mod - Reset all mutation costs to default.");
 }
 //Most of LoadVanillaMutations, LoadCategoryNode, and LoadMutationNode were adapted directly from the game's MutationFactory code
 private static void LoadVanillaMutations()
 {
     Egcb_MutableMutations.MutationData.Clear();
     Egcb_MutableMutations.MutationCategoryDict.Clear();
     using (XmlTextReader stream = DataManager.GetStreamingAssetsXMLStream("Mutations.xml"))
     {
         stream.WhitespaceHandling = WhitespaceHandling.None;
         while (stream.Read())
         {
             if (stream.Name == "mutations")
             {
                 while (stream.Read())
                 {
                     if (stream.Name == "category")
                     {
                         MutationCategory mutationCategory = Egcb_MutableMutations.LoadCategoryNode(stream);
                         if (mutationCategory.Name[0] == '-')
                         {
                             if (Egcb_MutableMutations.MutationCategoryDict.ContainsKey(mutationCategory.Name.Substring(1)))
                             {
                                 MutationCategory cat = MutationFactory.CategoriesByName[mutationCategory.Name.Substring(1)];
                                 Egcb_MutableMutations.MutationCategoryDict.Remove(mutationCategory.Name.Substring(1));
                                 Egcb_MutableMutations.MutationData.Remove(cat);
                             }
                         }
                         else if (Egcb_MutableMutations.MutationCategoryDict.ContainsKey(mutationCategory.Name))
                         {
                             Egcb_MutableMutations.MutationCategoryDict[mutationCategory.Name].MergeWith(mutationCategory);
                         }
                         else
                         {
                             Egcb_MutableMutations.MutationCategoryDict.Add(mutationCategory.Name, mutationCategory);
                             Egcb_MutableMutations.MutationData.Add(mutationCategory);
                         }
                     }
                     if (stream.NodeType == XmlNodeType.EndElement && (stream.Name == string.Empty || stream.Name == "mutations"))
                     {
                         break;
                     }
                 }
             }
             if (stream.NodeType == XmlNodeType.EndElement && (stream.Name == string.Empty || stream.Name == "mutations"))
             {
                 break;
             }
         }
         stream.Close();
     }
 }
 public static void ReapplyOptions()
 {
     //the user selected the option to reset all mutation costs to default
     if (Options.GetOption(Egcb_MutableMutations.ResetMutationValueOptionID) == "Yes")
     {
         Options.SetOption(Egcb_MutableMutations.ResetMutationValueOptionID, "No");
         Egcb_MutableMutations.ResetMutationCosts();
         Egcb_MutableMutations.ApplyMutationOptions(false);
     }
     //check if any mutation cost settings were changed, and if so, rebuild Mutations.xml
     else if (!Egcb_MutableMutations.ValidateCache())
     {
         Egcb_MutableMutations.ApplyMutationOptions(false);
     }
 }
        private static void CreateOptionsList()
        {
            Egcb_MutableMutations.OptionsList.Clear();
            //create a reset option to restore game defaults
            GameOption resetOp = new GameOption();

            resetOp.ID          = Egcb_MutableMutations.ResetMutationValueOptionID;
            resetOp.DisplayText = "Reset all mutation costs to default (after closing Options)";
            resetOp.Category    = Egcb_MutableMutations.OptionsCategoryName;
            resetOp.Type        = "Checkbox";
            resetOp.Default     = "No";
            Egcb_MutableMutations.OptionsList.Add(resetOp.ID);
            Egcb_MutableMutations.AddNewGameOption(resetOp);
            foreach (MutationCategory mc in Egcb_MutableMutations.MutationData)
            {
                string mutationCategoryName = ConsoleLib.Console.ColorUtility.StripFormatting(mc.DisplayName).TrimEnd('s');
                foreach (MutationEntry me in mc.Entries)
                {
                    string optionName = Egcb_MutableMutations.GetOptionNameForMutationCost(me);
                    Egcb_MutableMutations.OptionsList.Add(optionName);
                    if (Options.OptionsByID.ContainsKey(optionName) || me.Cost == 0) //also skip 0 cost vanilla entries, if they exist, because we won't really know what Values range to use
                    {
                        continue;
                    }
                    //option doesn't exist, so we'll create it
                    GameOption gameOption = new GameOption();
                    gameOption.ID          = optionName;
                    gameOption.DisplayText = Egcb_MutableMutations.GetMutationCostOptionDisplayText(me);
                    gameOption.Category    = Egcb_MutableMutations.OptionsCategoryName;
                    gameOption.Type        = "Combo";
                    gameOption.Values      = Egcb_MutableMutations.GetCostValueArrayForMutation(me);
                    gameOption.Default     = Egcb_MutableMutations.GetDefaultCostForMutationValueArray(me);
                    //add to game Options
                    Egcb_MutableMutations.AddNewGameOption(gameOption);
                }
            }
        }
        private static void ApplyMutationOptions(bool bInitialLoad)
        {
            if (bInitialLoad)
            {
                Debug.Log("Mutable Mutations Mod - Generating initial mutation cost settings...");
            }
            else
            {
                Debug.Log("Mutable Mutations Mod - Regenerating mutation cost settings...");
            }
            Egcb_MutableMutations.CachedOptionValues.Clear();
            using (StreamWriter xmlStream = new StreamWriter(Path.Combine(Egcb_MutableMutations.ModDirectory, "Mutations.xml"), false))
            {
                XmlWriterSettings xmlSettings = new XmlWriterSettings {
                    Indent = true
                };
                using (XmlWriter xmlWriter = XmlWriter.Create(xmlStream, xmlSettings))
                {
                    xmlWriter.WriteStartDocument();
                    xmlWriter.WriteStartElement("mutations");
                    foreach (MutationCategory mc in Egcb_MutableMutations.MutationData)
                    {
                        xmlWriter.WriteStartElement("category");
                        xmlWriter.WriteAttributeString("Name", mc.Name);
                        xmlWriter.WriteAttributeString("DisplayName", mc.DisplayName);
                        if (!String.IsNullOrEmpty(mc.Help))
                        {
                            xmlWriter.WriteAttributeString("Help", mc.Help);
                        }
                        if (!String.IsNullOrEmpty(mc.Stat))
                        {
                            xmlWriter.WriteAttributeString("Stat", mc.Stat);
                        }
                        if (!String.IsNullOrEmpty(mc.Property))
                        {
                            xmlWriter.WriteAttributeString("Property", mc.Property);
                        }
                        if (!String.IsNullOrEmpty(mc.ForceProperty))
                        {
                            xmlWriter.WriteAttributeString("ForceProperty", mc.ForceProperty);
                        }
                        foreach (MutationEntry me in mc.Entries)
                        {
                            xmlWriter.WriteStartElement("mutation");
                            string mutationOptionName = Egcb_MutableMutations.GetOptionNameForMutationCost(me);
                            string mutationOptionCost = Options.GetOption(mutationOptionName, String.Empty).Trim();
                            mutationOptionCost = (String.IsNullOrEmpty(mutationOptionCost) ? me.Cost.ToString() : mutationOptionCost);
                            Egcb_MutableMutations.CachedOptionValues.Add(mutationOptionName, mutationOptionCost);

                            xmlWriter.WriteAttributeString("Name", me.DisplayName);
                            xmlWriter.WriteAttributeString("Cost", mutationOptionCost);
                            if (!String.IsNullOrEmpty(me.Stat))
                            {
                                xmlWriter.WriteAttributeString("Stat", me.Stat);
                            }
                            if (me.Maximum != -999)
                            {
                                xmlWriter.WriteAttributeString("MaxSelected", me.Maximum.ToString());
                            }
                            xmlWriter.WriteAttributeString("Class", me.Class);
                            if (!String.IsNullOrEmpty(me.Constructor))
                            {
                                xmlWriter.WriteAttributeString("Constructor", me.Constructor);
                            }
                            xmlWriter.WriteAttributeString("Exclusions", me.Exclusions);
                            if (me.MaxLevel != -999)
                            {
                                xmlWriter.WriteAttributeString("MaxLevel", me.MaxLevel.ToString());
                            }
                            if (!String.IsNullOrEmpty(me.Property))
                            {
                                xmlWriter.WriteAttributeString("Property", me.Property);
                            }
                            if (!String.IsNullOrEmpty(me.ForceProperty))
                            {
                                xmlWriter.WriteAttributeString("ForceProperty", me.ForceProperty);
                            }
                            if (!String.IsNullOrEmpty(me.BearerDescription))
                            {
                                xmlWriter.WriteAttributeString("BearerDescription", me.BearerDescription);
                            }
                            xmlWriter.WriteAttributeString("Code", me.MutationCode);
                            if (me.Prerelease == true)
                            {
                                xmlWriter.WriteAttributeString("Prerelease", "true");
                            }
                            xmlWriter.WriteFullEndElement(); //important to write FullEndElement, game can't parse self-closing element in Mutations.xml yet
                        }
                        xmlWriter.WriteFullEndElement();
                    }
                    xmlWriter.WriteEndDocument();
                    xmlWriter.Flush();
                    xmlWriter.Close();
                }
                xmlStream.Flush();
                xmlStream.Close();
            }
        }