protected override void FillTab() { Text.Font = GameFont.Small; const float Padding = 10f, Spacing = 6f, SettingsRowHeight = 29f; var canvasRect = new Rect(0f, 18f, WinSize.x, WinSize.y).ContractedBy(Padding); var settingsRect = new Rect(canvasRect.x, canvasRect.y, canvasRect.width, Padding * 2f + SettingsRowHeight * 3f + Spacing * 2f); Widgets.DrawMenuSection(settingsRect); var settingsContent = settingsRect.ContractedBy(Padding); Text.Anchor = TextAnchor.MiddleLeft; var rowRect = new Rect(settingsContent.x, settingsContent.y, settingsContent.width, SettingsRowHeight); var sparkpowderVariant = (currentVariant & RecipeVariantType.Sparkpowder) != 0; Widgets.Label(rowRect, "BillsTab_MainMaterial_label".Translate()); if (Widgets.ButtonText(rowRect.RightHalf(), sparkpowderVariant ? "BillsTab_MaterialButton_sparkpowder_mode".Translate() : "BillsTab_MaterialButton_silver_mode".Translate())) { currentVariant ^= RecipeVariantType.Sparkpowder; } rowRect.y += SettingsRowHeight + Spacing; var steelVariant = (currentVariant & RecipeVariantType.Steel) != 0; Widgets.Label(rowRect, "BillsTab_SecondaryMaterial_label".Translate()); if (Widgets.ButtonText(rowRect.RightHalf(), steelVariant ? "BillsTab_MaterialButton_steel_mode".Translate() : "BillsTab_MaterialButton_component_mode".Translate())) { currentVariant ^= RecipeVariantType.Steel; } rowRect.y += SettingsRowHeight + Spacing; Widgets.Label(rowRect, "BillsTab_WorkAmount_label".Translate()); var workPercent = 100f * (sparkpowderVariant ? RemoteTechController.SilverReplacementWorkMultiplier : 1f) * (steelVariant ? RemoteTechController.ComponentReplacementWorkMultiplier : 1f); Widgets.Label(rowRect.RightHalf(), $"{workPercent:F0}%"); Text.Anchor = TextAnchor.UpperLeft; const float InfoBtnSize = 24f; var infoRect = new Rect(rowRect.xMax - InfoBtnSize, rowRect.y + (rowRect.height - InfoBtnSize) / 2f, InfoBtnSize, InfoBtnSize); TooltipHandler.TipRegion(infoRect, "BillsTab_Materials_info".Translate()); GUI.DrawTexture(infoRect, Resources.Textures.InfoButtonIcon); var recipesRect = new Rect(canvasRect.x, canvasRect.y + settingsRect.height + Spacing, canvasRect.width, canvasRect.height - (settingsRect.height + Spacing)); List <FloatMenuOption> RecipeOptionsMaker() { var list = new List <FloatMenuOption>(); for (int i = 0; i < SelTable.def.AllRecipes.Count; i++) { var recipe = SelTable.def.AllRecipes[i]; var variant = recipe.GetModExtension <MakeRecipeVariants>(); if (variant != null && variant.Variant != currentVariant) { continue; } var locked = recipe.researchPrerequisite != null && !recipe.AvailableNow && !DebugSettings.godMode; string researchTip = ""; if (locked) { researchTip = "BillsTab_ResearchRequired".Translate(recipe.researchPrerequisite.label); } var option = new FloatMenuOptionWithTooltip(recipe.LabelCap, delegate { var map = Find.CurrentMap; if (map == null || !map.mapPawns.FreeColonists.Any(recipe.PawnSatisfiesSkillRequirements)) { Bill.CreateNoPawnsWithSkillDialog(recipe); } var bill = recipe.MakeNewBill(); SelTable.billStack.AddBill(bill); }, researchTip, MenuOptionPriority.Default, null, null, 29f, rect => Widgets.InfoCardButton(rect.x + 5f, rect.y + (rect.height - 24f) / 2f, recipe)); option.Disabled = locked; list.Add(option); } if (list.Count == 0) { list.Add(new FloatMenuOption("(no recipes)", null)); } return(list); } mouseoverBill = SelTable.billStack.DoListing(recipesRect, RecipeOptionsMaker, ref scrollPosition, ref viewHeight); }
/// <summary> /// Generates a copy of a given recipe with the provided ingredient replaced with another, at the given ratio. /// Will return null if recipe requires none of the original ingredient. /// </summary> private RecipeDef TryMakeRecipeVariant(RecipeDef recipeOriginal, RecipeVariantType variant, ThingDef originalIngredient, ThingDef replacementIngredient, float replacementRatio, float workAmountMultiplier) { // check original recipe for the replaced ingredient, copy other ingredients var resourceCountRequired = 0f; var newIngredientList = new List <IngredientCount>(recipeOriginal.ingredients); foreach (var ingredientCount in newIngredientList) { if (ingredientCount.filter.Allows(originalIngredient)) { resourceCountRequired = ingredientCount.GetBaseCount(); newIngredientList.Remove(ingredientCount); break; } } if (resourceCountRequired < float.Epsilon) { return(null); } var recipeCopy = CloneObject(recipeOriginal); recipeCopy.defName = $"{recipeOriginal.defName}_{replacementIngredient.defName}"; recipeCopy.shortHash = 0; InjectedDefHasher.GiveShortHashToDef(recipeCopy, typeof(RecipeDef)); // clone our extension to avoid polluting the original def recipeCopy.modExtensions = recipeCopy.modExtensions?.Select(e => e is ICloneable i ? (DefModExtension)i.Clone() : e).ToList(); if (!recipeOriginal.HasModExtension <MakeRecipeVariants>()) { // mark original as a variant, as well recipeOriginal.modExtensions = recipeOriginal.modExtensions ?? new List <DefModExtension>(); recipeOriginal.modExtensions.Add(new MakeRecipeVariants()); } // mark the copy as variant var variantExtension = recipeCopy.GetModExtension <MakeRecipeVariants>(); if (variantExtension == null) { variantExtension = new MakeRecipeVariants(); recipeCopy.modExtensions = recipeCopy.modExtensions ?? new List <DefModExtension>(); recipeCopy.modExtensions.Add(variantExtension); } variantExtension.Variant |= variant; // copy non-replaced ingredients over to the new filter var newFixedFilter = new ThingFilter(); foreach (var allowedThingDef in recipeOriginal.fixedIngredientFilter.AllowedThingDefs) { if (allowedThingDef != originalIngredient) { newFixedFilter.SetAllow(allowedThingDef, true); } } newFixedFilter.SetAllow(replacementIngredient, true); recipeCopy.fixedIngredientFilter = newFixedFilter; recipeCopy.defaultIngredientFilter = null; // add the replacement ingredient var replacementIngredientFilter = new ThingFilter(); replacementIngredientFilter.SetAllow(replacementIngredient, true); var replacementCount = new IngredientCount { filter = replacementIngredientFilter }; replacementCount.SetBaseCount(Mathf.Round(resourceCountRequired * replacementRatio)); newIngredientList.Add(replacementCount); recipeCopy.ingredients = newIngredientList; // multiply work amount recipeCopy.workAmount = recipeOriginal.workAmount * workAmountMultiplier; recipeCopy.ResolveReferences(); return(recipeCopy); }