private void DestroyLink(ProductionLink link) { if (link.owner.links.Contains(link)) { link.owner.RecordUndo().links.Remove(link); Rebuild(); } }
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); } }
private void CreateLink(ProductionTable table, Goods goods) { if (table.linkMap.ContainsKey(goods)) { return; } var link = new ProductionLink(table, goods); Rebuild(); table.RecordUndo().links.Add(link); }
private void DrawDesiredProduct(ImGui gui, ProductionLink element) { gui.allocator = RectAllocator.Stretch; gui.spacing = 0f; var error = element.flags.HasFlags(ProductionLink.Flags.LinkNotMatched); var evt = gui.BuildFactorioGoodsWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out var newAmount, error ? SchemeColor.Error : SchemeColor.Primary); if (evt == GoodsWithAmountEvent.ButtonClick) { OpenProductDropdown(gui, gui.lastRect, element.goods, ProductDropdownType.DesiredProduct, null, model); } else if (evt == GoodsWithAmountEvent.TextEditing && newAmount != 0) { element.RecordUndo().amount = newAmount; } }
public bool FindLink(Goods goods, out ProductionLink link) => linkRoot.FindLink(goods, out link);
private void DrawLinkedProduct(ImGui gui, ProductionLink element) { BuildGoodsIcon(gui, element.goods, element.amount, ProductDropdownType.LinkedProduct, null, model); }
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); } } }