public static void TechTreeSimulation(User user) { var info = new InfoBuilder(); var data = new TechTreeSimData(); info.AppendLineLocStr("Starting Tech Tree Sim"); info.AppendLineLocStr("Getting Starting Skills..."); var introSkills = PlayerDefaults.GetDefaultSkills().ToList(); introSkills.ForEach(x => data.AddSkill(x)); info.AppendLineLocStr("Getting Initial Tables..."); data.CraftingTables.Add(typeof(CampsiteObject)); info.AppendLineLocStr("Getting Recipes with No Skills..."); data.UnusableRecipes.AddRange(RecipeFamily.AllRecipes.SelectMany(x => x.Recipes).Where(x => !x.Family.RequiredSkills.Any() && x.Ingredients.Any())); info.AppendLineLocStr("Getting World Resources..."); //manually defined for now since theres no easy way of checking for these data.AddItems(new List <Type> { typeof(DirtItem), typeof(SandItem), typeof(IronOreItem), typeof(CopperOreItem), typeof(GoldOreItem), typeof(CoalItem), typeof(ClayItem) }); // Add all items with "Rock" tag data.AddItems(TagManager.TagToTypes[TagManager.Tag("Rock")]); info.AppendLineLocStr("Getting Species Resources..."); var resourceless = new List <Species>(); EcoSim.AllSpecies.ForEach(x => { var speciesInfo = new InfoBuilder(); AddNewResources(speciesInfo, x.ResourceList.Select(x => x.ResourceType), data); if (x is TreeSpecies treeSpecies) { AddNewResources(speciesInfo, treeSpecies.TrunkResources.Keys, data); AddNewResources(speciesInfo, treeSpecies.DebrisResources.Keys, data); } if (speciesInfo.IsEmpty) { resourceless.Add(x); speciesInfo.AppendLineLocStr("No resources"); } info.AddSectionLoc($"Adding Species: {x.DisplayName}", speciesInfo); }); info.AppendLine(); info.AppendLineLocStr("Simulating..."); info.AppendLine(); UpdateRecipes(info, data); UpdateRecipesFromSkills(info, data); info.AppendLine(); info.AppendLineLocStr("Tech Tree Sim Complete"); ChatManager.SendChat(CheckStatus(info, data) ? "Tech Tree Complete" : "Tech Tree Failed, check the TechTreeSimulation.txt for more information.", user); //get issues with complete //PLANT DATA info.AppendLineLocStr("Plants Missing Resources"); info.AppendLine(Localizer.NotLocalizedStr(string.Join(",", resourceless))); //CRAFTABLES var uncraftableAccessed = new InfoBuilder(); foreach (var recipe in data.UnusableRecipes) { var recipeInfo = new InfoBuilder(); ReportMissingIngredients(recipeInfo, recipe, data); if (!CraftingComponent.TablesForRecipe(recipe.Family.GetType()).Intersect(data.CraftingTables).Any()) { recipeInfo.AppendLineLocStr("- missing crafting table"); } uncraftableAccessed.AddSection(recipe.DisplayName, recipeInfo); } info.AppendLine(); info.AddSectionLocStr("Uncraftable Accessed", uncraftableAccessed); var uncraftableUnaccessed = new InfoBuilder(); foreach (var recipe in RecipeFamily.AllRecipes.SelectMany(x => x.Recipes).Except(data.CurRecipes)) { var recipeInfo = new InfoBuilder(); var missingSkillsInfo = new InfoBuilder(); foreach (var skill in recipe.Family.RequiredSkills) { if (!data.CurSkills.Contains(skill.SkillType)) { missingSkillsInfo.AppendLine(skill.SkillItem.DisplayName); } } recipeInfo.AddSectionLocStr("- missing skills:", missingSkillsInfo); ReportMissingIngredients(recipeInfo, recipe, data); // notify about missing crafting table if (!CraftingComponent.TablesForRecipe(recipe.Family.GetType()).Intersect(data.CraftingTables).Any()) { recipeInfo.AppendLineLocStr("- missing crafting table"); } uncraftableUnaccessed.AddSection(recipe.DisplayName, recipeInfo); } info.AppendLine(); info.AddSectionLocStr("Uncraftable Unaccessed", uncraftableUnaccessed); //ALL UNOBTAINABLE ITEMS info.AppendLine(); info.AppendLineLocStr("Unobtainable Items"); foreach (var item in Item.AllItems .Where(x => !(x is Skill) && !(x is ActionbarItem) && !(x is SkillScroll) && !x.Hidden) .Select(x => x.Type) .Except(data.CraftableItems)) { info.AppendLineLocStr(" " + item.Name); } using var sw = new StreamWriter("TechTreeSimulation.txt"); sw.Write(info.ToLocString()); }