protected ExposableList <Speciality> getTileNeighboringSpecialities(int tile, int timeLimit, float moveCost, Func <int, int, Speciality, bool> extraValidator = null)
        {
            ExposableList <Speciality> specialityList = new ExposableList <Speciality>();

            specialityList.Exposer = (List <Speciality> list) => {
                Scribe_Collections.Look <Speciality>(ref list, "data", LookMode.Reference);
            };
            WorldGrid grid = Find.WorldGrid;
            Dictionary <int, float> timeCostToTile = new Dictionary <int, float>();

            Find.WorldFloodFiller.FloodFill(tile, (int currentPlace) => {
                if (Find.World.Impassable(currentPlace))
                {
                    return(false);
                }
                Tile tileObject = grid.tiles[currentPlace];
                if (tileObject == null || tileObject.WaterCovered || tileObject.hilliness == Hilliness.Impassable)
                {
                    return(false);
                }
                if (currentPlace == tile)
                {
                    timeCostToTile[currentPlace] = 0;
                    return(true);
                }
                List <int> neighbors = new List <int>(6);
                grid.GetTileNeighbors(currentPlace, neighbors);
                float bestNeighborTimeCost = -1;
                int bestNeighbor           = currentPlace;
                foreach (int neighborTile in neighbors)
                {
                    if (timeCostToTile.ContainsKey(neighborTile))
                    {
                        if (bestNeighborTimeCost < 0)
                        {
                            bestNeighborTimeCost = timeCostToTile[neighborTile];
                        }
                        if (timeCostToTile[neighborTile] < bestNeighborTimeCost)
                        {
                            bestNeighborTimeCost = timeCostToTile[neighborTile];
                            bestNeighbor         = neighborTile;
                        }
                    }
                }
                float timeCostToCurrentPlace = bestNeighborTimeCost + getMoveCost(bestNeighbor, currentPlace, moveCost);
                if (timeCostToCurrentPlace <= timeLimit)
                {
                    timeCostToTile[currentPlace] = timeCostToCurrentPlace;
                    return(true);
                }
                else
                {
                    return(false);
                }
            }, (int currentPlace, int distanceInTiles) => {
                Speciality speciality = getTileSpeciality(currentPlace);
                if (speciality != null)
                {
                    specialityList.Add(speciality);
                }
                if (extraValidator != null)
                {
                    return(extraValidator(currentPlace, distanceInTiles, speciality));
                }
                return(false);
            });
            return(specialityList);
        }
        private void generateSpecialities(string seed)
        {
            float chanceAnimal           = RimEconomy.SettingFloat["specialityChanceAnimal"].Value;
            float chancePlant            = RimEconomy.SettingFloat["specialityChancePlant"].Value;
            float chanceResourceRock     = RimEconomy.SettingFloat["specialityChanceResourceRock"].Value;
            bool  dontFilterSpeciality   = RimEconomy.SettingBool["dontFilterSpeciality"].Value;
            float maxCommonalityOfAnimal = RimEconomy.SettingFloat["maxCommonalityOfAnimal"].Value;
            float maxCommonalityOfPlant  = RimEconomy.SettingFloat["maxCommonalityOfPlant"].Value;

            if (seed != null)
            {
                Rand.Seed = GenText.StableStringHash(seed);
            }
            List <Tile> tiles = Find.WorldGrid.tiles;
            Dictionary <BiomeDef, IEnumerable <ThingDef> >    biomePlantCache  = new Dictionary <BiomeDef, IEnumerable <ThingDef> >();
            Dictionary <BiomeDef, IEnumerable <PawnKindDef> > biomeAnimalCache = new Dictionary <BiomeDef, IEnumerable <PawnKindDef> >();
            IEnumerable <ThingDef> resourceRocks = from d in DefDatabase <ThingDef> .AllDefs
                                                   where d.category == ThingCategory.Building && d.building != null && d.building.isResourceRock
                                                   select d;

            for (int i = 0; i <= Find.WorldGrid.TilesCount - 1; i++)
            {
                Tile tile = tiles[i];
                if (!tile.WaterCovered)
                {
                    BiomeDef    biome         = tile.biome;
                    PawnKindDef animalKindDef = null;
                    ThingDef    plantDef      = null;
                    ThingDef    resourceRock  = null;
                    if (Rand.Chance(chanceAnimal * biome.animalDensity))
                    {
                        IEnumerable <PawnKindDef> animals = null;
                        if (biomeAnimalCache.ContainsKey(biome))
                        {
                            animals = biomeAnimalCache[biome];
                        }
                        else
                        {
                            animals = biome.AllWildAnimals;
                            biomeAnimalCache[biome] = animals;
                            Log.Message("-----------deubg for animal----------------");
                            Log.Message("biome: " + biome);
                            foreach (PawnKindDef def in animals)
                            {
                                Log.Message("def: " + def.race + " commonality: " + biome.CommonalityOfAnimal(def) / def.wildSpawn_GroupSizeRange.Average);
                            }
                        }
                        animalKindDef = animals.RandomElementByWeight((PawnKindDef def) => {
                            if (def.race == DefDatabase <ThingDef> .GetNamed("Rat", true))
                            {
                                return(0);
                            }
                            if (def.wildSpawn_GroupSizeRange.Average > 0)
                            {
                                float commonality = biome.CommonalityOfAnimal(def) / def.wildSpawn_GroupSizeRange.Average;
                                if (!dontFilterSpeciality && commonality >= maxCommonalityOfAnimal)
                                {
                                    return(0);
                                }
                                return(commonality);
                            }
                            return(0);
                        });
                    }
                    if (Rand.Chance(chancePlant * biome.plantDensity))
                    {
                        IEnumerable <ThingDef> plants = null;
                        if (biomePlantCache.ContainsKey(biome))
                        {
                            plants = biomePlantCache[biome];
                        }
                        else
                        {
                            plants = from ThingDef def in biome.AllWildPlants
                                     where def.plant != null && (def.plant.harvestedThingDef != null || (def.plant.sowTags != null && def.plant.sowTags.Contains("Ground")))
                                     select def;
                            biomePlantCache[biome] = plants;
                            Log.Message("-----------deubg for plant----------------");
                            Log.Message("biome: " + biome);
                            foreach (ThingDef def in plants)
                            {
                                Log.Message("def: " + def + " commonality: " + biome.CommonalityOfPlant(def));
                            }
                        }
                        plantDef = plants.RandomElementByWeight((ThingDef def) => {
                            if (def == DefDatabase <ThingDef> .GetNamed("PlantGrass", true))
                            {
                                return(0);
                            }
                            float commonality = biome.CommonalityOfPlant(def);
                            if (!dontFilterSpeciality && commonality >= maxCommonalityOfPlant)
                            {
                                return(0);
                            }
                            return(commonality);
                        });
                    }
                    if (Rand.Chance(chanceResourceRock))
                    {
                        resourceRock = resourceRocks.RandomElementByWeight((ThingDef def) => def.building.mineableScatterCommonality);
                    }
                    if (animalKindDef != null || plantDef != null || resourceRock != null)
                    {
                        tileSpeciality[i] = new Speciality(i, animalKindDef, plantDef, resourceRock);
                    }
                }
            }
        }
        public List <Speciality> getSpecialityList()
        {
            Settlement settlement = TradeSession.trader as Settlement;

            if (settlement == null)
            {
                return(new List <Speciality>());
            }
            List <Speciality> specialityList;

            if (specialityLists.ContainsKey(settlement))
            {
                specialityList = specialityLists[settlement];
            }
            else
            {
                specialityList = new List <Speciality>();
                int                    ticksPerDay            = 60000 / 24 * 14;
                WorldGrid              grid                   = Find.WorldGrid;
                WorldPathFinder        finder                 = Find.WorldPathFinder;
                SpecialityWorldManager specialityWorldManager = Find.World.GetComponent <SpecialityWorldManager>();
                finder.FloodPathsWithCost(new List <int> {
                    settlement.Tile
                }, (int currentPlace, int neighborPlace) => {
                    float moveCost = 2500;
                    Tile tile      = grid.tiles[neighborPlace];
                    if (tile == null && tile.WaterCovered)
                    {
                        return(99999);
                    }
                    Season season = GenDate.Season(Find.TickManager.TicksGame, grid.LongLatOf(neighborPlace));
                    switch (season)
                    {
                    case Season.Spring:
                        moveCost += tile.biome.pathCost_spring;
                        break;

                    case Season.Summer:
                    case Season.PermanentSummer:
                        moveCost += tile.biome.pathCost_summer;
                        break;

                    case Season.Fall:
                        moveCost += tile.biome.pathCost_fall;
                        break;

                    case Season.Winter:
                    case Season.PermanentWinter:
                        moveCost += tile.biome.pathCost_winter;
                        break;
                    }
                    moveCost *= grid.GetRoadMovementMultiplierFast(currentPlace, neighborPlace);
                    return((int)moveCost);
                }, null, (int currentPlace, float cost) => {
                    if (cost <= ticksPerDay)
                    {
                        Speciality speciality = specialityWorldManager.getSpeciality(currentPlace);
                        if (speciality != null)
                        {
                            specialityList.Add(speciality);
                        }
                        return(false);
                    }
                    else
                    {
                        return(true);
                    }
                });
                specialityLists[settlement] = specialityList;
            }
            return(specialityList);
        }