private void BuildGoodsIcon(ImGui gui, Goods goods, float amount, ProductDropdownType dropdownType, RecipeRow recipe, ProductionTable context) { var hasLink = context.FindLink(goods, out var link); var linkIsError = hasLink && ((link.flags & (ProductionLink.Flags.HasProductionAndConsumption | ProductionLink.Flags.LinkRecursiveNotMatched)) != ProductionLink.Flags.HasProductionAndConsumption); var linkIsForeign = hasLink && link.owner != context; if (gui.BuildFactorioObjectWithAmount(goods, amount, goods?.flowUnitOfMeasure ?? UnitOfMeasure.None, hasLink ? linkIsError ? SchemeColor.Error : linkIsForeign?SchemeColor.Secondary : SchemeColor.Primary: SchemeColor.None) && goods != Database.voidEnergy) { OpenProductDropdown(gui, gui.lastRect, goods, dropdownType, recipe, context); } }
private void OpenProductDropdown(ImGui targetGui, Rect rect, Goods goods, ProductDropdownType type, RecipeRow recipe, ProductionTable context) { context.FindLink(goods, out var link); var comparer = DataUtils.GetRecipeComparerFor(goods); var allRecipes = new HashSet <Recipe>(context.recipes.Select(x => x.recipe)); Predicate <Recipe> recipeExists = rec => allRecipes.Contains(rec); Action <Recipe> addRecipe = rec => { CreateLink(context, goods); if (!allRecipes.Contains(rec)) { AddRecipe(context, rec); } }; var selectFuel = type != ProductDropdownType.Fuel ? null : (Action <Goods>)(fuel => { recipe.RecordUndo().fuel = fuel; DataUtils.FavouriteFuel.AddToFavourite(fuel); }); targetGui.ShowDropDown(rect, DropDownContent, new Padding(1f)); void DropDownContent(ImGui gui, ref bool close) { if (type == ProductDropdownType.Fuel && (recipe.entity.energy.fuels.Count > 0)) { close |= gui.BuildInlineObejctListAndButton(recipe.entity.energy.fuels, DataUtils.FavouriteFuel, selectFuel, "Select fuel"); } if (link != null) { if (link.goods.fluid != null) { gui.BuildText("Fluid temperature: " + DataUtils.FormatAmount(link.resultTemperature, UnitOfMeasure.None) + "°"); } if (!link.flags.HasFlags(ProductionLink.Flags.HasProduction)) { gui.BuildText("This link has no production (Link ignored)", wrap: true, color: SchemeColor.Error); } if (!link.flags.HasFlags(ProductionLink.Flags.HasConsumption)) { gui.BuildText("This link has no consumption (Link ignored)", wrap: true, color: SchemeColor.Error); } if (!link.flags.HasFlags(ProductionLink.Flags.HasProductionAndConsumption) && link.owner.owner is RecipeRow recipeRow && recipeRow.FindLink(link.goods, out _)) { gui.BuildText("Nested tables have their own set of links that DON'T connect to parent links. To connect this product to the outside, remove this link", wrap: true, color: SchemeColor.Error); } if (link.flags.HasFlags(ProductionLink.Flags.LinkRecursiveNotMatched)) { if (link.notMatchedFlow <= 0f) { gui.BuildText("YAFC was unable to satisfy this link (Negative feedback loop). This doesn't mean that this link is the problem, but it is part of the loop.", wrap: true, color: SchemeColor.Error); } else { gui.BuildText("YAFC was unable to satisfy this link (Overproduction). You can allow overproduction for this link to solve the error.", wrap: true, color: SchemeColor.Error); } } } if (type != ProductDropdownType.Product && goods != null && goods.production.Length > 0) { close |= gui.BuildInlineObejctListAndButton(goods.production, comparer, addRecipe, "Add production recipe", 6, true, recipeExists); } if (type != ProductDropdownType.Fuel && goods != null && type != ProductDropdownType.Ingredient && goods.usages.Length > 0) { close |= gui.BuildInlineObejctListAndButton(goods.usages, DataUtils.DefaultRecipeOrdering, addRecipe, "Add consumption recipe", type == ProductDropdownType.Product ? 6 : 3, true, recipeExists); } if (link != null && gui.BuildCheckBox("Allow overproduction", link.algorithm == LinkAlgorithm.AllowOverProduction, out var newValue)) { link.RecordUndo().algorithm = newValue ? LinkAlgorithm.AllowOverProduction : LinkAlgorithm.Match; } if (link != null && link.owner == context) { if (link.amount != 0) { gui.BuildText(goods.locName + " is a desired product and cannot be unlinked.", wrap: true); } else { gui.BuildText(goods.locName + " production is currently linked. This means that YAFC will try to match production with consumption.", wrap: true); } if (type == ProductDropdownType.DesiredProduct) { if (gui.BuildButton("Remove desired product")) { link.RecordUndo().amount = 0; close = true; } if (gui.BuildButton("Remove and unlink")) { DestroyLink(context, goods); close = true; } } else if (link.amount == 0 && gui.BuildButton("Unlink")) { DestroyLink(context, goods); close = true; } } else if (goods != null) { if (link != null) { gui.BuildText(goods.locName + " production is currently linked, but the link is outside this nested table. Nested tables can have its own separate set of links", wrap: true); } else { gui.BuildText(goods.locName + " production is currently NOT linked. This means that YAFC will make no attempt to match production with consumption.", wrap: true); } if (gui.BuildButton("Create link")) { CreateLink(context, goods); close = true; } } } }
private void OpenProductDropdown(ImGui targetGui, Rect rect, Goods goods, float amount, ProductionLink link, ProductDropdownType type, RecipeRow recipe, ProductionTable context, Goods[] variants = null) { if (InputSystem.Instance.control) { Project.current.preferences.SetSourceResource(goods, !goods.IsSourceResource()); targetGui.Rebuild(); return; } var comparer = DataUtils.GetRecipeComparerFor(goods); var allRecipes = new HashSet <Recipe>(context.recipes.Select(x => x.recipe)); Predicate <Recipe> recipeExists = rec => allRecipes.Contains(rec); Action <Recipe> addRecipe = async rec => { if (variants == null) { CreateLink(context, goods); } else { foreach (var variant in variants) { if (rec.GetProduction(variant) > 0f) { CreateLink(context, variant); if (variant != goods) { recipe.RecordUndo().ChangeVariant(goods, variant); } break; } } } if (!allRecipes.Contains(rec) || (await MessageBox.Show("Recipe already exists", "Add a second copy?", "Add a copy", "Cancel")).choice) { AddRecipe(context, rec); } }; var selectFuel = type != ProductDropdownType.Fuel ? null : (Action <Goods>)(fuel => { recipe.RecordUndo().fuel = fuel; }); var allProduction = goods == null?Array.Empty <Recipe>() : variants == null ? goods.production : variants.SelectMany(x => x.production).Distinct().ToArray(); var fuelDisplayFunc = recipe?.entity?.energy.type == EntityEnergyType.FluidHeat ? (Func <Goods, string>)(g => DataUtils.FormatAmount(g.fluid?.heatValue ?? 0, UnitOfMeasure.Megajoule)) : g => DataUtils.FormatAmount(g.fuelValue, UnitOfMeasure.Megajoule); targetGui.ShowDropDown(rect, DropDownContent, new Padding(1f), 25f); void DropDownContent(ImGui gui, ref bool close) { if (type == ProductDropdownType.Fuel && recipe?.entity != null && recipe.entity.energy.fuels.Count > 1) { close |= gui.BuildInlineObejctListAndButton(recipe.entity.energy.fuels, DataUtils.FavouriteFuel, selectFuel, "Select fuel", extra: fuelDisplayFunc); } if (variants != null) { gui.BuildText("Accepted fluid variants:"); using (var grid = gui.EnterInlineGrid(3f)) { foreach (var variant in variants) { grid.Next(); if (gui.BuildFactorioObjectButton(variant, 3f, MilestoneDisplay.Contained, variant == goods ? SchemeColor.Primary : SchemeColor.None) && variant != goods) { recipe.RecordUndo().ChangeVariant(goods, variant); close = true; } } } gui.allocator = RectAllocator.Stretch; } if (link != null) { if (!link.flags.HasFlags(ProductionLink.Flags.HasProduction)) { gui.BuildText("This link has no production (Link ignored)", wrap: true, color: SchemeColor.Error); } if (!link.flags.HasFlags(ProductionLink.Flags.HasConsumption)) { gui.BuildText("This link has no consumption (Link ignored)", wrap: true, color: SchemeColor.Error); } if (link.flags.HasFlags(ProductionLink.Flags.ChildNotMatched)) { gui.BuildText("Nested table link have unmatched production/consumption. These unmatched products are not captured by this link.", wrap: true, color: SchemeColor.Error); } if (!link.flags.HasFlags(ProductionLink.Flags.HasProductionAndConsumption) && link.owner.owner is RecipeRow recipeRow && recipeRow.FindLink(link.goods, out _)) { gui.BuildText("Nested tables have their own set of links that DON'T connect to parent links. To connect this product to the outside, remove this link", wrap: true, color: SchemeColor.Error); } if (link.flags.HasFlags(ProductionLink.Flags.LinkRecursiveNotMatched)) { if (link.notMatchedFlow <= 0f) { gui.BuildText("YAFC was unable to satisfy this link (Negative feedback loop). This doesn't mean that this link is the problem, but it is part of the loop.", wrap: true, color: SchemeColor.Error); } else { gui.BuildText("YAFC was unable to satisfy this link (Overproduction). You can allow overproduction for this link to solve the error.", wrap: true, color: SchemeColor.Error); } } } if (type != ProductDropdownType.Product && goods != null && allProduction.Length > 0) { close |= gui.BuildInlineObejctListAndButton(allProduction, comparer, addRecipe, "Add production recipe", 6, true, recipeExists); } if (type != ProductDropdownType.Fuel && goods != null && type != ProductDropdownType.Ingredient && goods.usages.Length > 0) { close |= gui.BuildInlineObejctListAndButton(goods.usages, DataUtils.DefaultRecipeOrdering, addRecipe, "Add consumption recipe", type == ProductDropdownType.Product ? 6 : 3, true, recipeExists); } if (type == ProductDropdownType.Product && goods != null && allProduction.Length > 0) { close |= gui.BuildInlineObejctListAndButton(allProduction, comparer, addRecipe, "Add production recipe", 1, true, recipeExists); } if (link != null && gui.BuildCheckBox("Allow overproduction", link.algorithm == LinkAlgorithm.AllowOverProduction, out var newValue)) { link.RecordUndo().algorithm = newValue ? LinkAlgorithm.AllowOverProduction : LinkAlgorithm.Match; } if (link != null && gui.BuildButton("View link summary") && (close = true)) { ProductionLinkSummaryScreen.Show(link); } if (link != null && link.owner == context) { if (link.amount != 0) { gui.BuildText(goods.locName + " is a desired product and cannot be unlinked.", wrap: true); } else { gui.BuildText(goods.locName + " production is currently linked. This means that YAFC will try to match production with consumption.", wrap: true); } if (type == ProductDropdownType.DesiredProduct) { if (gui.BuildButton("Remove desired product")) { link.RecordUndo().amount = 0; close = true; } if (gui.BuildButton("Remove and unlink")) { DestroyLink(link); close = true; } } else if (link.amount == 0 && gui.BuildButton("Unlink")) { DestroyLink(link); close = true; } } else if (goods != null) { if (link != null) { gui.BuildText(goods.locName + " production is currently linked, but the link is outside this nested table. Nested tables can have its own separate set of links", wrap: true); } else { gui.BuildText(goods.locName + " production is currently NOT linked. This means that YAFC will make no attempt to match production with consumption.", wrap: true); } if (gui.BuildButton("Create link")) { CreateLink(context, goods); close = true; } } if (goods is Item) { BuildBeltInserterInfo(gui, amount, recipe?.buildingCount ?? 0, ref close); } } }
private void BuildGoodsIcon(ImGui gui, Goods goods, ProductionLink link, float amount, ProductDropdownType dropdownType, RecipeRow recipe, ProductionTable context, Goods[] variants = null) { var linkIsError = link != null && ((link.flags & (ProductionLink.Flags.HasProductionAndConsumption | ProductionLink.Flags.LinkRecursiveNotMatched | ProductionLink.Flags.ChildNotMatched)) != ProductionLink.Flags.HasProductionAndConsumption); var linkIsForeign = link != null && link.owner != context; if (gui.BuildFactorioObjectWithAmount(goods, amount, goods?.flowUnitOfMeasure ?? UnitOfMeasure.None, link != null ? linkIsError ? SchemeColor.Error : linkIsForeign?SchemeColor.Secondary : SchemeColor.Primary: goods.IsSourceResource() ? SchemeColor.Green : SchemeColor.None) && goods != Database.voidEnergy) { OpenProductDropdown(gui, gui.lastRect, goods, amount, link, dropdownType, recipe, context, variants); } }