public static IntVec3 PreCalculateBannerCell() { if (!Info.Active) { Log.Error("[Carnivale] Tried to perform a cell calculation while current carnival is inactive."); return(IntVec3.Invalid); } var map = Info.map; var setupCentre = Info.setupCentre; var baseRadius = Info.baseRadius; var minDistToCentre = baseRadius / 2; var maxDistToCentre = baseRadius + 3f; var minDistSqrdToCentre = (int)(minDistToCentre * minDistToCentre * 2); var maxDistSqrdToCentre = (int)(maxDistToCentre * maxDistToCentre * 2); IntVec3 colonistPos; if (!(colonistPos = CellsUtil.ApproxClosestColonistBuilding(map, setupCentre, ThingDefOf.Door)).IsValid) { colonistPos = CellsUtil.AverageColonistPosition(map); } else { colonistPos = colonistPos.AverageWith(CellsUtil.AverageColonistPosition(map)); } // Initial pass var closestCell = CellRect.CenteredOn(setupCentre, (int)maxDistToCentre).EdgeCells.ClosestCellTo(colonistPos, map); if (Prefs.DevMode) { Log.Message("[Carnivale] bannerCell initial pass: "******"Road")) { adjustedCell += IntVec3.East * 2; } else if ((adjustedCell + IntVec3.West * 2).GetTerrain(map).HasTag("Road")) { adjustedCell += IntVec3.West * 2; } else { adjustedCell += IntVec3.North; } closestCell = adjustedCell; if (Prefs.DevMode) { Log.Message("\t[Carnivale] bannerCell road pass: "******". distSqrdToCentre=" + dist + ", minDistSqrdToCentre=" + minDistSqrdToCentre + ", maxDistSqrdToCentre=" + maxDistSqrdToCentre); } break; } else { if (Prefs.DevMode) { Log.Warning("\t[Carnivale] bannerCell road pass failure: " + rcell + ". distSqrdToCentre=" + dist + ", minDistSqrdToCentre=" + minDistSqrdToCentre + ", maxDistSqrdToCentre=" + maxDistSqrdToCentre); } } } // line of sight pass if (!GenSight.LineOfSight(setupCentre, closestCell, map)) { Func <IntVec3, float> weightLoSSetupCentre = c => 2f / (setupCentre.CountObstructingCellsTo(c, map) + 1f); Func <IntVec3, float> weightLoSColony = c => 1f / (c.CountObstructingCellsTo(colonistPos, map) + 1f); Func <IntVec3, float> weightBest = c => (weightLoSSetupCentre(c) == 2f ? 2f : 0f) + (weightLoSColony(c) == 1f ? 1f : 0f); var candidateCells = CellTriangle .FromTarget(setupCentre, closestCell, 45f, maxDistToCentre) .Where(c => c.InBounds(map) && c.DistanceToSquared(setupCentre) >= minDistSqrdToCentre); try { closestCell = candidateCells.MaxBy(weightBest); if (Prefs.DevMode) { Log.Message("\t[Carnivale] bannerCell optimal LoS pass: "******"\t[Carnivale] bannerCell sub-optimal LoS pass: "******"\t[Carnivale] bannerCell failed LoS passes. Is candidateCells empty? Leaving it at: " + closestCell); } } } // Mountain proximity pass var attempts = 0; IntVec3 nearestMineable; while (attempts < 10 && closestCell.DistanceSquaredToNearestMineable(map, 12, out nearestMineable) <= 36) { closestCell = CellRect.CenteredOn(closestCell, 5).FurthestCellFrom(nearestMineable); attempts++; if (Prefs.DevMode) { Log.Message("\t[Carnivale] bannerCell mountain proximity pass #" + attempts + ": " + closestCell); } } if (attempts == 10 && Prefs.DevMode) { Log.Warning("\t[Carnivale] bannerCell mountain proximity passes took too many tries. Leaving it at: " + closestCell); } // End passes if (Prefs.DevMode) { Log.Message("[Carnivale] bannerCell pre-buildability pass: " + closestCell); } return(closestCell); }
private static bool TryCarnivalSetupPosition_Triangular(IntVec3 initPos, IntVec3 colPos, int distSqrToColony, Map map, out IntVec3 result) { var minDistSqrToColony = MinDistToColony * MinDistToColony * 2; var halfAngle = Mathf.Lerp(30f, 10f, distSqrToColony / map.Size.LengthHorizontalSquared); IntVec3 roadPos; if (!map.AnyRoads() || !initPos .TranslateToward(colPos, c => !c.InNoBuildEdgeArea(map)) .TryFindNearestRoadCell(map, 15f, out roadPos, c => !c.InNoBuildEdgeArea(map))) { roadPos = initPos; } var mapRect = CellRect.WholeMap(map).ContractedBy(MinDistToMapEdge); var candidateTri = CellTriangle .FromTarget(colPos, roadPos, halfAngle, map.Size.x / 2) .ClipInside(mapRect); if (Prefs.DevMode) { Log.Message("\t[Carnivale] triangular setupSpot: MinDistToMapEdge=" + MinDistToMapEdge + ", minDistSqrToColony=" + minDistSqrToColony + ", halfAngle=" + halfAngle + ", roadPos=" + roadPos); candidateTri.DebugFlashDraw(); } Func <IntVec3, bool> reachable = c => map.reachability.CanReachColony(c); Func <IntVec3, bool> validDist = c => c.DistanceToSquared(colPos) >= minDistSqrToColony; Func <IntVec3, bool> validRoad = c => !map.AnyRoads() || c.DistanceSquaredToNearestRoad(map, Info.baseRadius * 1.5f) < int.MaxValue; Func <IntVec3, float> weightBuildable = c => 1f / (c.CountBadTerrainInRadius(map, 14) + 1f); Func <IntVec3, float> weightDist = c => Rand.Range(0, 20) + distSqrToColony / (c.DistanceToSquared(colPos) + 1f); var maxWeight = 0f; foreach (var cell in candidateTri.InRandomOrder().Where(c => validDist(c) && reachable(c) && validRoad(c))) { if (Prefs.DevMode) { map.debugDrawer.FlashCell(cell); } var weight = weightBuildable(cell) + weightDist(cell); var thresh = Rand.Range(19, 24); if (weight > maxWeight) { maxWeight = weight; } if (weight >= thresh) { result = cell; if (Prefs.DevMode) { Log.Message("\t[Carnivale] triangular setupSpot: successfully found cell. result=" + result + ", weight=" + weight); } return(true); } } if (Prefs.DevMode) { Log.Warning("\t[Carnivale] triangular setupSpot: unable to find cell in candidates. maxWeight=" + maxWeight); } result = IntVec3.Invalid; return(false); }