private static Blueprint PlaceEntranceBlueprint()
        {
            if (!availableCrates.Any(c => c.def == _DefOf.Carn_Crate_Stall))
            {
                return(null);
            }

            ThingDef bannerDef = _DefOf.Carn_SignEntry;
            Rot4     rot       = default(Rot4);

            IntVec3 bannerSpot = FindRadialPlacementFor(bannerDef, rot, info.bannerCell, 16);

            if (bannerSpot.IsValid)
            {
                info.bannerCell = bannerSpot;

                if (Prefs.DevMode)
                {
                    Log.Message("[Carnivale] bannerCell final pass: "******"Couldn't find an optimum place for " + bannerDef + ". Trying random place in carnival area.");
            bannerSpot = FindRandomPlacementFor(bannerDef, rot);

            if (bannerSpot.IsValid)
            {
                info.bannerCell = bannerSpot;

                if (Prefs.DevMode)
                {
                    Log.Message("[Carnivale] bannerCell final pass: "******"Couldn't find any place for " + bannerDef + ". Not retrying.");
            return(null);
        }
        private static Blueprint PlaceTrashBlueprint()
        {
            var     signDef = _DefOf.Carn_SignTrash;
            var     noGo    = CellRect.CenteredOn(info.bannerCell, 10);
            IntVec3 trashPos;

            if (!CellRect.CenteredOn(info.bannerCell, 15).Cells
                .Where(c => !noGo.Contains(c))
                .TryRandomElement(out trashPos))
            {
                if (Prefs.DevMode)
                {
                    Log.Message("[Carnivale] New trash position calculation failed. Using old method.");
                }

                trashPos = info.carnivalArea.ContractedBy(5).FurthestCellFrom(cachedPos.Average(), true, delegate(IntVec3 c)
                {
                    if (noGo.Contains(c))
                    {
                        return(false);
                    }

                    return(true);
                });
            }

            if (!CanPlaceBlueprintAt(trashPos, signDef))
            {
                trashPos = FindRadialPlacementFor(signDef, default(Rot4), trashPos, 10);
            }

            if (!trashPos.IsValid)
            {
                Log.Error("[Carnivale] Could not find any place for a trash spot. Trash will not be hauled.");
                info.TrashCentre = IntVec3.Invalid;
                return(null);
            }

            info.TrashCentre = trashPos;

            RemoveFirstCrateOf(ThingDefOf.WoodLog);
            CarnUtils.ClearThingsFor(info.map, trashPos, new IntVec2(4, 4), default(Rot4));
            return(PlaceBlueprint(signDef, trashPos, default(Rot4), ThingDefOf.WoodLog));
        }
        private static IEnumerable <Blueprint> PlaceGameBlueprints()
        {
            var gameMasters = stallUsers.Where(p => p.TraderKind == null).ToList();
            var gameCrates  = availableCrates.ListFullCopy().Where(c => c.def.entityDefToBuild != null);

            var points = new IntVec3[]
            {
                info.setupCentre,
                info.bannerCell
            };

            var gameSpot = CellRect.CenteredOn(points.Average(), 7).FurthestCellFrom(info.carnivalArea.EdgeCells.RandomElement());

            ThingDef gameDef;
            int      i = 0;

            foreach (var crate in gameCrates)
            {
                gameDef = (ThingDef)crate.def.entityDefToBuild;

                gameSpot = FindRadialPlacementFor(gameDef, default(Rot4), gameSpot, 20);

                if (gameSpot.IsValid)
                {
                    RemoveFirstCrateOf(crate.def);
                    CarnUtils.ClearThingsFor(info.map, gameSpot, gameDef.size, default(Rot4), 2);

                    if (i < gameMasters.Count)
                    {
                        IntVec3 gameMasterSpot = gameSpot + gameDef.interactionCellOffset + new IntVec3(-1, 0, 1);
                        info.rememberedPositions.Add(gameMasters[i++], gameMasterSpot);
                    }

                    yield return(PlaceBlueprint(gameDef, gameSpot));
                }
                else
                {
                    Log.Error("Found no place for " + gameDef + ". It will not be built.");
                }
            }
        }
        private static IEnumerable <Blueprint> PlaceTentBlueprints()
        {
            // main chapiteau
            ThingDef tentDef;
            Rot4     rot = info.setupCentre.RotationFacing(info.bannerCell).Opposite;
            IntVec3  tentSpot;

            if (availableCrates.Any(c => c.def == _DefOf.Carn_Crate_TentHuge))
            {
                tentDef  = _DefOf.Carn_TentChap;
                tentSpot = FindRadialPlacementFor(tentDef, rot, info.setupCentre, 11);
                if (tentSpot.IsValid)
                {
                    RemoveFirstCrateOf(_DefOf.Carn_Crate_TentHuge);
                    CarnUtils.ClearThingsFor(info.map, tentSpot, tentDef.size, rot, 2);
                    yield return(PlaceBlueprint(tentDef, tentSpot, rot));
                }
                else
                {
                    Log.Error("Could not find placement for " + tentDef + ", which is a major attraction. Tell Xnope to get it together.");
                }
            }



            // lodging tents
            tentDef  = _DefOf.Carn_TentLodge;
            rot      = Rot4.Random;
            tentSpot = FindRadialPlacementFor(tentDef, rot, info.carnivalArea.ContractedBy(9).FurthestCellFrom(CellsUtil.AverageColonistPosition(info.map)), 7);

            IntVec3 lineDirection = rot.ToIntVec3(1); // shifted clockwise by 1

            int  numFailures  = 0;
            bool firstNewPass = true;

            while (numFailures < 30 && availableCrates.Any(t => t.def == _DefOf.Carn_Crate_TentLodge))
            {
                // Following works as intended iff size.x == size.y

                if (!firstNewPass)
                {
                    // try adding tent next in a an adjacent line
                    tentSpot += lineDirection * ((tentDef.size.x + 1));
                }

                if (CanPlaceBlueprintAt(tentSpot, tentDef, rot))
                {
                    // bingo
                    firstNewPass = false;
                    RemoveFirstCrateOf(_DefOf.Carn_Crate_TentLodge);
                    CarnUtils.ClearThingsFor(info.map, tentSpot, tentDef.size, rot, 1);
                    yield return(PlaceBlueprint(tentDef, tentSpot, rot));
                }
                else if (numFailures % 3 != 0)
                {
                    // try different line directions
                    rot.AsByte++;
                    lineDirection = rot.ToIntVec3(1);
                    numFailures++;
                }
                else
                {
                    // Find new placement if next spot and any of its rotations don't work
                    tentSpot = FindRadialPlacementFor(tentDef, rot, tentSpot, (int)info.baseRadius / 2);

                    if (!tentSpot.IsValid)
                    {
                        // suboptimal random placement
                        tentSpot = FindRandomPlacementFor(tentDef, rot, false, 5);
                    }

                    firstNewPass = true;
                }
            }

            if (numFailures == 30 && availableCrates.Any(t => t.def == _DefOf.Carn_Crate_TentLodge))
            {
                Log.Error("Tried too many times to place tents. Some may not be built.");
            }

            // manager tent
            if (!availableCrates.Any(c => c.def == _DefOf.Carn_Crate_TentMan))
            {
                yield break;
            }

            rot     = Rot4.Random;
            tentDef = _DefOf.Carn_TentLodgeMan;

            // Try to place near other tents
            tentSpot = FindRadialCardinalPlacementFor(tentDef, rot, tentSpot, 10);

            if (tentSpot.IsValid)
            {
                RemoveFirstCrateOf(_DefOf.Carn_Crate_TentMan);
                CarnUtils.ClearThingsFor(info.map, tentSpot, tentDef.size, rot, 1);
                yield return(PlaceBlueprint(tentDef, tentSpot, rot));
            }
            else
            {
                // suboptimal placement
                tentSpot = FindRadialPlacementFor(tentDef, rot, info.setupCentre, (int)info.baseRadius / 2);
                if (tentSpot.IsValid)
                {
                    RemoveFirstCrateOf(_DefOf.Carn_Crate_TentMan);
                    CarnUtils.ClearThingsFor(info.map, tentSpot, tentDef.size, rot, 1);
                    yield return(PlaceBlueprint(tentDef, tentSpot, rot));
                }
                else
                {
                    Log.Error("Found no valid placement for manager tent. It will not be placed.");
                }
            }
        }
        private static IEnumerable <Blueprint> PlaceStallBlueprints()
        {
            ThingDef stallDef  = _DefOf.Carn_StallFood;
            IntVec3  stallSpot = IntVec3.Invalid;
            Rot4     rot       = default(Rot4);

            foreach (Pawn stallUser in stallUsers.Where(p => p.TraderKind != null))
            {
                // Handle different kinds of vendor stalls

                if (stallUser.TraderKind == _DefOf.Carn_Trader_Food)
                {
                    stallDef = _DefOf.Carn_StallFood;
                }
                else if (stallUser.TraderKind == _DefOf.Carn_Trader_Surplus)
                {
                    stallDef = _DefOf.Carn_StallSurplus;
                }
                else if (stallUser.TraderKind == _DefOf.Carn_Trader_Curios)
                {
                    stallDef = _DefOf.Carn_StallCurios;
                }
                else
                {
                    Log.Error("Trader " + stallUser.NameStringShort + " is not a carnival vendor and will get no stall.");
                    continue;
                }



                if (stallSpot.IsValid)
                {
                    if (Rand.Chance(0.8f))
                    {
                        // Next spot should be close to last spot
                        stallSpot = FindRadialCardinalPlacementFor(stallDef, rot, stallSpot, 10);
                    }
                    else
                    {
                        // Next spot should be random
                        IntVec3[] points = new IntVec3[]
                        {
                            stallSpot,
                            stallSpot,
                            info.carnivalArea.EdgeCells.RandomElement()
                        };

                        stallSpot = FindRadialPlacementFor(stallDef, rot, points.Average(), 10);
                    }
                }
                else
                {
                    IntVec3[] points = new IntVec3[]
                    {
                        info.setupCentre,
                        info.bannerCell
                    };

                    stallSpot = FindRadialPlacementFor(stallDef, rot, points.Average(), 10);
                }


                // Finally, spawn the f*cker
                if (stallSpot.IsValid)
                {
                    RemoveFirstCrateOf(_DefOf.Carn_Crate_Stall);
                    CarnUtils.ClearThingsFor(info.map, stallSpot, stallDef.size, rot, 2);
                    // Add spot to stall user's spot
                    info.rememberedPositions.Add(stallUser, stallSpot);
                    yield return(PlaceBlueprint(stallDef, stallSpot, rot));
                }
            }
        }