private void BuildIconRow(ImGui gui, IReadOnlyList <FactorioObject> objects, int maxRows) { const int itemsPerRow = 9; var count = objects.Count; if (count == 0) { gui.BuildText("Nothing", color: SchemeColor.BackgroundTextFaint); return; } var arr = new List <FactorioObject>(count); arr.AddRange(objects); arr.Sort(DataUtils.DefaultOrdering); if (count <= maxRows) { for (var i = 0; i < count; i++) { gui.BuildFactorioObjectButtonWithText(arr[i]); } return; } var index = 0; if (count - 1 < (maxRows - 1) * itemsPerRow) { gui.BuildFactorioObjectButtonWithText(arr[0]); index++; } var rows = Math.Min(((count - 1 - index) / itemsPerRow) + 1, maxRows); for (var i = 0; i < rows; i++) { using (gui.EnterRow()) { for (var j = 0; j < itemsPerRow; j++) { if (arr.Count <= index) { return; } gui.BuildFactorioObjectIcon(arr[index++]); } } } if (rows * itemsPerRow < count) { gui.BuildText("... and " + (count - rows * itemsPerRow) + " more"); } }
private void DrawEntryList(ImGui gui, List <RecipeEntry> entries, bool production) { var footerDrawn = false; var prevEntryStatus = EntryStatus.Normal; FactorioObject prevLatestMilestone = null; foreach (var entry in entries) { var status = entry.entryStatus; if (status < showRecipesRange) { DrawEntryFooter(gui, production); footerDrawn = true; gui.BuildText(entry.entryStatus == EntryStatus.Special ? "Show special recipes (barreling / voiding)" : entry.entryStatus == EntryStatus.NotAccessibleWithCurrentMilestones ? "There are more recipes, but they are locked based on current milestones" : "There are more recipes but they are inaccessible", wrap: true); if (gui.BuildButton("Show more recipes")) { ChangeShowStatus(status); } break; } if (status < prevEntryStatus) { prevEntryStatus = status; using (gui.EnterRow()) { gui.BuildText(status == EntryStatus.Special ? "Special recipes:" : status == EntryStatus.NotAccessibleWithCurrentMilestones ? "Locked recipes:" : "Inaccessible recipes:"); if (gui.BuildLink("hide")) { ChangeShowStatus(status + 1); } } } if (status == EntryStatus.NotAccessibleWithCurrentMilestones) { var latest = Milestones.Instance.GetHighest(entry.recipe, false); if (latest != prevLatestMilestone) { gui.BuildFactorioObjectButtonWithText(latest, size: 3f, display: MilestoneDisplay.None); prevLatestMilestone = latest; } } if (gui.ShouldBuildGroup(entry.recipe, out var group)) { DrawRecipeEntry(gui, entry, production); group.Complete(); } } if (!footerDrawn) { DrawEntryFooter(gui, production); } CheckChanging(); }
private void ChoiceObject <T>(ImGui gui, string text, T[] list, T current, Action <T> select) where T : FactorioObject { using (gui.EnterRow()) { gui.BuildText(text, topOffset: 0.5f); if (gui.BuildFactorioObjectButtonWithText(current)) { gui.BuildObjectSelectDropDown(list, DataUtils.DefaultOrdering, select, text); } } }
public override void Build(ImGui gui) { BuildHeader(gui, "Module customisation"); if (recipe.modules == null) { if (gui.BuildButton("Enable custom modules")) { recipe.RecordUndo().modules = new CustomModules(recipe); } } else { gui.BuildText("Internal modules:", Font.subheader); gui.BuildText("Leave zero amount to fill the remainings slots"); DrawRecipeModules(gui, null); gui.BuildText("Beacon modules:", Font.subheader); if (recipe.modules.beacon == null) { gui.BuildText("Use default parameters"); if (gui.BuildButton("Override beacons as well")) { gui.ShowDropDown(SelectBeacon); } } else { if (gui.BuildFactorioObjectButtonWithText(recipe.modules.beacon)) { gui.ShowDropDown(SelectBeacon); } gui.BuildText("Input the amount of modules, not the amount of beacons. Single beacon can hold " + recipe.modules.beacon.moduleSlots + " modules.", wrap: true); DrawRecipeModules(gui, recipe.modules.beacon); } } gui.AllocateSpacing(3f); if (gui.BuildButton("Done")) { Close(); } }
private void BuildRecipe(RecipeOrTechnology recipe, ImGui gui) { BuildCommon(recipe, gui); using (gui.EnterGroup(contentPadding, RectAllocator.LeftRow)) { gui.BuildIcon(Icon.Time, 2f, SchemeColor.BackgroundText); gui.BuildText(DataUtils.FormatAmount(recipe.time, UnitOfMeasure.Second)); } using (gui.EnterGroup(contentPadding)) { foreach (var ingredient in recipe.ingredients) { BuildItem(gui, ingredient); } if (recipe is Recipe rec) { var waste = rec.RecipeWaste(); if (waste > 0.01f) { var wasteAmount = MathUtils.Round(waste * 100f); var wasteText = ". (Wasting " + wasteAmount + "% of YAFC cost)"; var color = wasteAmount < 90 ? SchemeColor.BackgroundText : SchemeColor.Error; if (recipe.products.Length == 1) { gui.BuildText("YAFC analysis: There are better recipes to create " + recipe.products[0].goods.locName + wasteText, wrap: true, color: color); } else if (recipe.products.Length > 0) { gui.BuildText("YAFC analysis: There are better recipes to create each of the products" + wasteText, wrap: true, color: color); } else { gui.BuildText("YAFC analysis: This recipe wastes useful products. Don't do this recipe.", wrap: true, color: color); } } } if (recipe.flags.HasFlags(RecipeFlags.UsesFluidTemperature)) { gui.BuildText("Uses fluid temperature"); } if (recipe.flags.HasFlags(RecipeFlags.UsesMiningProductivity)) { gui.BuildText("Uses mining productivity"); } if (recipe.flags.HasFlags(RecipeFlags.ScaleProductionWithPower)) { gui.BuildText("Production scaled with power"); } } if (recipe.products.Length > 0 && !(recipe.products.Length == 1 && recipe.products[0].IsSimple && recipe.products[0].goods is Item && recipe.products[0].amount == 1f)) { BuildSubHeader(gui, "Products"); using (gui.EnterGroup(contentPadding)) foreach (var product in recipe.products) { BuildItem(gui, product); } } BuildSubHeader(gui, "Made in"); using (gui.EnterGroup(contentPadding)) BuildIconRow(gui, recipe.crafters, 2); if (recipe.modules.Length > 0) { BuildSubHeader(gui, "Allowed modules"); using (gui.EnterGroup(contentPadding)) BuildIconRow(gui, recipe.modules, 1); } if (recipe is Recipe lockedRecipe && !lockedRecipe.enabled) { BuildSubHeader(gui, "Unlocked by"); using (gui.EnterGroup(contentPadding)) { if (lockedRecipe.technologyUnlock.Count > 2) { BuildIconRow(gui, lockedRecipe.technologyUnlock, 1); } else { foreach (var technology in lockedRecipe.technologyUnlock) { var ingredient = TechnologyScienceAnalysis.Instance.GetMaxTechnologyIngredient(technology); using (gui.EnterRow(allocator: RectAllocator.RightRow)) { gui.spacing = 0f; if (ingredient != null) { gui.BuildFactorioObjectIcon(ingredient.goods); gui.BuildText(DataUtils.FormatAmount(ingredient.amount, UnitOfMeasure.None)); } gui.allocator = RectAllocator.RemainigRow; gui.BuildFactorioObjectButtonWithText(technology); } } } } } }
public override void Build(ImGui gui) { BuildHeader(gui, "Module customisation"); if (recipe.modules == null) { if (gui.BuildButton("Enable custom modules")) { recipe.RecordUndo().modules = new CustomModules(recipe); } } else { var effects = new ModuleEffects(); if (recipe.entity?.moduleSlots > 0) { gui.BuildText("Internal modules:", Font.subheader); gui.BuildText("Leave zero amount to fill the remainings slots"); DrawRecipeModules(gui, null, ref effects); } else { gui.BuildText("This building doesn't have module slots, but can be affected by beacons"); } gui.BuildText("Beacon modules:", Font.subheader); if (recipe.modules.beacon == null) { gui.BuildText("Use default parameters"); if (gui.BuildButton("Override beacons as well")) { SelectBeacon(gui); } var defaultFiller = recipe.GetModuleFiller(); if (defaultFiller != null && defaultFiller.beacon != null && defaultFiller.beaconModule != null) { effects.AddModules(defaultFiller.beaconModule.module, defaultFiller.beacon.beaconEfficiency * defaultFiller.beacon.moduleSlots * defaultFiller.beaconsPerBuilding); } } else { if (gui.BuildFactorioObjectButtonWithText(recipe.modules.beacon)) { SelectBeacon(gui); } gui.BuildText("Input the amount of modules, not the amount of beacons. Single beacon can hold " + recipe.modules.beacon.moduleSlots + " modules.", wrap: true); DrawRecipeModules(gui, recipe.modules.beacon, ref effects); } gui.BuildText("Current effects:", Font.subheader); gui.BuildText("Productivity bonus: " + DataUtils.FormatAmount(effects.productivity, UnitOfMeasure.Percent)); gui.BuildText("Speed bonus: " + DataUtils.FormatAmount(effects.speedMod, UnitOfMeasure.Percent) + " (Crafting speed: " + DataUtils.FormatAmount((recipe.entity?.craftingSpeed ?? 1f) * (1f + effects.speedMod), UnitOfMeasure.None) + ")"); var energyUsageLine = "Energy usage: " + DataUtils.FormatAmount(effects.energyUsageMod, UnitOfMeasure.Percent); if (!recipe.recipe.flags.HasFlagAny(RecipeFlags.UsesFluidTemperature | RecipeFlags.ScaleProductionWithPower) && recipe.entity != null) { energyUsageLine += " (" + DataUtils.FormatAmount(effects.energyUsageMod * recipe.entity.power / recipe.entity.energy.effectivity, UnitOfMeasure.Megawatt) + " per building)"; } gui.BuildText(energyUsageLine); } gui.AllocateSpacing(3f); using (gui.EnterRow(allocator: RectAllocator.RightRow)) { if (gui.BuildButton("Done")) { Close(); } if (recipe.modules != null && gui.BuildButton("Copy settings", SchemeColor.Grey)) { if (copiedModuleSettings == null) { MessageBox.Show("Info", "Use ctrl+click on module slot to paste settings", "Ok"); } copiedModuleSettings = JsonUtils.SaveToJson(recipe.modules); } gui.allocator = RectAllocator.LeftRow; if (recipe.modules != null && gui.BuildRedButton("Remove module customisation") == ImGuiUtils.Event.Click) { recipe.RecordUndo().modules = null; Close(); } } }
public override void Build(ImGui gui) { BuildHeader(gui, "Module autofill parameters"); BuildSimple(gui, modules); if (gui.BuildCheckBox("Fill modules in miners", modules.fillMiners, out var newFill)) { modules.RecordUndo().fillMiners = newFill; } gui.AllocateSpacing(); gui.BuildText("Filler module:", Font.subheader); gui.BuildText("Use this module when aufofill doesn't add anything (for example when productivity modules doesn't fit)", wrap: true); if (gui.BuildFactorioObjectButtonWithText(modules.fillerModule)) { SelectObjectPanel.Select(Database.allModules, "Select filler module", select => { modules.RecordUndo().fillerModule = select; }, true); } gui.AllocateSpacing(); gui.BuildText("Beacons & beacon modules:", Font.subheader); if (gui.BuildFactorioObjectButtonWithText(modules.beacon)) { SelectObjectPanel.Select(Database.allBeacons, "Select beacon", select => { modules.RecordUndo(); modules.beacon = select; if (modules.beaconModule != null && (modules.beacon == null || !modules.beacon.CanAcceptModule(modules.beaconModule.module))) { modules.beaconModule = null; } gui.Rebuild(); }, true); } if (gui.BuildFactorioObjectButtonWithText(modules.beaconModule)) { SelectObjectPanel.Select(Database.allModules.Where(x => modules.beacon?.CanAcceptModule(x.module) ?? false), "Select module for beacon", select => { modules.RecordUndo().beaconModule = select; }, true); } using (gui.EnterRow()) { gui.BuildText("Beacons per building: "); if (gui.BuildTextInput(modules.beaconsPerBuilding.ToString(), out var newText, null, Icon.None, true, new Padding(0.5f, 0f)) && int.TryParse(newText, out var newAmount) && newAmount > 0) { modules.RecordUndo().beaconsPerBuilding = newAmount; } } gui.BuildText("Please note that beacons themself are not part of the calculation", wrap: true); using (gui.EnterRow()) { gui.BuildText("Mining productivity bonus (project-wide setting): "); if (gui.BuildTextInput(DataUtils.FormatAmount(Project.current.settings.miningProductivity, UnitOfMeasure.Percent), out var newText, null, Icon.None, true, new Padding(0.5f, 0f)) && DataUtils.TryParseAmount(newText, out var newAmount, UnitOfMeasure.Percent)) { Project.current.settings.RecordUndo().miningProductivity = newAmount; } } if (gui.BuildButton("Done")) { Close(); } }