/* * For resources (including work) that are part of the cost of both the road to build and the best existing road, * grant CostUpgradeRebate% (default 30%) of the best existing road build costs as a rebate on the costs of the road to be built * i.e. the exisitng road cost 300 stones, the new road cost 600 stones, the rebate is 300*30% = 90 stones */ public static void GetUpgradeModifiers(int fromTile_int, int toTile_int, RoadDef roadToBuild, out Dictionary <string, int> rebate) { rebate = new Dictionary <string, int>(); RoadDef bestExistingRoad = RoadsOfTheRim.BestExistingRoad(fromTile_int, toTile_int); if (bestExistingRoad != null) { DefModExtension_RotR_RoadDef bestExistingRoadDefModExtension = bestExistingRoad.GetModExtension <DefModExtension_RotR_RoadDef>(); DefModExtension_RotR_RoadDef roadToBuildRoadDefModExtension = roadToBuild.GetModExtension <DefModExtension_RotR_RoadDef>(); if (bestExistingRoadDefModExtension != null && roadToBuildRoadDefModExtension != null && RoadsOfTheRim.isRoadBetter(roadToBuild, bestExistingRoad)) { foreach (string resourceName in DefModExtension_RotR_RoadDef.allResourcesAndWork) { int existingCost = bestExistingRoadDefModExtension.GetCost(resourceName); int toBuildCost = roadToBuildRoadDefModExtension.GetCost(resourceName); if (existingCost != 0 && toBuildCost != 0) { if ((int)(existingCost * (float)RoadsOfTheRim.settings.CostUpgradeRebate / 100) > toBuildCost) { rebate[resourceName] = toBuildCost; } else { rebate[resourceName] = (int)(existingCost * (float)RoadsOfTheRim.settings.CostUpgradeRebate / 100); } } } } } }
public static string ShowBestRoad(Pawn p) { RoadDef BestRoadDef = null; if (PawnBuildingUtility.HealthyColonist(p)) { foreach (RoadDef thisDef in DefDatabase <RoadDef> .AllDefs) { if (thisDef.HasModExtension <DefModExtension_RotR_RoadDef>() && thisDef.GetModExtension <DefModExtension_RotR_RoadDef>().built) // Only add RoadDefs that are buildable, based on DefModExtension_RotR_RoadDef.built { DefModExtension_RotR_RoadDef RoadDefMod = thisDef.GetModExtension <DefModExtension_RotR_RoadDef>(); if (PawnBuildingUtility.ConstructionLevel(p) >= RoadDefMod.minConstruction) { if ((BestRoadDef == null) || (thisDef.movementCostMultiplier < BestRoadDef.movementCostMultiplier)) { BestRoadDef = thisDef; } } } } if (BestRoadDef != null) { return(BestRoadDef.label); } } return("-"); }
public static bool ImpassableAllowed(int tile, RoadDef roadDef) { DefModExtension_RotR_RoadDef RoadDefMod = roadDef.GetModExtension <DefModExtension_RotR_RoadDef>(); Hilliness hillinnessHere = Find.WorldGrid.tiles[tile].hilliness; if (RoadDefMod.canBuildOnImpassable && hillinnessHere == Hilliness.Impassable) { return(true); } return(hillinnessHere != Hilliness.Impassable); }
public static bool BiomeAllowed(int tile, RoadDef roadDef, out BiomeDef biomeHere) { DefModExtension_RotR_RoadDef RoadDefMod = roadDef.GetModExtension <DefModExtension_RotR_RoadDef>(); biomeHere = Find.WorldGrid.tiles[tile].biome; if (RoadDefMod.canBuildOnWater && (biomeHere.defName == "Ocean" || biomeHere.defName == "Lake")) { return(true); } return(biomeHere.allowRoads); }
public void setCosts() { try { RoadsOfTheRimSettings settings = LoadedModManager.GetMod <RoadsOfTheRim>().GetSettings <RoadsOfTheRimSettings>(); RoadConstructionSite parentSite = this.parent as RoadConstructionSite; float elevationModifier = 0f; float hillinessModifier = 0f; float swampinessModifier = 0f; float bridgeModifier = 0f; GetCostsModifiers(parentSite.Tile, parentSite.GetNextLeg().Tile, ref elevationModifier, ref hillinessModifier, ref swampinessModifier, ref bridgeModifier); // Total cost modifier float totalCostModifier = (1 + elevationModifier + hillinessModifier + swampinessModifier + bridgeModifier) * ((float)settings.BaseEffort / 10); DefModExtension_RotR_RoadDef roadDefExtension = parentSite.roadDef.GetModExtension <DefModExtension_RotR_RoadDef>(); // Check existing roads for potential rebates when upgrading GetUpgradeModifiers(parentSite.Tile, parentSite.GetNextLeg().Tile, parentSite.roadDef, out Dictionary <string, int> rebate); List <string> s = new List <string>(); foreach (string resourceName in DefModExtension_RotR_RoadDef.allResourcesAndWork) { if (roadDefExtension.GetCost(resourceName) > 0) { int thisRebate = 0; // The cost modifier doesn't affect some advanced resources, as defined in static DefModExtension_RotR_RoadDef.allResourcesWithoutModifiers float costModifierForThisResource = ((DefModExtension_RotR_RoadDef.allResourcesWithoutModifiers.Contains(resourceName)) ? 1 : totalCostModifier); rebate.TryGetValue(resourceName, out thisRebate); // Minimum cost of anything that's needed is 1 costs[resourceName] = Math.Max((int)((roadDefExtension.GetCost(resourceName) - thisRebate) * costModifierForThisResource), 1); left[resourceName] = Math.Max(costs[resourceName], 1f); if (thisRebate > 0) { s.Add("RoadsOfTheRim_UpgradeRebateDetail".Translate((int)(thisRebate * costModifierForThisResource), resourceName)); } } } if (s.Count > 0) { Messages.Message("RoadsOfTheRim_UpgradeRebate".Translate(parentSite.roadDef.label, string.Join(", ", s.ToArray())), MessageTypeDefOf.PositiveEvent); } parentSite.UpdateProgressBarMaterial(); } catch (Exception e) { Log.Error("[RotR] : Exception when setting constructionSite costs = " + e); } }
public static void PostFix(ref float __result, int tile, bool perceivedStatic, int?ticksAbs, StringBuilder explanation) { if (__result > 999f) { try { if (Find.WorldGrid.InBounds(tile)) { Tile tile2 = Find.WorldGrid.tiles[tile]; List <Tile.RoadLink> roads = tile2.Roads; if (roads?.Count > 0) { RoadDef BestRoad = null; for (int i = 0; i < roads.Count; i++) { if (BestRoad == null) { BestRoad = roads[i].road; } else { if (BestRoad.movementCostMultiplier < roads[i].road.movementCostMultiplier) { BestRoad = roads[i].road; } } } if (BestRoad != null) { DefModExtension_RotR_RoadDef roadDefExtension = BestRoad.GetModExtension <DefModExtension_RotR_RoadDef>(); if (roadDefExtension != null && ((tile2.biome.impassable && roadDefExtension.biomeModifier > 0) || (tile2.hilliness == Hilliness.Impassable))) { __result = 12f; //RoadsOfTheRim.DebugLog(String.Format("[RotR] - Impassable Tile {0} of biome {1} movement difficulty patched to 12", tile , tile2.biome.label)); } } } } else { RoadsOfTheRim.DebugLog("[RotR] - CalculatedMovementDifficultyAt Patch - Tile out of bounds"); } } catch (Exception e) { RoadsOfTheRim.DebugLog("[RotR] - CalculatedMovementDifficultyAt Patch - Catastrophic failure", e); return; } } }
public bool isDeveloppedEnough(Faction faction, DefModExtension_RotR_RoadDef RoadDefModExtension) { return(faction.def.techLevel >= RoadDefModExtension.techlevelToBuild); }
/* * Based on the Caravan's resources, Pawns & the road's cost (modified by terrain) : * - Determine the amount of work done in a tick * - Consume the caravan's resources * - Return whether or not the Caravan must now stop because it ran out of resources * - NOTE : Does this need to be here ? Maybe better in Mod.cs * Returns TRUE if work finished * CALLED FROM : CompTick() of WorldObjectComp_Caravan */ public static bool DoSomeWork(Caravan caravan, RoadConstructionSite site, out bool noMoreResources) { var caravanComp = caravan.GetComponent <WorldObjectComp_Caravan>(); var siteComp = site.GetComponent <WorldObjectComp_ConstructionSite>(); _ = site.roadDef.GetModExtension <DefModExtension_RotR_RoadDef>(); noMoreResources = false; var useISR2G = caravanComp.UseISR2G(); var available = new Dictionary <string, int>(); var needed = new Dictionary <string, int>(); var ratio = new Dictionary <string, float>(); float ratio_final = 1; //RoadsOfTheRim.DebugLog("[RotR] DEBUG ========== doSomeWork() =========="); //RoadsOfTheRim.DebugLog("[RotR] DEBUG ISR2G set to "+useISR2G); if (DebugSettings.godMode) { return(siteComp.FinishWork(caravan)); } if (caravanComp.CaravanCurrentState() != CaravanState.ReadyToWork) { DebugLog("[RotR] DEBUG : doSomeWork() failed because the caravan can't work."); return(false); } // Percentage of total work that can be done in this batch, might be 0 if no pawn was found with enough skill var amountOfWork = caravanComp.AmountOfWork(true); // Work was 0 (not enough skill) if (Math.Abs(amountOfWork) < double.Epsilon) { Messages.Message("RoadsOfTheRim_CaravanNoWork".Translate(caravan.Name, site.roadDef.label), MessageTypeDefOf.RejectInput); caravanComp.StopWorking(); return(false); } // calculate material present in the caravan foreach (var resourceName in DefModExtension_RotR_RoadDef.allResources) { available[resourceName] = 0; } foreach (var aThing in CaravanInventoryUtility.AllInventoryItems(caravan)) { foreach (var resourceName in DefModExtension_RotR_RoadDef.allResources) { if (IsThis(aThing.def, resourceName)) { available[resourceName] += aThing.stackCount; } } } // What percentage of work will remain after amountOfWork is done ? var percentOfWorkLeftToDoAfter = (siteComp.GetLeft("Work") - amountOfWork) / siteComp.GetCost("Work"); // The amount of each resource left to spend in total is : percentOfWorkLeftToDoAfter * {this resource cost} // Materials that would be needed to do that much work foreach (var resourceName in DefModExtension_RotR_RoadDef.allResources) { needed[resourceName] = (int)Math.Round(siteComp.GetLeft(resourceName) - (percentOfWorkLeftToDoAfter * siteComp.GetCost(resourceName))); // Check if there's enough material to go through this batch. Materials with a cost of 0 are always OK // Don't check when ISR2G is in use for this resource, don't check for work if (DefModExtension_RotR_RoadDef.GetInSituModifier(resourceName, useISR2G) || resourceName == "Work") { continue; } ratio[resourceName] = needed[resourceName] == 0 ? 1f : Math.Min(available[resourceName] / (float)needed[resourceName], 1f); if (ratio[resourceName] < ratio_final) { ratio_final = ratio[resourceName]; } } // The caravan didn't have enough resources for a full batch of work. Use as much as we can then stop working if (ratio_final < 1f) { Messages.Message("RoadsOfTheRim_CaravanNoResource".Translate(caravan.Name, site.roadDef.label), MessageTypeDefOf.RejectInput); foreach (var resourceName in DefModExtension_RotR_RoadDef.allResources) { needed[resourceName] = (int)(needed[resourceName] * ratio_final); } caravanComp.StopWorking(); } //RoadsOfTheRim.DebugLog("[RotR] ISR2G DEBUG ratio final = " + ratio_final); // Consume resources from the caravan _ = site.roadDef.defName == "DirtPathBuilt"; // Always consider resources have been consumed when the road is a dirt path foreach (var aThing in CaravanInventoryUtility.AllInventoryItems(caravan)) { foreach (var resourceName in DefModExtension_RotR_RoadDef.allResources) { if (!DefModExtension_RotR_RoadDef.GetInSituModifier(resourceName, useISR2G)) { if (needed[resourceName] <= 0 || !IsThis(aThing.def, resourceName)) { continue; //RoadsOfTheRim.DebugLog("[RotR] ISR2G consumption DEBUG =" + resourceName + " Qty consumed = " + amountUsed); } var amountUsed = aThing.stackCount > needed[resourceName] ? needed[resourceName] : aThing.stackCount; aThing.stackCount -= amountUsed; // Reduce how much of this resource is needed needed[resourceName] -= amountUsed; siteComp.ReduceLeft(resourceName, amountUsed); } else { if (needed[resourceName] <= 0) { continue; } //RoadsOfTheRim.DebugLog("[RotR] ISR2G consumption DEBUG =" + resourceName + " Qty freely awarded = " + needed[resourceName]); siteComp.ReduceLeft(resourceName, needed[resourceName]); needed[resourceName] = 0; } } if (aThing.stackCount == 0) { aThing.Destroy(); } } caravanComp.TeachPawns(ratio_final); // Pawns learn some construction // HARDCODED : ISR2G divides work done by 4 , AISR2G by 2 for all roads except dirt path if (useISR2G > 0 && site.roadDef.defName != "DirtPathBuilt") { amountOfWork = amountOfWork * 0.25f * useISR2G; } // Update amountOfWork based on the actual ratio worked & finally reducing the work & resources left amountOfWork = ratio_final * amountOfWork; return(siteComp.UpdateProgress(amountOfWork, caravan)); }
/* * Amount of work : * - Construction speed (0.5 + 0.15 per level) times the construct success chance (0.75 to 1.13 - lvl 8 is 1) * - Pack animals help as well (see below) */ public float AmountOfWork(bool verbose = false) { var pawns = GetCaravan().PawnsListForReading; DefModExtension_RotR_RoadDef roadDefModExtension = null; try { roadDefModExtension = site.roadDef.GetModExtension <DefModExtension_RotR_RoadDef>(); } catch { /* Either there's no site, no roaddef, or no modextension. In any case, not much to do here */ } //site.roadDef.GetModExtension<DefModExtension_RotR_RoadDef>().minConstruction ; var totalConstruction = 0f; var totalConstructionAboveMinLevel = 0f; var animalConstruction = 0f; foreach (var pawn in pawns) { /* * if (pawn.IsFreeColonist && pawn.health.State == PawnHealthState.Mobile) * { * totalConstruction += pawn.GetStatValue(StatDefOf.ConstructionSpeed) * pawn.GetStatValue(StatDefOf.ConstructSuccessChance); * * if (roadDefModExtension!=null && pawn.skills.GetSkill(SkillDefOf.Construction).levelInt >= roadDefModExtension.minConstruction) * { * totalConstructionAboveMinLevel += pawn.GetStatValue(StatDefOf.ConstructionSpeed) * pawn.GetStatValue(StatDefOf.ConstructSuccessChance); * } * } * else if (pawn.RaceProps.packAnimal && pawn.health.State == PawnHealthState.Mobile) * { * animalConstruction += pawn.GetStatValue(StatDefOf.ConstructionSpeed) * pawn.GetStatValue(StatDefOf.ConstructSuccessChance); * } */ var PawnConstructionValue = PawnBuildingUtility.ConstructionValue(pawn); if (PawnBuildingUtility.HealthyColonist(pawn)) { totalConstruction += PawnConstructionValue; if (roadDefModExtension != null && PawnBuildingUtility.ConstructionLevel(pawn) >= roadDefModExtension.minConstruction) { totalConstructionAboveMinLevel += PawnConstructionValue; } continue; } if (PawnBuildingUtility.HealthyPackAnimal(pawn)) { animalConstruction += PawnConstructionValue; } } if (roadDefModExtension != null) { var ratioOfConstructionAboveMinLevel = totalConstructionAboveMinLevel / totalConstruction; if (ratioOfConstructionAboveMinLevel < roadDefModExtension.percentageOfminConstruction) { // Check minimum construction level requirements if needed var ratioActuallyWorked = ratioOfConstructionAboveMinLevel / roadDefModExtension.percentageOfminConstruction; totalConstruction *= ratioActuallyWorked; if (verbose) { Messages.Message( "RoadsOfTheRim_InsufficientConstructionMinLevel".Translate(totalConstruction, roadDefModExtension.percentageOfminConstruction.ToString("P0"), roadDefModExtension.minConstruction), MessageTypeDefOf.NegativeEvent); } } } // Pack animals can only add as much work as humans (i.e. : at best, pack animals double the amount of work) if (animalConstruction > totalConstruction) { animalConstruction = totalConstruction; } totalConstruction += animalConstruction; return(totalConstruction); }
// Here, test if we picked a tile that's already part of the chain for this construction site (different construction sites can cross each other's paths) // Yes -> // Was it the construction site itself ? // Yes -> We are done creating the site // No -> delete this leg and all legs after it // No -> create a new Leg public static bool ActionOnTile(RoadConstructionSite site, int tile) { if (site.def != DefDatabase <WorldObjectDef> .GetNamed("RoadConstructionSite", true)) { Log.Error("[RotR] - The RoadConstructionSite given is somehow wrong"); return(true); } try { foreach (WorldObject o in Find.WorldObjects.ObjectsAt(tile)) { // Action on the construction site = we're done if ((o.def == DefDatabase <WorldObjectDef> .GetNamed("RoadConstructionSite", true)) && (RoadConstructionSite)o == site) { return(true); } // Action on a leg that's part of this chain = we should delete all legs after that & keep targetting if ((o.def == DefDatabase <WorldObjectDef> .GetNamed("RoadConstructionLeg", true)) && ((RoadConstructionLeg)o).site == site) { RoadConstructionLeg.Remove((RoadConstructionLeg)o); Target(site); return(false); } } // Check whether we clicked on a neighbour List <int> neighbouringTiles = new List <int>(); Find.WorldGrid.GetTileNeighbors(tile, neighbouringTiles); // This is not a neighbour : do nothing if (!neighbouringTiles.Contains(site.LastLeg.Tile)) { Target(site); return(false); } // There can be no ConstructionLeg on a biome that doesn't allow roads if (!DefModExtension_RotR_RoadDef.BiomeAllowed(tile, site.roadDef, out BiomeDef biomeHere)) { Messages.Message("RoadsOfTheRim_BiomePreventsConstruction".Translate(site.roadDef.label, biomeHere.label), MessageTypeDefOf.RejectInput); Target(site); return(false); } else if (!DefModExtension_RotR_RoadDef.ImpassableAllowed(tile, site.roadDef)) { Messages.Message("RoadsOfTheRim_BiomePreventsConstruction".Translate(site.roadDef.label, " impassable mountains"), MessageTypeDefOf.RejectInput); Target(site); return(false); } RoadConstructionLeg newLeg = (RoadConstructionLeg)WorldObjectMaker.MakeWorldObject(DefDatabase <WorldObjectDef> .GetNamed("RoadConstructionLeg", true)); newLeg.Tile = tile; newLeg.site = site; // This is not the first Leg if (site.LastLeg.def == DefDatabase <WorldObjectDef> .GetNamed("RoadConstructionLeg", true)) { RoadConstructionLeg l = site.LastLeg as RoadConstructionLeg; l.SetNext(newLeg); newLeg.previous = l; } else { newLeg.previous = null; } newLeg.SetNext(null); Find.WorldObjects.Add(newLeg); site.LastLeg = newLeg; Target(site); return(false); } catch (Exception e) { Log.Error("[RotR] Exception : " + e); return(true); } }
public string progressDescription() { RoadConstructionSite parentSite = this.parent as RoadConstructionSite; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("RoadsOfTheRim_ConstructionSiteDescription_Main".Translate(String.Format("{0:P1}", GetPercentageDone("Work")))); // Description of ally's help, if any if (parentSite.helpFromFaction != null) { stringBuilder.Append("RoadsOfTheRim_ConstructionSiteDescription_Help".Translate(parentSite.helpFromFaction.Name, (int)parentSite.helpAmount, String.Format("{0:0.0}", parentSite.helpWorkPerTick))); if (parentSite.helpFromTick > Find.TickManager.TicksGame) { stringBuilder.Append("RoadsOfTheRim_ConstructionSiteDescription_HelpStartsWhen".Translate(String.Format("{0:0.00}", (float)(parentSite.helpFromTick - Find.TickManager.TicksGame) / (float)GenDate.TicksPerDay))); } } // Show total cost modifiers float totalCostModifier = 0f; stringBuilder.Append(WorldObjectComp_ConstructionSite.CostModifersDescription(parentSite.Tile, parentSite.GetNextLeg().Tile, ref totalCostModifier)); List <Caravan> AllCaravansHere = new List <Caravan>(); Find.WorldObjects.GetPlayerControlledCaravansAt(parentSite.Tile, AllCaravansHere); int ISR2G = 0; foreach (Caravan c in AllCaravansHere) { int caravanISR2G = c.GetComponent <WorldObjectComp_Caravan>().useISR2G(); if (caravanISR2G > ISR2G) { ISR2G = caravanISR2G; } } // Per resource : show costs & how much is left to do foreach (string resourceName in DefModExtension_RotR_RoadDef.allResourcesAndWork) { if (GetCost(resourceName) > 0) { stringBuilder.AppendLine(); string ISR2Gmsg = ""; if (ISR2G > 0) { if (resourceName == "Work") { ISR2Gmsg = (ISR2G == 1 ? "RoadsOfTheRim_ConstructionSiteDescription_ISR2Gwork".Translate() : "RoadsOfTheRim_ConstructionSiteDescription_AISR2Gwork".Translate()); } else if (DefModExtension_RotR_RoadDef.GetInSituModifier(resourceName, ISR2G)) { ISR2Gmsg = (ISR2G == 1 ? "RoadsOfTheRim_ConstructionSiteDescription_ISR2GFree".Translate() : "RoadsOfTheRim_ConstructionSiteDescription_AISR2GFree".Translate()); } } stringBuilder.Append("RoadsOfTheRim_ConstructionSiteDescription_Resource".Translate( resourceName, String.Format((resourceName == "Work" ? "{0:##.00}" : "{0:##}"), GetLeft(resourceName)), // Only Work should be shown with 2 decimals (int)GetCost(resourceName), ISR2Gmsg )); } } return(stringBuilder.ToString()); }
public override void DoWindowContents(Rect inRect) { if (Event.current.isKey && site != null) { RoadsOfTheRim.DeleteConstructionSite(site.Tile); Close(); } //Resources icons for (int i = 0; i < 9; i++) { // Icon Rect ResourceRect = new Rect(0, 202f + i * 40f, 32f, 32f); ThingDef theDef; switch (i) { case 0: theDef = ThingDefOf.Spark; break; case 1: theDef = ThingDefOf.WoodLog; break; case 2: theDef = ThingDefOf.BlocksGranite; break; case 3: theDef = ThingDefOf.Steel; break; case 4: theDef = ThingDefOf.Chemfuel; break; case 5: theDef = ThingDefOf.Plasteel; break; case 6: theDef = ThingDefOf.Uranium; break; case 7: theDef = ThingDefOf.ComponentIndustrial; break; default: theDef = ThingDefOf.ComponentSpacer; break; } if (i == 0) { Widgets.ButtonImage(ResourceRect, ContentFinder <Texture2D> .Get("UI/Commands/AddConstructionSite")); } else { Widgets.ThingIcon(ResourceRect, theDef); } } // Sections : one per type of buildable road int nbOfSections = 0; Vector2 groupSize = new Vector2(144, 512 + 128); foreach (RoadDef aDef in buildableRoads) { DefModExtension_RotR_RoadDef roadDefExtension = aDef.GetModExtension <DefModExtension_RotR_RoadDef>(); // Check if a tech is necessary to build this road, don't display the road if it isn't researched yet ResearchProjectDef NeededTech = roadDefExtension.techNeededToBuild; bool TechResearched = false; if (NeededTech != null) { TechResearched = NeededTech.IsFinished; } else { TechResearched = true; } if (TechResearched) { GUI.BeginGroup(new Rect(new Vector2(64 + 144 * nbOfSections, 32f), groupSize)); // Buildable Road icon Texture2D theButton = ContentFinder <Texture2D> .Get("UI/Commands/Build_" + aDef.defName, true); Rect ButtonRect = new Rect(8, 8, 128, 128); if (Widgets.ButtonImage(ButtonRect, theButton)) { if (Event.current.button == 0) { SoundStarter.PlayOneShotOnCamera(SoundDefOf.Tick_High, null); site.roadDef = aDef; Close(); RoadsOfTheRim.RoadBuildingState.CurrentlyTargeting = site; RoadsOfTheRim.RoadBuildingState.Caravan = caravan; RoadConstructionLeg.Target(site); } } // Buildable Road label Text.Anchor = TextAnchor.MiddleCenter; Text.Font = GameFont.Medium; Rect NameRect = new Rect(0, 144, 144f, 32f); Widgets.Label(NameRect, aDef.label); // Resources amounts Text.Font = GameFont.Small; int i = 0; foreach (string resourceName in DefModExtension_RotR_RoadDef.allResourcesAndWork) { Rect ResourceAmountRect = new Rect(0, 176f + i++ *40f, 144f, 32f); Widgets.Label(ResourceAmountRect, (roadDefExtension.GetCost(resourceName) > 0) ? (roadDefExtension.GetCost(resourceName) * ((float)RoadsOfTheRim.settings.BaseEffort / 10)).ToString() : "-" ); } GUI.EndGroup(); nbOfSections++; } } Text.Anchor = TextAnchor.UpperLeft; }