Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show total output for scrap #338

Merged
merged 4 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions Yafc.Model/Data/DataClasses.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public void FallbackLocalization(FactorioObject? other, string description) {

public int CompareTo(FactorioObject? other) => DataUtils.DefaultOrdering.Compare(this, other);

public virtual bool showInExplorers => true;
public bool showInExplorers { get; internal set; } = true;
}

public class FactorioIconPart(string path) {
Expand Down Expand Up @@ -304,6 +304,7 @@ public abstract class Goods : FactorioObject {
public FactorioObject[] miscSources { get; internal set; } = [];
public Entity[] fuelFor { get; internal set; } = [];
public abstract UnitOfMeasure flowUnitOfMeasure { get; }
public bool isLinkable { get; internal set; } = true;

public override void GetDependencies(IDependencyCollector collector, List<FactorioObject> temp) => collector.Add(production.Concat(miscSources).ToArray(), DependencyList.Flags.Source);

Expand Down Expand Up @@ -361,8 +362,6 @@ public class Special : Goods {
public override string type => isPower ? "Power" : "Special";
public override UnitOfMeasure flowUnitOfMeasure => isVoid ? UnitOfMeasure.None : isPower ? UnitOfMeasure.Megawatt : UnitOfMeasure.PerSecond;
internal override FactorioObjectSortOrder sortingOrder => FactorioObjectSortOrder.SpecialGoods;
public override bool showInExplorers => !isResearch;

public override void GetDependencies(IDependencyCollector collector, List<FactorioObject> temp) {
if (isResearch) {
collector.Add(Database.technologies.all.ToArray(), DependencyList.Flags.Source);
Expand Down
2 changes: 2 additions & 0 deletions Yafc.Model/Data/Database.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public static class Database {
public static Dictionary<string, List<Fluid>> fluidVariants { get; internal set; } = null!;
public static Goods voidEnergy { get; internal set; } = null!;
public static Goods researchUnit { get; internal set; } = null!;
public static Goods itemInput { get; internal set; } = null!;
public static Goods itemOutput { get; internal set; } = null!;
public static Goods electricity { get; internal set; } = null!;
public static Recipe electricityGeneration { get; internal set; } = null!;
public static Goods heat { get; internal set; } = null!;
Expand Down
11 changes: 8 additions & 3 deletions Yafc.Model/Model/ProductionTableContent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ public Goods? fixedProduct {
if (value == null) {
_fixedProduct = null;
}
else if (recipe.products.All(p => p.goods != value)) {
else if (value != Database.itemOutput && recipe.products.All(p => p.goods != value)) {
// The UI doesn't know the difference between a product and a spent fuel, but we care about the difference
_fixedProduct = null;
fixedFuel = true;
Expand Down Expand Up @@ -612,13 +612,18 @@ public void Dispose() {
row.fixedBuildings *= row.parameters.recipeTime / oldParameters.recipeTime; // step 3, for fixed ingredient consumption
}
else if (row.fixedProduct != null) {
if (row.recipe.products.SingleOrDefault(p => p.goods == row.fixedProduct, false) is not Product product) {
if (row.fixedProduct == Database.itemOutput) {
float oldAmount = row.recipe.products.Where(p => p.goods is Item).Sum(p => p.GetAmountPerRecipe(oldParameters.productivity)) / oldParameters.recipeTime;
float newAmount = row.recipe.products.Where(p => p.goods is Item).Sum(p => p.GetAmountPerRecipe(row.parameters.productivity)) / row.parameters.recipeTime;
row.fixedBuildings *= oldAmount / newAmount; // step 3, for fixed combined production amount
}
else if (row.recipe.products.SingleOrDefault(p => p.goods == row.fixedProduct, false) is not Product product) {
row.fixedBuildings = 0; // We couldn't find the Product corresponding to fixedProduct. Just clear the fixed amount.
}
else {
float oldAmount = product.GetAmountPerRecipe(oldParameters.productivity) / oldParameters.recipeTime;
float newAmount = product.GetAmountPerRecipe(row.parameters.productivity) / row.parameters.recipeTime;
row.fixedBuildings *= oldAmount / newAmount; // step 3, for fixed production amount
row.fixedBuildings *= oldAmount / newAmount; // step 3, for fixed individual production amount
}
}

Expand Down
4 changes: 4 additions & 0 deletions Yafc.Model/Model/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,10 @@ public class ProjectPreferences(Project owner) : ModelObject<Project>(owner) {
/// The scale to use when drawing icons that have information stored in their background color, stored as a ratio from 0 to 1.
/// </summary>
public float iconScale { get; set; } = .9f;
/// <summary>
/// The <see cref="Database.itemInput"/> and <see cref="Database.itemOutput"/> pseudo-items will be displayed at or above this ingredient/product count.
/// </summary>
public int minForTotalItems { get; set; } = 3;
veger marked this conversation as resolved.
Show resolved Hide resolved

protected internal override void AfterDeserialize() {
base.AfterDeserialize();
Expand Down
23 changes: 23 additions & 0 deletions Yafc.Parser/Data/FactorioDataDeserializer_Context.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ internal partial class FactorioDataDeserializer {
private readonly Special electricity;
private readonly Special rocketLaunch;
private readonly Special researchUnit;
private readonly Item totalItemOutput;
private readonly Item totalItemInput;
private readonly EntityEnergy voidEntityEnergy;
private readonly EntityEnergy laborEntityEnergy;
private Entity? character;
Expand All @@ -53,6 +55,19 @@ Special createSpecialObject(bool isPower, string name, string locName, string lo
return obj;
}

Item createSpecialItem(string name, string locName, string locDescr, string icon) {
Item obj = GetObject<Item>(name);
obj.factorioType = "special";
obj.locName = locName;
obj.locDescr = locDescr;
obj.iconSpec = [new FactorioIconPart(icon)];
obj.isLinkable = false;
obj.showInExplorers = false;
rootAccessible.Add(obj);

return obj;
}

electricity = createSpecialObject(true, SpecialNames.Electricity, "Electricity", "This is an object that represents electric energy",
"__core__/graphics/icons/alerts/electricity-icon-unplugged.png", "signal-E");
fuels.Add(SpecialNames.Electricity, electricity);
Expand All @@ -62,6 +77,8 @@ Special createSpecialObject(bool isPower, string name, string locName, string lo

voidEnergy = createSpecialObject(true, SpecialNames.Void, "Void", "This is an object that represents infinite energy", "__core__/graphics/icons/mip/infinity.png", "signal-V");
voidEnergy.isVoid = true;
voidEnergy.isLinkable = false;
voidEnergy.showInExplorers = false;
fuels.Add(SpecialNames.Void, voidEnergy);
rootAccessible.Add(voidEnergy);

Expand All @@ -70,6 +87,7 @@ Special createSpecialObject(bool isPower, string name, string locName, string lo
researchUnit = createSpecialObject(false, SpecialNames.ResearchUnit, "Research",
"This represents one unit of a research task.", "__base__/graphics/icons/compilatron.png", "signal-L");
researchUnit.isResearch = true;
researchUnit.showInExplorers = false;
Analysis.ExcludeFromAnalysis<CostAnalysis>(researchUnit);

generatorProduction = CreateSpecialRecipe(electricity, SpecialNames.GeneratorRecipe, "generating");
Expand All @@ -84,6 +102,9 @@ Special createSpecialObject(bool isPower, string name, string locName, string lo

voidEntityEnergy = new EntityEnergy { type = EntityEnergyType.Void, effectivity = float.PositiveInfinity };
laborEntityEnergy = new EntityEnergy { type = EntityEnergyType.Labor, effectivity = float.PositiveInfinity };

totalItemInput = createSpecialItem("item-total-input", "Total item consumption", "This item represents the combined total item input of a multi-ingredient recipe. It can be used to set or measure the number of sushi belts required to supply this recipe row.", "__base__/graphics/icons/signal/signal_I.png");
totalItemOutput = createSpecialItem("item-total-output", "Total item production", "This item represents the combined total item output of a multi-product recipe. It can be used to set or measure the number of sushi belts required to handle the products of this recipe row.", "__base__/graphics/icons/signal/signal_O.png");
}

private T GetObject<T>(string name) where T : FactorioObject, new() => GetObject<T, T>(name);
Expand Down Expand Up @@ -120,6 +141,8 @@ private void ExportBuiltData() {
Database.allSciencePacks = [.. sciencePacks];
Database.voidEnergy = voidEnergy;
Database.researchUnit = researchUnit;
Database.itemInput = totalItemInput;
Database.itemOutput = totalItemOutput;
Database.electricity = electricity;
Database.electricityGeneration = generatorProduction;
Database.heat = heat;
Expand Down
29 changes: 29 additions & 0 deletions Yafc/Windows/PreferencesScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ private static void DrawGeneral(ImGui gui) {
}
}

float textBoxHeight; // measure the height of a text box, for use when drawing the minForTotalItems input box.
using (gui.EnterRow()) {
gui.BuildText("Reactor layout:", topOffset: 0.5f);
if (gui.BuildTextInput(settings.reactorSizeX + "x" + settings.reactorSizeY, out string newSize, null, delayed: true)) {
Expand All @@ -155,8 +156,36 @@ private static void DrawGeneral(ImGui gui) {
settings.reactorSizeY = sizeY;
}
}
textBoxHeight = gui.lastRect.Height;
}

string ioItemMessage = "The I and O items represent the total item input or item output of a recipe row.\nRecipes with at least this many item ingredients or item products will show the pseudo-items after all their real ingredients/products.";
using (gui.EnterRowWithHelpIcon(ioItemMessage)) {
float captionWidth = gui.GetTextDimensions(out _, "Minimum recipe ingredients or products").X;
using (gui.EnterFixedPositioning(captionWidth, 0, new())) { // the height will grow to fit the controls
gui.BuildText("Minimum recipe ingredients or products");
using (gui.EnterRow(0)) {
// Allocate the horizontal space now but draw the icons first, so the text can be drawn vertically centered.
Rect textRect = gui.AllocateTextRect(out _, "to display the ", TextBlockDisplayStyle.Default());
gui.BuildFactorioObjectButton(Database.itemInput, ButtonDisplayStyle.Default);
gui.BuildFactorioObjectButton(Database.itemOutput, ButtonDisplayStyle.Default);
textRect.Height = gui.lastRect.Height;
gui.DrawText(textRect, "to display the ");
gui.BuildText(" summary items");
}
}
float spacing = (gui.lastRect.Height - textBoxHeight) / 2;
using (gui.RemainingRow().EnterFixedPositioning(0, gui.lastRect.Height, new())) {
gui.AllocateSpacing(spacing - .5f); // draw the box vertically centered, rather than top-aligned (without this) or full height (without EnterFixedPositioning)
if (gui.BuildIntegerInput(prefs.minForTotalItems, out int newValue2)) {
prefs.RecordUndo().minForTotalItems = newValue2;
gui.Rebuild();
}
}
}

gui.AllocateSpacing();

if (gui.BuildCheckBox("Dark mode", Preferences.Instance.darkMode, out bool newValue)) {
Preferences.Instance.darkMode = newValue;
Preferences.Instance.Save();
Expand Down
25 changes: 17 additions & 8 deletions Yafc/Workspace/ProductionTable/ProductionTableView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,11 @@ public override void BuildElement(ImGui gui, RecipeRow recipe) {
grid.Next();
view.BuildGoodsIcon(gui, goods, link, amount, ProductDropdownType.Ingredient, recipe, recipe.linkRoot, HintLocations.OnProducingRecipes, variants);
}
if (recipe.fixedIngredient == Database.itemInput || recipe.Ingredients.Count() >= Project.current.preferences.minForTotalItems) {
grid.Next();
view.BuildGoodsIcon(gui, recipe.hierarchyEnabled ? Database.itemInput : null, null, recipe.Ingredients.Where(i => i.Goods is Item).Sum(i => i.Amount),
ProductDropdownType.Ingredient, recipe, recipe.linkRoot, HintLocations.None);
}
}
grid.Dispose();
}
Expand All @@ -552,6 +557,11 @@ public override void BuildElement(ImGui gui, RecipeRow recipe) {
grid.Next();
view.BuildGoodsIcon(gui, goods, link, amount, ProductDropdownType.Product, recipe, recipe.linkRoot, HintLocations.OnConsumingRecipes);
}
if (recipe.fixedProduct == Database.itemOutput || recipe.Products.Count() >= Project.current.preferences.minForTotalItems) {
grid.Next();
view.BuildGoodsIcon(gui, recipe.hierarchyEnabled ? Database.itemOutput : null, null, recipe.Products.Where(i => i.Goods is Item).Sum(i => i.Amount),
ProductDropdownType.Product, recipe, recipe.linkRoot, HintLocations.None);
}
}
grid.Dispose();
}
Expand Down Expand Up @@ -712,7 +722,7 @@ private enum ProductDropdownType {
}

private void CreateLink(ProductionTable table, Goods goods) {
if (table.linkMap.ContainsKey(goods)) {
if (table.linkMap.ContainsKey(goods) || !goods.isLinkable) {
return;
}

Expand Down Expand Up @@ -966,13 +976,12 @@ void dropDownContent(ImGui gui) {
"Nested tables can have its own separate set of links";
gui.BuildText(goodsNestLinkMessage, TextBlockDisplayStyle.WrappedText);
}
else {
else if (goods.isLinkable) {
string notLinkedMessage = goods.locName + " production is currently NOT linked. This means that YAFC will make no attempt to match production with consumption.";
gui.BuildText(notLinkedMessage, TextBlockDisplayStyle.WrappedText);
}

if (gui.BuildButton("Create link").WithTooltip(gui, "Shortcut: right-click") && gui.CloseDropdown()) {
CreateLink(context, goods);
if (gui.BuildButton("Create link").WithTooltip(gui, "Shortcut: right-click") && gui.CloseDropdown()) {
CreateLink(context, goods);
}
}
}
#endregion
Expand Down Expand Up @@ -1170,7 +1179,7 @@ private void BuildGoodsIcon(ImGui gui, Goods? goods, ProductionLink? link, float
case GoodsWithAmountEvent.LeftButtonClick when goods is not null:
OpenProductDropdown(gui, gui.lastRect, goods, amount, link, dropdownType, recipe, context, variants);
break;
case GoodsWithAmountEvent.RightButtonClick when goods is not null && (link is null || link.owner != context):
case GoodsWithAmountEvent.RightButtonClick when goods is not null and not { isLinkable: false } && (link is null || link.owner != context):
CreateLink(context, goods);
break;
case GoodsWithAmountEvent.RightButtonClick when link?.amount == 0 && link.owner == context:
Expand Down Expand Up @@ -1421,7 +1430,7 @@ protected override void BuildContent(ImGui gui) {
}

private static void AddDesiredProductAtLevel(ProductionTable table) => SelectMultiObjectPanel.Select(
Database.goods.all.Except(table.linkMap.Where(p => p.Value.amount != 0).Select(p => p.Key)), "Add desired product", product => {
Database.goods.all.Except(table.linkMap.Where(p => p.Value.amount != 0).Select(p => p.Key)).Where(g => g.isLinkable), "Add desired product", product => {
if (table.linkMap.TryGetValue(product, out var existing)) {
if (existing.amount != 0) {
return;
Expand Down
5 changes: 5 additions & 0 deletions changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
// Internal changes:
// Changes to the code that do not affect the behavior of the program.
----------------------------------------------------------------------------------------------------------------------
Version:
Date:
Features:
- Add pseudo-items representing a recipe's total sushi-inputs and sushi-outputs.
----------------------------------------------------------------------------------------------------------------------
Version: 2.1.0
Date: October 29th 2024
Features:
Expand Down