FishIndustry utility class.
        public static string GetSpeciesInZoneText(Map map, int oceanCellsCount, int riverCellsCount, int marshCellsCount)
        {
            StringBuilder speciesInZone = new StringBuilder();

            foreach (PawnKindDef_FishSpecies species in Util_FishIndustry.GetFishSpeciesList(map.Biome))
            {
                if ((oceanCellsCount > 0) &&
                    (species.livesInOcean))
                {
                    speciesInZone.AppendWithComma(species.label);
                }
                else if ((riverCellsCount > 0) &&
                         (species.livesInRiver))
                {
                    speciesInZone.AppendWithComma(species.label);
                }
                else if ((marshCellsCount > 0) &&
                         (species.livesInMarsh))
                {
                    speciesInZone.AppendWithComma(species.label);
                }
            }
            if (speciesInZone.Length == 0)
            {
                speciesInZone.Append("none");
            }
            return(speciesInZone.ToString());
        }
Exemple #2
0
        public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
        {
            if ((t is Building_FishingPier) == false)
            {
                return(false);
            }
            Building_FishingPier fishingPier = t as Building_FishingPier;

            if (fishingPier.IsBurning())
            {
                return(false);
            }
            if (Util_FishIndustry.IsAquaticTerrain(fishingPier.Map, fishingPier.fishingSpotCell) == false)
            {
                return(false);
            }
            if (pawn.Dead ||
                pawn.Downed ||
                pawn.IsBurning())
            {
                return(false);
            }
            if (pawn.CanReserveAndReach(fishingPier, this.PathEndMode, Danger.Some) == false)
            {
                return(false);
            }
            if (fishingPier.fishStock <= 0)
            {
                return(false);
            }
            return(true);
        }
Exemple #3
0
        // ===================== Inspection pannel functions =====================
        /// <summary>
        /// Get the string displayed in the inspection panel.
        /// </summary>
        public override string GetInspectString()
        {
            StringBuilder stringBuilder = new StringBuilder();

            if (Util_FishIndustry.GetFishSpeciesList(this.Map.Biome).NullOrEmpty())
            {
                stringBuilder.Append("FishIndustry.FishingPier_InvalidBiome".Translate());
                return(stringBuilder.ToString());
            }

            if (this.maxFishStock < 0)
            {
                // Update after a savegame loading for example.
                UpdateAquaticCellsAround();
                Util_Zone_Fishing.UpdateZoneProperties(this.Map, this.Cells, ref this.oceanCellsCount, ref this.riverCellsCount, ref this.marshCellsCount,
                                                       ref this.isAffectedByBiome, ref this.isAffectedByToxicFallout, ref this.isAffectedByBadTemperature, ref this.maxFishStock);
            }
            // Fish stock.
            stringBuilder.Append("FishIndustry.FishStock".Translate(this.fishesPosition.Count));
            // Status.
            stringBuilder.AppendLine();
            if (this.viableCellsCount < Util_Zone_Fishing.minCellsToSpawnFish)
            {
                stringBuilder.Append("FishIndustry.NotViableNow".Translate());
            }
            else
            {
                stringBuilder.Append("FishIndustry.SpeciesInZone".Translate() + Util_Zone_Fishing.GetSpeciesInZoneText(this.Map, this.oceanCellsCount, this.riverCellsCount, this.marshCellsCount));
            }
            // Affections.
            if (this.Cells.Count < Util_Zone_Fishing.minCellsToSpawnFish)
            {
                stringBuilder.AppendLine();
                stringBuilder.Append("FishIndustry.TooSmallZone".Translate());
            }
            if (this.isAffectedByBiome ||
                this.isAffectedByToxicFallout ||
                this.isAffectedByBadTemperature)
            {
                stringBuilder.AppendLine();
                stringBuilder.Append("FishIndustry.AffectedBy".Translate());
                StringBuilder effects = new StringBuilder();
                if (this.isAffectedByBiome)
                {
                    effects.Append("FishIndustry.AffectedByBiome".Translate());
                }
                if (this.isAffectedByToxicFallout)
                {
                    effects.AppendWithSeparator("FishIndustry.AffectedByToxicFallout".Translate(), "FishIndustry.AffectedBySeparator".Translate());
                }
                if (this.isAffectedByBadTemperature)
                {
                    effects.AppendWithSeparator("FishIndustry.AffectedByBadTemperature".Translate(), "FishIndustry.AffectedBySeparator".Translate());
                }
                stringBuilder.Append(effects);
            }
            return(stringBuilder.ToString());
        }
        /// <summary>
        /// Update the max fishing stock according to terrain and biome (to avoid exploits due to terrain changes).
        /// </summary>
        public void UpdateMaxFishStock()
        {
            int maxFishStockTerrain = maxFishStockDefault;

            // Compute max fish stock according to surrounding aquatic cells.
            float aquaticCellsNumber = 0;

            foreach (IntVec3 cell in GenRadial.RadialCellsAround(this.Position, this.def.specialDisplayRadius, true))
            {
                if (cell.InBounds(this.Map) == false)
                {
                    continue;
                }
                if (Util_FishIndustry.IsAquaticTerrain(this.Map, cell))
                {
                    aquaticCellsNumber++;
                }
            }
            float aquaticCellsNumberThreshold = (float)(GenRadial.NumCellsInRadius(this.def.specialDisplayRadius)) / 2f;

            if (aquaticCellsNumber < aquaticCellsNumberThreshold)
            {
                maxFishStockTerrain = Mathf.CeilToInt((float)maxFishStockDefault * (aquaticCellsNumber / aquaticCellsNumberThreshold));
            }

            // Compute max fish stock according to biome.
            int maxFishStockBiome = maxFishStockDefault;

            if ((this.Map.Biome == BiomeDef.Named("AridShrubland")) ||
                (this.Map.Biome == BiomeDef.Named("Tundra")))
            {
                maxFishStockBiome = 3;
            }
            else if ((this.Map.Biome == BiomeDef.Named("IceSheet")) ||
                     (this.Map.Biome == BiomeDef.Named("Desert")))
            {
                maxFishStockBiome = 2;
            }
            else if ((this.Map.Biome == BiomeDef.Named("SeaIce")) ||
                     (this.Map.Biome == BiomeDef.Named("ExtremeDesert")))
            {
                maxFishStockBiome = 1;
            }
            this.maxFishStock = Math.Min(maxFishStockTerrain, maxFishStockBiome);
            if (this.maxFishStock < 1)
            {
                this.maxFishStock = 1;
            }
        }
Exemple #5
0
        /// <summary>
        /// Compute the max fishing stock and fish respawn rate according to terrain and biome (to avoid exploits due to terrain changes).
        /// </summary>
        public void ComputeMaxFishStockAndRespawnPeriod()
        {
            // Compute max fish stock according to biome.
            this.maxFishStock = maxFishStockDefault;
            if (this.Map.Biome == BiomeDefOf.BorealForest)
            {
                this.maxFishStock = 4;
            }
            else if ((this.Map.Biome == BiomeDefOf.Tundra) ||
                     (this.Map.Biome == BiomeDefOf.AridShrubland))
            {
                this.maxFishStock = 3;
            }
            else if ((this.Map.Biome == BiomeDefOf.IceSheet) ||
                     (this.Map.Biome == BiomeDefOf.Desert))
            {
                this.maxFishStock = 2;
            }
            else if ((this.Map.Biome == BiomeDefOf.SeaIce) ||
                     (this.Map.Biome == BiomeDef.Named("ExtremeDesert")))
            {
                this.maxFishStock = 1;
            }

            // Compute fish stock respawn period factor according  to surrounding aquatic cells.
            float aquaticCellsProportion = Util_FishIndustry.GetAquaticCellsProportionInRadius(this.Position, this.Map, Building_FishingPier.optimalAquaticAreaRadius);
            float fishRespawnFactor      = 1f;

            if (aquaticCellsProportion < optimalAquaticCellsProportion)
            {
                fishRespawnFactor = (aquaticCellsProportion / optimalAquaticCellsProportion);
            }
            if (fishRespawnFactor <= 0)
            {
                // Avoid division by 0.
                fishRespawnFactor = 0.05f;
            }

            this.fishStockRespawnInterval = Mathf.CeilToInt((2f * GenDate.TicksPerDay) / ((float)this.maxFishStock * fishRespawnFactor));
        }
        // ===================== Inspection pannel functions =====================
        /// <summary>
        /// Get the inspection string.
        /// </summary>
        ///
        public override string GetInspectString()
        {
            StringBuilder stringBuilder = new StringBuilder();

            if (Util_FishIndustry.GetFishSpeciesList(this.Map.Biome).NullOrEmpty())
            {
                stringBuilder.Append("FishIndustry.FishingPier_InvalidBiome".Translate());
                return(stringBuilder.ToString());
            }

            if (this.maxFishStock < 0)
            {
                // Update after a savegame loading for example.
                UpdateCells();
                Util_Zone_Fishing.UpdateZoneProperties(this.Map, this.aquaticCells, ref this.oceanCellsCount, ref this.riverCellsCount, ref this.marshCellsCount,
                                                       ref this.isAffectedByBiome, ref this.isAffectedByToxicFallout, ref this.isAffectedByBadTemperature, ref this.maxFishStock);
                this.cachedSpeciesInZone = Util_Zone_Fishing.GetSpeciesInZoneText(this.Map.Biome, this.oceanCellsCount, this.riverCellsCount, this.marshCellsCount);
            }
            // Fish stock.
            stringBuilder.Append("FishIndustry.FishStock".Translate(this.fishStock));
            if (Prefs.DevMode)
            {
                stringBuilder.Append("/" + this.maxFishStock);
            }
            // Status.
            stringBuilder.AppendLine();
            if (this.viableCellsCount < Util_Zone_Fishing.minCellsToSpawnFish)
            {
                stringBuilder.Append("FishIndustry.NotEnoughViableSpace".Translate());
            }
            else
            {
                stringBuilder.Append("FishIndustry.SpeciesInZone".Translate() + this.cachedSpeciesInZone);
            }
            // Affections.
            if (this.isAffectedByBiome ||
                this.isAffectedByToxicFallout ||
                this.isAffectedByBadTemperature)
            {
                stringBuilder.AppendLine();
                stringBuilder.Append("FishIndustry.AffectedBy".Translate());
                StringBuilder effects = new StringBuilder();
                if (this.isAffectedByBiome)
                {
                    effects.AppendWithComma("FishIndustry.AffectedByBiome".Translate());
                }
                if (this.isAffectedByToxicFallout)
                {
                    effects.AppendWithComma("FishIndustry.AffectedByToxicFallout".Translate());
                }
                if (this.isAffectedByBadTemperature)
                {
                    effects.AppendWithComma("FishIndustry.AffectedByBadTemperature".Translate());
                }
                stringBuilder.Append(effects);
            }
            // Debug.
            if (Prefs.DevMode)
            {
                stringBuilder.AppendLine();
                stringBuilder.Append("Spawn mean time: " + GenDate.ToStringTicksToPeriod((int)cachedFishSpawnMtb));
            }
            return(stringBuilder.ToString());
        }
        /// <summary>
        /// Check if a new fishing pier can be built at this location.
        /// - the fishing pier bank cell must be on a bank.
        /// - the rest of the fishing pier and the fishing spot must be on water.
        /// - must not be too near from another fishing pier.
        /// </summary>
        public override AcceptanceReport AllowsPlacing(BuildableDef checkingDef, IntVec3 loc, Rot4 rot, Thing thingToIgnore = null)
        {
            // Check fishing pier bank cell is on a "solid" terrain.
            if (Util_FishIndustry.IsAquaticTerrain(this.Map, loc))
            {
                return(new AcceptanceReport("Fishing pier must touch a bank."));
            }
            // Check fishing pier middle and river cells are on water.
            if ((Util_FishIndustry.IsAquaticTerrain(this.Map, loc + new IntVec3(0, 0, 1).RotatedBy(rot)) == false) ||
                (Util_FishIndustry.IsAquaticTerrain(this.Map, loc + new IntVec3(0, 0, 2).RotatedBy(rot)) == false))
            {
                return(new AcceptanceReport("Fishing pier must be placed on water."));
            }
            // Check fishing zone is on water.
            for (int xOffset = -1; xOffset <= 1; xOffset++)
            {
                for (int yOffset = 3; yOffset <= 5; yOffset++)
                {
                    if (Util_FishIndustry.IsAquaticTerrain(this.Map, loc + new IntVec3(xOffset, 0, yOffset).RotatedBy(rot)) == false)
                    {
                        return(new AcceptanceReport("Fishing zone must be placed on water."));
                    }
                }
            }

            // Check if another fishing pier is not too close (mind the test on "fishing pier" def and "fishing pier spawner" blueprint and frame defs.
            List <Thing> fishingPierList = this.Map.listerThings.ThingsOfDef(Util_FishIndustry.FishingPierDef);
            List <Thing> fishingPierSpawnerBlueprintList      = this.Map.listerThings.ThingsOfDef(Util_FishIndustry.FishingPierSpawnerDef.blueprintDef);
            List <Thing> fishingPierSpawnerFrameList          = this.Map.listerThings.ThingsOfDef(Util_FishIndustry.FishingPierSpawnerDef.frameDef);
            List <Thing> fishingPierSpawnerOnMudBlueprintList = this.Map.listerThings.ThingsOfDef(Util_FishIndustry.FishingPierSpawnerOnMudDef.blueprintDef);
            List <Thing> fishingPierSpawnerOnMudFrameList     = this.Map.listerThings.ThingsOfDef(Util_FishIndustry.FishingPierSpawnerOnMudDef.frameDef);

            if (fishingPierList != null)
            {
                IEnumerable <Thing> fishingPierInTheArea = fishingPierList.Where(building => loc.InHorDistOf(building.Position, minDistanceBetweenTwoFishingPiers));
                if (fishingPierInTheArea.Count() > 0)
                {
                    return(new AcceptanceReport("An other fishing pier is too close."));
                }
            }
            if (fishingPierSpawnerBlueprintList != null)
            {
                IEnumerable <Thing> fishingPierBlueprintInTheArea = fishingPierSpawnerBlueprintList.Where(building => loc.InHorDistOf(building.Position, minDistanceBetweenTwoFishingPiers));
                if (fishingPierBlueprintInTheArea.Count() > 0)
                {
                    return(new AcceptanceReport("An other fishing pier blueprint is too close."));
                }
            }
            if (fishingPierSpawnerFrameList != null)
            {
                IEnumerable <Thing> fishingPierFrameInTheArea = fishingPierSpawnerFrameList.Where(building => loc.InHorDistOf(building.Position, minDistanceBetweenTwoFishingPiers));
                if (fishingPierFrameInTheArea.Count() > 0)
                {
                    return(new AcceptanceReport("An other fishing pier frame is too close."));
                }
            }
            if (fishingPierSpawnerOnMudBlueprintList != null)
            {
                IEnumerable <Thing> fishingPierOnMudBlueprintInTheArea = fishingPierSpawnerOnMudBlueprintList.Where(building => loc.InHorDistOf(building.Position, minDistanceBetweenTwoFishingPiers));
                if (fishingPierOnMudBlueprintInTheArea.Count() > 0)
                {
                    return(new AcceptanceReport("An other fishing pier blueprint is too close."));
                }
            }
            if (fishingPierSpawnerOnMudFrameList != null)
            {
                IEnumerable <Thing> fishingPierOnMudFrameInTheArea = fishingPierSpawnerOnMudFrameList.Where(building => loc.InHorDistOf(building.Position, minDistanceBetweenTwoFishingPiers));
                if (fishingPierOnMudFrameInTheArea.Count() > 0)
                {
                    return(new AcceptanceReport("An other fishing pier frame is too close."));
                }
            }

            return(true);
        }
        protected override IEnumerable <Toil> MakeNewToils()
        {
            const float baseFishingDuration    = 1600f;
            const float catchSuccessRateOnPier = 0.90f;
            const float skillGainPerTick       = 0.15f;

            int fishingDuration = (int)baseFishingDuration;
            Building_FishingPier fishingPier = this.TargetThingA as Building_FishingPier;
            int thrownCornCount   = 0;
            int nextCornThrowTick = 0;

            this.FailOnBurningImmobile(fishingPierIndex);

            // Drawing.
            this.rotateToFace = TargetIndex.B;
            this.pawn.CurJob.SetTarget(TargetIndex.C, fishingPier.riverCell);
            TargetIndex riverCellIndex = TargetIndex.C;

            // Compute fishing duration.
            float fishingSkillLevel = 0f;

            fishingSkillLevel = this.pawn.skills.AverageOfRelevantSkillsFor(WorkTypeDefOf.Hunting);
            float fishingSkillDurationFactor = fishingSkillLevel / 20f;

            fishingDuration = (int)(baseFishingDuration * (1.5f - fishingSkillDurationFactor));

            yield return(Toils_Goto.GotoThing(fishingPierIndex, fishingPier.riverCell).FailOn(FishingForbiddenOrPierDestroyedOrNoFish));

            Toil attractfishesToil = new Toil()
            {
                tickAction = () =>
                {
                    if (fishingPier.allowUsingGrain == false)
                    {
                        this.ReadyForNextToil();
                        return;
                    }
                    if (Find.TickManager.TicksGame >= nextCornThrowTick)
                    {
                        nextCornThrowTick = Find.TickManager.TicksGame + 3 * GenTicks.TicksPerRealSecond;
                        // Look for corn in inventory.
                        Thing corn = null;
                        foreach (Thing thing in this.pawn.inventory.innerContainer)
                        {
                            if (thing.def == Util_FishIndustry.RawCornDef)
                            {
                                corn = thing;
                                break;
                            }
                        }
                        if ((corn == null) ||
                            (thrownCornCount >= grainCountToAttractFishes))
                        {
                            fishingDuration -= Mathf.CeilToInt((float)fishingDuration * 0.75f * ((float)thrownCornCount / (float)grainCountToAttractFishes));
                            this.ReadyForNextToil();
                            return;
                        }
                        // Throw corn grains.
                        thrownCornCount++;
                        this.pawn.inventory.innerContainer.Take(corn, 1);
                        for (int cornGrainIndex = 0; cornGrainIndex < 5; cornGrainIndex++)
                        {
                            if (!this.pawn.Position.ShouldSpawnMotesAt(this.pawn.Map) || this.pawn.Map.moteCounter.Saturated)
                            {
                                break;
                            }
                            float   speed          = Rand.Range(1.5f, 3f);
                            Vector3 targetPosition = this.pawn.Position.ToVector3Shifted() + new Vector3(0f, 0f, 2f).RotatedBy(fishingPier.Rotation.AsAngle) + Vector3Utility.RandomHorizontalOffset(1.5f);
                            targetPosition.y = this.pawn.DrawPos.y;
                            MoteThrown moteThrown = (MoteThrown)ThingMaker.MakeThing(ThingDefOf.Mote_Stone, null);
                            moteThrown.Scale         = 0.2f;
                            moteThrown.rotationRate  = (float)Rand.Range(-300, 300);
                            moteThrown.exactPosition = this.pawn.DrawPos;
                            moteThrown.SetVelocity((targetPosition - moteThrown.exactPosition).AngleFlat(), speed);
                            moteThrown.airTimeLeft = (float)Mathf.RoundToInt((moteThrown.exactPosition - targetPosition).MagnitudeHorizontal() / speed);
                            GenSpawn.Spawn(moteThrown, this.pawn.Position, this.pawn.Map);
                            MoteMaker.MakeWaterSplash(targetPosition, this.pawn.Map, 1.8f, 0.5f);
                        }
                    }
                },
                defaultDuration     = fishingDuration,
                defaultCompleteMode = ToilCompleteMode.Delay
            };

            yield return(attractfishesToil.FailOn(FishingForbiddenOrPierDestroyedOrNoFish));

            Toil fishToil = new Toil()
            {
                initAction = () =>
                {
                    ThingDef moteDef = null;
                    if (fishingPier.Rotation == Rot4.North)
                    {
                        moteDef = Util_FishIndustry.MoteFishingRodNorthDef;
                    }
                    else if (fishingPier.Rotation == Rot4.East)
                    {
                        moteDef = Util_FishIndustry.MoteFishingRodEastDef;
                    }
                    else if (fishingPier.Rotation == Rot4.South)
                    {
                        moteDef = Util_FishIndustry.MoteFishingRodSouthDef;
                    }
                    else
                    {
                        moteDef = Util_FishIndustry.MoteFishingRodWestDef;
                    }
                    this.fishingRodMote = (Mote)ThingMaker.MakeThing(moteDef, null);
                    this.fishingRodMote.exactPosition = fishingPier.fishingSpotCell.ToVector3Shifted();
                    this.fishingRodMote.Scale         = 1f;
                    GenSpawn.Spawn(this.fishingRodMote, fishingPier.fishingSpotCell, this.Map);
                },
                tickAction = () =>
                {
                    this.pawn.skills.Learn(SkillDefOf.Shooting, skillGainPerTick);

                    if (this.fishingRodMote != null)
                    {
                        this.fishingRodMote.Maintain();
                    }
                },
                defaultDuration     = fishingDuration,
                defaultCompleteMode = ToilCompleteMode.Delay
            };

            yield return(fishToil.WithProgressBarToilDelay(riverCellIndex).FailOn(FishingForbiddenOrPierDestroyedOrNoFish));

            Toil catchFishToil = new Toil()
            {
                initAction = () =>
                {
                    Thing fishingCatch = null;

                    bool catchIsSuccessful = (Rand.Value <= catchSuccessRateOnPier);
                    if (catchIsSuccessful == false)
                    {
                        MoteMaker.ThrowMetaIcon(this.pawn.Position, this.Map, ThingDefOf.Mote_IncapIcon);
                        this.pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
                        return;
                    }

                    float catchSelectorValue = Rand.Value;

                    if (catchSelectorValue < 0.02)
                    {
                        float bonusCatchValue = Rand.Value;
                        if (bonusCatchValue < 0.01f)
                        {
                            // Really small chance to find a sunken treasure!!!
                            fishingCatch            = GenSpawn.Spawn(ThingDefOf.Gold, this.pawn.Position, this.Map);
                            fishingCatch.stackCount = Rand.RangeInclusive(58, 289);
                            Thing treasureSilver = GenSpawn.Spawn(ThingDefOf.Silver, fishingPier.middleCell, this.Map);
                            treasureSilver.stackCount = Rand.RangeInclusive(237, 2154);
                            Find.LetterStack.ReceiveLetter("FishIndustry.LetterLabelSunkenTreasure".Translate(), "FishIndustry.SunkenTreasure".Translate(this.pawn.Name.ToStringShort.CapitalizeFirst()),
                                                           LetterDefOf.PositiveEvent, this.pawn);
                        }
                        else if (bonusCatchValue < 0.02f)
                        {
                            // Really small chance to find a complete power armor set + sniper or charge rifle.
                            Thing powerArmor = GenSpawn.Spawn(ThingDef.Named("Apparel_PowerArmor"), this.pawn.Position, this.Map);
                            fishingCatch = powerArmor; // Used to carry the power armor.
                            Thing powerArmorHelmet = GenSpawn.Spawn(ThingDef.Named("Apparel_PowerArmorHelmet"), this.pawn.Position, this.Map);
                            Thing rifle            = null;
                            if (Rand.Value < 0.5f)
                            {
                                rifle = GenSpawn.Spawn(ThingDef.Named("Gun_ChargeRifle"), this.pawn.Position, this.Map);
                            }
                            else
                            {
                                rifle = GenSpawn.Spawn(ThingDef.Named("Gun_SniperRifle"), this.pawn.Position, this.Map);
                            }
                            CompQuality qualityComp = powerArmor.TryGetComp <CompQuality>();
                            if (qualityComp != null)
                            {
                                qualityComp.SetQuality(QualityCategory.Masterwork, ArtGenerationContext.Outsider);
                            }
                            qualityComp = powerArmorHelmet.TryGetComp <CompQuality>();
                            if (qualityComp != null)
                            {
                                qualityComp.SetQuality(QualityCategory.Masterwork, ArtGenerationContext.Outsider);
                            }
                            qualityComp = rifle.TryGetComp <CompQuality>();
                            if (qualityComp != null)
                            {
                                qualityComp.SetQuality(QualityCategory.Masterwork, ArtGenerationContext.Outsider);
                            }

                            Faction faction    = Find.FactionManager.FirstFactionOfDef(FactionDefOf.AncientsHostile);
                            Pawn    deadMarine = PawnGenerator.GeneratePawn(PawnKindDefOf.AncientSoldier, faction);
                            HealthUtility.DamageUntilDead(deadMarine);
                            Corpse       corpse  = deadMarine.ParentHolder as Corpse;
                            CompRottable rotComp = corpse.TryGetComp <CompRottable>();
                            if (rotComp != null)
                            {
                                rotComp.RotProgress = 20f * GenDate.TicksPerDay; // 20 days so the corpse is dessicated.
                            }
                            GenSpawn.Spawn(corpse, fishingPier.bankCell, this.Map);
                            string eventText = "FishIndustry.LetterDescDeadMarine".Translate(this.pawn.Name.ToStringShort.CapitalizeFirst());
                            Find.LetterStack.ReceiveLetter("FishIndustry.LetterLabelDeadMarine".Translate(), eventText, LetterDefOf.PositiveEvent, this.pawn);
                        }
                        else
                        {
                            // Find a small amount of gold.
                            fishingCatch            = GenSpawn.Spawn(ThingDefOf.Gold, this.pawn.Position, this.Map);
                            fishingCatch.stackCount = Rand.RangeInclusive(1, 7);
                        }
                    }
                    else if (catchSelectorValue < 0.04)
                    {
                        // Find oysters.
                        fishingCatch            = GenSpawn.Spawn(Util_FishIndustry.OysterDef, this.pawn.Position, this.Map);
                        fishingCatch.stackCount = Rand.RangeInclusive(2, 9);
                    }
                    else
                    {
                        // Catch a fish.
                        IntVec3 fishSpot        = fishingPier.aquaticCells.RandomElement();
                        bool    fishSpotIsOcean = (this.Map.terrainGrid.TerrainAt(fishSpot) == TerrainDefOf.WaterOceanShallow) ||
                                                  (this.Map.terrainGrid.TerrainAt(fishSpot) == TerrainDefOf.WaterOceanDeep);
                        bool fishSpotIsMarshy = (this.Map.terrainGrid.TerrainAt(fishSpot) == TerrainDef.Named("Marsh"));

                        PawnKindDef caugthFishDef = null;
                        if (fishSpotIsOcean)
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList(this.Map.Biome)
                                             where fishSpecies.livesInOcean
                                             select fishSpecies).RandomElementByWeight((PawnKindDef_FishSpecies def) => def.commonality);
                        }
                        else if (fishSpotIsMarshy)
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList(this.Map.Biome)
                                             where fishSpecies.livesInMarsh
                                             select fishSpecies).RandomElementByWeight((PawnKindDef_FishSpecies def) => def.commonality);
                        }
                        else
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList(this.Map.Biome)
                                             where fishSpecies.livesInRiver
                                             select fishSpecies).RandomElementByWeight((PawnKindDef_FishSpecies def) => def.commonality);
                        }
                        Pawn caughtFish = PawnGenerator.GeneratePawn(caugthFishDef);
                        ExecutionUtility.DoExecutionByCut(this.pawn, caughtFish);
                        Corpse corpse = caughtFish.ParentHolder as Corpse;
                        GenSpawn.Spawn(corpse, this.pawn.Position, this.Map);
                        fishingCatch = corpse;
                        fishingCatch.SetForbidden(false);
                        if (caughtFish.BodySize >= 0.1f)
                        {
                            fishingPier.fishStock--;
                        }
                    }
                    IntVec3 storageCell;
                    if (StoreUtility.TryFindBestBetterStoreCellFor(fishingCatch, this.pawn, this.Map, StoragePriority.Unstored, this.pawn.Faction, out storageCell, true))
                    {
                        this.pawn.Reserve(fishingCatch, this.job);
                        this.pawn.Reserve(storageCell, this.job);
                        this.pawn.CurJob.SetTarget(TargetIndex.B, storageCell);
                        this.pawn.CurJob.SetTarget(TargetIndex.A, fishingCatch);
                        this.pawn.CurJob.count    = 9999;
                        this.pawn.CurJob.haulMode = HaulMode.ToCellStorage;
                    }
                    else
                    {
                        this.pawn.jobs.EndCurrentJob(JobCondition.Succeeded);
                    }
                }
            };

            yield return(catchFishToil);

            yield return(Toils_Haul.StartCarryThing(TargetIndex.A));

            Toil carryToCell = Toils_Haul.CarryHauledThingToCell(TargetIndex.B);

            yield return(carryToCell);

            yield return(Toils_Haul.PlaceHauledThingInCell(TargetIndex.B, carryToCell, true));
        }
        protected override IEnumerable <Toil> MakeNewToils()
        {
            QualityCategory      fishingEquipmentQuality = QualityCategory.Normal;
            float                catchSomethingThreshold = 0f;
            Building_FishingPier fishingPier             = this.TargetThingA as Building_FishingPier;
            Passion              passion = Passion.None;
            int         fishingDuration  = 1000;
            const float skillGainPerTick = 0.15f;
            float       skillGainFactor  = 0f;

            this.AddEndCondition(() =>
            {
                var targ = this.pawn.jobs.curJob.GetTarget(fishingPierIndex).Thing;
                if (targ is Building && !targ.Spawned)
                {
                    return(JobCondition.Incompletable);
                }
                return(JobCondition.Ongoing);
            });

            this.FailOnBurningImmobile(fishingPierIndex); // Bill giver or product burning in carry phase.

            yield return(Toils_Reserve.Reserve(fishingPierIndex));

            float statValue = this.pawn.GetStatValue(Util_FishIndustry.FishingSpeedDef, true);

            fishingDuration = (int)Math.Round((double)(800f / statValue));

            yield return(Toils_Goto.GotoThing(fishingPierIndex, fishingPier.riverCell).FailOnDespawnedOrNull(fishingPierIndex));

            Toil verifyFisherHasFishingEquipmentToil = new Toil()
            {
                initAction = () =>
                {
                    if ((this.pawn.equipment.Primary != null) &&
                        (this.pawn.equipment.Primary.def == Util_FishIndustry.HarpoonDef))
                    {
                        this.fishingEquipment = FishingEquipment.Harpoon;
                        this.pawn.equipment.Primary.TryGetQuality(out fishingEquipmentQuality);
                    }
                    foreach (Apparel apparel in this.pawn.apparel.WornApparel)
                    {
                        if (apparel.def == Util_FishIndustry.FishingRodDef)
                        {
                            this.fishingEquipment = FishingEquipment.FishingRod;
                            apparel.TryGetQuality(out fishingEquipmentQuality);
                            break;
                        }
                    }
                    if (this.fishingEquipment == FishingEquipment.NoEquipment)
                    {
                        this.pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
                    }
                }
            };

            yield return(verifyFisherHasFishingEquipmentToil);

            Toil fishToil = new Toil()
            {
                initAction = () =>
                {
                    ThingDef moteDef = null;
                    if (fishingEquipment == FishingEquipment.FishingRod)
                    {
                        if (fishingPier.Rotation == Rot4.North)
                        {
                            moteDef = Util_FishIndustry.MoteFishingRodNorthDef;
                        }
                        else if (fishingPier.Rotation == Rot4.East)
                        {
                            moteDef = Util_FishIndustry.MoteFishingRodEastDef;
                        }
                        else if (fishingPier.Rotation == Rot4.South)
                        {
                            moteDef = Util_FishIndustry.MoteFishingRodSouthDef;
                        }
                        else
                        {
                            moteDef = Util_FishIndustry.MoteFishingRodWestDef;
                        }
                    }
                    if (moteDef != null)
                    {
                        this.fishingRodMote = (Mote)ThingMaker.MakeThing(moteDef, null);
                        this.fishingRodMote.exactPosition = fishingPier.fishingSpotCell.ToVector3Shifted();
                        this.fishingRodMote.Scale         = 1f;
                        GenSpawn.Spawn(this.fishingRodMote, fishingPier.fishingSpotCell);
                    }
                    WorkTypeDef fishingWorkDef = DefDatabase <WorkTypeDef> .GetNamed("Fishing");

                    passion = this.pawn.skills.MaxPassionOfRelevantSkillsFor(fishingWorkDef);
                    if (passion == Passion.None)
                    {
                        skillGainFactor = 0.3f;
                    }
                    else if (passion == Passion.Minor)
                    {
                        skillGainFactor = 1f;
                    }
                    else
                    {
                        skillGainFactor = 1.5f;
                    }
                },
                tickAction = () =>
                {
                    if (fishingEquipment == FishingEquipment.Harpoon)
                    {
                        if (Find.TickManager.TicksGame % GenTicks.TickRareInterval == 0)
                        {
                            Bullet     thrownHarpoon = GenSpawn.Spawn(Util_FishIndustry.HarpoonDef.Verbs.First().projectileDef, this.pawn.Position) as Bullet;
                            TargetInfo targetCell    = new TargetInfo(fishingPier.fishingSpotCell + new IntVec3(Rand.RangeInclusive(-1, 1), 0, Rand.RangeInclusive(0, 2)).RotatedBy(fishingPier.Rotation));
                            thrownHarpoon.Launch(this.pawn, targetCell);
                        }
                    }
                    this.pawn.Drawer.rotator.FaceCell(fishingPier.fishingSpotCell);

                    if (passion == Passion.Minor)
                    {
                        this.pawn.needs.joy.GainJoy(NeedTunings.JoyPerXpForPassionMinor, JoyKindDefOf.Work);
                    }
                    else if (passion == Passion.Major)
                    {
                        this.pawn.needs.joy.GainJoy(NeedTunings.JoyPerXpForPassionMajor, JoyKindDefOf.Work);
                    }
                    SkillDef fishingSkillDef = DefDatabase <SkillDef> .GetNamed("Fishing");

                    this.pawn.skills.Learn(fishingSkillDef, skillGainPerTick * skillGainFactor);

                    if (this.ticksLeftThisToil == 1)
                    {
                        if (this.fishingRodMote != null)
                        {
                            Log.Message("destroy mote");
                            this.fishingRodMote.Destroy();
                        }
                    }
                },
                defaultDuration     = fishingDuration,
                defaultCompleteMode = ToilCompleteMode.Delay
            };

            yield return(fishToil.WithProgressBarToilDelay(fishingPierIndex));

            yield return(verifyFisherHasFishingEquipmentToil); // Could be dropped during fishToil.

            Toil computeChanceToCatchToil = new Toil()
            {
                initAction = () =>
                {
                    float       fishingSkillLevel = 0f;
                    WorkTypeDef fishingWorkDef    = DefDatabase <WorkTypeDef> .GetNamed("Fishing");

                    fishingSkillLevel = this.pawn.skills.AverageOfRelevantSkillsFor(fishingWorkDef);
                    float fishingEquipmentQualityFactor = (float)fishingEquipmentQuality / (float)QualityCategory.Legendary;
                    float fishingSkillFactor            = fishingSkillLevel / 20f;
                    float snowFactor             = 1 - Find.SnowGrid.GetDepth(fishingPier.fishingSpotCell);
                    float fishingEquipmentOffset = 0f;
                    switch (this.fishingEquipment)
                    {
                    case FishingEquipment.Harpoon:
                        fishingEquipmentOffset = 0.2f;
                        break;

                    case FishingEquipment.FishingRod:
                        fishingEquipmentOffset = 0.5f;
                        break;
                    }
                    catchSomethingThreshold = ((fishingEquipmentOffset * fishingEquipmentQualityFactor) + 0.4f * fishingSkillFactor) * (0.25f + 0.75f * snowFactor);
                    // Reframe min and max chance (min 5%, max 75 % chance of success).
                    catchSomethingThreshold = catchSomethingThreshold * 0.75f;
                    catchSomethingThreshold = Math.Max(catchSomethingThreshold, 0.05f);
                },
                defaultCompleteMode = ToilCompleteMode.Instant
            };

            yield return(computeChanceToCatchToil);

            Toil catchFishToil = new Toil()
            {
                initAction = () =>
                {
                    Job   curJob       = this.pawn.jobs.curJob;
                    Thing fishingCatch = null;

                    bool catchIsSuccessful = Rand.Value <= catchSomethingThreshold;
                    if (catchIsSuccessful == false)
                    {
                        MoteMaker.ThrowMetaIcon(this.pawn.Position, ThingDefOf.Mote_IncapIcon);
                        this.pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
                        return;
                    }

                    float catchSelectorValue = Rand.Value;
                    if (catchSelectorValue > 0.04f)
                    {
                        // Catch a fish.
                        bool fishSpotIsMarshy = (Find.TerrainGrid.TerrainAt(fishingPier.fishingSpotCell) == TerrainDef.Named("Marsh"));
                        bool isDaytime        = (SkyManager.CurSkyGlow >= 0.4f);

                        ThingDef caugthFishDef = Util_FishIndustry.TailteethDef;
                        if (fishSpotIsMarshy && isDaytime)
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList()
                                             where fishSpecies.livesInMarsh
                                             where fishSpecies.catchableDuringDay
                                             select fishSpecies).RandomElementByWeight((ThingDef_FishSpecies def) => def.commonality);
                        }
                        else if (fishSpotIsMarshy && !isDaytime)
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList()
                                             where fishSpecies.livesInMarsh
                                             where fishSpecies.catchableDuringNight
                                             select fishSpecies).RandomElementByWeight((ThingDef_FishSpecies def) => def.commonality);
                        }
                        else if (!fishSpotIsMarshy && isDaytime)
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList()
                                             where fishSpecies.livesInSea
                                             where fishSpecies.catchableDuringDay
                                             select fishSpecies).RandomElementByWeight((ThingDef_FishSpecies def) => def.commonality);
                        }
                        else
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList()
                                             where fishSpecies.livesInSea
                                             where fishSpecies.catchableDuringNight
                                             select fishSpecies).RandomElementByWeight((ThingDef_FishSpecies def) => def.commonality);
                        }

                        /*TerrainDef fishSpotType = Find.TerrainGrid.TerrainAt(fishingPier.fishingSpotCell);
                         * List<ThingDef> fishSpeciesList = null;
                         * ThingDef_FishSpecies.AquaticEnvironment aquaticEnvironment;
                         * ThingDef_FishSpecies.LivingTime livingTime;
                         * float fishSpeciesTotalCommonality = 0f;
                         * float fishSpeciesCommonalitySum = 0f;
                         *
                         * // Aquatic environment.
                         * if (fishSpotType == TerrainDef.Named("Marsh"))
                         * {
                         *  aquaticEnvironment = ThingDef_FishSpecies.AquaticEnvironment.Marsh;
                         * }
                         * else
                         * {
                         *  aquaticEnvironment = ThingDef_FishSpecies.AquaticEnvironment.Sea;
                         * }
                         * // Day time.
                         * if (SkyManager.CurSkyGlow >= 0.4f)
                         * {
                         *  livingTime = ThingDef_FishSpecies.LivingTime.Day;
                         * }
                         * else
                         * {
                         *  livingTime = ThingDef_FishSpecies.LivingTime.Night;
                         * }
                         *
                         * fishSpeciesList = Util_FishIndustry.GetFishSpeciesList(aquaticEnvironment, livingTime);
                         * fishSpeciesTotalCommonality = Util_FishIndustry.GetFishSpeciesTotalCommonality(aquaticEnvironment, livingTime);
                         *
                         * float randomSelector = Rand.Range(0f, fishSpeciesTotalCommonality);
                         * ThingDef selectedFishSpecies = null;
                         * for (int fishSpeciesIndex = 0; fishSpeciesIndex < fishSpeciesList.Count; fishSpeciesIndex++)
                         * {
                         *  ThingDef_FishSpecies currentFishSpecies = fishSpeciesList[fishSpeciesIndex] as ThingDef_FishSpecies;
                         *  fishSpeciesCommonalitySum += currentFishSpecies.commonality;
                         *
                         *  if (randomSelector <= fishSpeciesCommonalitySum)
                         *  {
                         *      selectedFishSpecies = currentFishSpecies;
                         *      break;
                         *  }
                         * }*/
                        fishingCatch            = GenSpawn.Spawn(caugthFishDef, this.pawn.Position);
                        fishingCatch.stackCount = (caugthFishDef as ThingDef_FishSpecies).catchQuantity;
                        fishingPier.fishStock--;
                    }
                    else if (catchSelectorValue > 0.02)
                    {
                        fishingCatch            = GenSpawn.Spawn(Util_FishIndustry.OysterDef, this.pawn.Position);
                        fishingCatch.stackCount = Rand.RangeInclusive(5, 27);
                    }
                    else
                    {
                        float bonusCatchValue = Rand.Value;
                        if (bonusCatchValue < 0.01f)
                        {
                            // Really small chance to find a sunken treasure!!!
                            fishingCatch            = GenSpawn.Spawn(ThingDefOf.Gold, this.pawn.Position);
                            fishingCatch.stackCount = Rand.RangeInclusive(58, 289);
                            Thing treasureSilver = GenSpawn.Spawn(ThingDefOf.Silver, fishingPier.middleCell);
                            treasureSilver.stackCount = Rand.RangeInclusive(237, 2154);
                            string eventText = this.pawn.Name.ToStringShort.CapitalizeFirst() + " has found a sunken treasure while fishing! What a good catch!\n";
                            Find.LetterStack.ReceiveLetter("Sunken treasure!", eventText, LetterType.Good, this.pawn.Position);
                        }
                        else if (bonusCatchValue < 0.02f)
                        {
                            // Really small chance to find a complete power armor set + sniper or charge rifle.
                            Thing powerArmor = GenSpawn.Spawn(ThingDef.Named("Apparel_PowerArmor"), this.pawn.Position);
                            fishingCatch = powerArmor; // Used to carry the power armor.
                            Thing powerArmorHelmet = GenSpawn.Spawn(ThingDef.Named("Apparel_PowerArmorHelmet"), this.pawn.Position);
                            Thing rifle            = null;
                            if (Rand.Value < 0.5f)
                            {
                                rifle = GenSpawn.Spawn(ThingDef.Named("Gun_ChargeRifle"), this.pawn.Position);
                            }
                            else
                            {
                                rifle = GenSpawn.Spawn(ThingDef.Named("Gun_SniperRifle"), this.pawn.Position);
                            }
                            CompQuality qualityComp = powerArmor.TryGetComp <CompQuality>();
                            if (qualityComp != null)
                            {
                                qualityComp.SetQuality(QualityCategory.Masterwork, ArtGenerationContext.Outsider);
                            }
                            qualityComp = powerArmorHelmet.TryGetComp <CompQuality>();
                            if (qualityComp != null)
                            {
                                qualityComp.SetQuality(QualityCategory.Masterwork, ArtGenerationContext.Outsider);
                            }
                            qualityComp = rifle.TryGetComp <CompQuality>();
                            if (qualityComp != null)
                            {
                                qualityComp.SetQuality(QualityCategory.Masterwork, ArtGenerationContext.Outsider);
                            }

                            Faction faction    = Find.FactionManager.FirstFactionOfDef(FactionDefOf.SpacerHostile);
                            Pawn    deadMarine = PawnGenerator.GeneratePawn(PawnKindDefOf.SpaceSoldier, faction);
                            GenSpawn.Spawn(deadMarine, fishingPier.bankCell);
                            HealthUtility.GiveInjuriesToKill(deadMarine);
                            List <Thing> thingsList = deadMarine.Position.GetThingList();
                            foreach (Thing thing in thingsList)
                            {
                                if (thing.def.defName.Contains("Corpse"))
                                {
                                    CompRottable rotComp = thing.TryGetComp <CompRottable>();
                                    if (rotComp != null)
                                    {
                                        rotComp.RotProgress = 20f * GenDate.TicksPerDay; // 20 days so the corpse is dessicated.
                                    }
                                }
                            }
                            string eventText = this.pawn.Name.ToStringShort.CapitalizeFirst() + " has cought a dead body while fishing!\n\n'This is really disgusting but look at his gear! This guy was probably a Mining & Co. security member. I wonder what happend to him...'\n";
                            Find.LetterStack.ReceiveLetter("Dead marine", eventText, LetterType.Good, this.pawn.Position);
                        }
                        else
                        {
                            // Find a small amount of gold.
                            fishingCatch            = GenSpawn.Spawn(ThingDefOf.Gold, this.pawn.Position);
                            fishingCatch.stackCount = Rand.RangeInclusive(1, 7);
                        }
                        // TODO: add chance to get hurt by a tailteeth (missing finger or even hand!).
                    }
                    IntVec3 storageCell;
                    if (StoreUtility.TryFindBestBetterStoreCellFor(fishingCatch, this.pawn, StoragePriority.Unstored, this.pawn.Faction, out storageCell, true))
                    {
                        this.pawn.carrier.TryStartCarry(fishingCatch);
                        curJob.targetB       = storageCell;
                        curJob.targetC       = fishingCatch;
                        curJob.maxNumToCarry = 99999;
                    }
                    else
                    {
                        this.pawn.jobs.EndCurrentJob(JobCondition.Succeeded);
                    }
                }
            };

            yield return(catchFishToil);

            // Reserve the product and storage cell.
            yield return(Toils_Reserve.Reserve(TargetIndex.B));

            yield return(Toils_Reserve.Reserve(TargetIndex.C));

            Toil carryToCell = Toils_Haul.CarryHauledThingToCell(TargetIndex.B);

            yield return(carryToCell);

            yield return(Toils_Haul.PlaceHauledThingInCell(TargetIndex.B, carryToCell, true));

            yield return(Toils_Reserve.Release(fishingPierIndex));
        }
Exemple #10
0
        /// <summary>
        /// Check if a new fishing pier can be built at this location.
        /// - the fishing pier bank cell must be on a bank.
        /// - the rest of the fishing pier and the fishing spot must be on water.
        /// - must not be too near another fishing pier.
        /// </summary>
        public override AcceptanceReport AllowsPlacing(BuildableDef checkingDef, IntVec3 loc, Rot4 rot, Map map, Thing thingToIgnore = null, Thing thing = null)
        {
            // Remove old fish stock respawn rate text mote.
            if (lastMotePosition.IsValid &&
                (loc != lastMotePosition))
            {
                RemoveLastRespawnRateMote(map);
            }

            // Check this biome contains some fishes.
            if (Util_FishIndustry.GetFishSpeciesList(map.Biome).NullOrEmpty())
            {
                return(new AcceptanceReport("FishIndustry.FishingPier_InvalidBiome".Translate()));
            }

            // Check if another fishing pier is not too close.
            if (Util_PlaceWorker.IsNearFishingPier(map, loc, Util_PlaceWorker.minDistanceBetweenTwoFishingSpots))
            {
                return(new AcceptanceReport("FishIndustry.TooCloseFishingPier".Translate()));
            }

            // Check if a fishing zone is not too close.
            if (Util_PlaceWorker.IsNearFishingZone(map, loc, Util_PlaceWorker.minDistanceBetweenTwoFishingSpots))
            {
                return(new AcceptanceReport("FishIndustry.TooCloseFishingZone".Translate()));
            }

            // Check fishing pier is on water.
            if ((Util_Zone_Fishing.IsAquaticTerrain(map, loc + new IntVec3(0, 0, -1).RotatedBy(rot)) == false) ||
                (Util_Zone_Fishing.IsAquaticTerrain(map, loc + new IntVec3(0, 0, 0).RotatedBy(rot)) == false) ||
                (Util_Zone_Fishing.IsAquaticTerrain(map, loc + new IntVec3(0, 0, 1).RotatedBy(rot)) == false))
            {
                return(new AcceptanceReport("FishIndustry.FishingPier_PierMustBeOnWater".Translate()));
            }

            // Check fishing zone is on water.
            for (int xOffset = -1; xOffset <= 1; xOffset++)
            {
                for (int yOffset = 2; yOffset <= 4; yOffset++)
                {
                    if (Util_Zone_Fishing.IsAquaticTerrain(map, loc + new IntVec3(xOffset, 0, yOffset).RotatedBy(rot)) == false)
                    {
                        return(new AcceptanceReport("FishIndustry.FishingPier_ZoneMustBeOnWater".Translate()));
                    }
                }
            }

            // Display fish stock respawn rate.
            if ((lastMotePosition.IsValid == false) ||
                (Find.TickManager.Paused &&
                 (DateTime.Now.Second != lastMoteUpdateSecond)) ||
                ((Find.TickManager.Paused == false) &&
                 (Find.TickManager.TicksGame >= lastMoteUpdateTick + GenTicks.TicksPerRealSecond)))
            {
                lastMoteUpdateSecond = DateTime.Now.Second;
                lastMoteUpdateTick   = Find.TickManager.TicksGame;
                RemoveLastRespawnRateMote(map);
                DisplayMaxFishStockMoteAt(map, loc, rot);
            }

            return(true);
        }
        protected override IEnumerable <Toil> MakeNewToils()
        {
            const float baseFishingDuration    = 2400f;
            const float skillGainPerTick       = 0.15f;
            const float catchSuccessRateInZone = 0.70f;

            int fishingDuration = (int)baseFishingDuration;

            // Compute fishing duration.
            float fishingSkillLevel = 0f;

            fishingSkillLevel = this.pawn.skills.AverageOfRelevantSkillsFor(WorkTypeDefOf.Hunting);
            float fishingSkillDurationFactor = fishingSkillLevel / 20f;

            fishingDuration = (int)(baseFishingDuration * (1.5f - fishingSkillDurationFactor));

            Toil faceWaterToil = new Toil()
            {
                initAction = () =>
                {
                    // Compute pawn rotation during fishing.
                    foreach (IntVec3 offset in GenAdj.CardinalDirections.InRandomOrder())
                    {
                        IntVec3      adjacentCell = this.TargetLocA + offset;
                        Zone_Fishing fishingZone  = adjacentCell.GetZone(this.Map) as Zone_Fishing;
                        if ((fishingZone != null) &&
                            fishingZone.fishingSpots.Contains(this.TargetLocA))
                        {
                            this.cardinalDir = offset;
                            this.pawn.CurJob.SetTarget(TargetIndex.B, adjacentCell);
                            this.rotateToFace = TargetIndex.B;
                            break;
                        }
                    }
                },
                defaultCompleteMode = ToilCompleteMode.Instant
            };

            yield return(faceWaterToil);

            yield return(Toils_Goto.GotoCell(this.TargetLocA, this.pathEndMode).FailOn(FishingForbiddenOrInvalidSpot));

            Toil fishToil = new Toil()
            {
                tickAction = () =>
                {
                    this.pawn.skills.Learn(SkillDefOf.Shooting, skillGainPerTick);

                    // Spawn mote or maintain it.
                    if (this.fishingRodMote.DestroyedOrNull())
                    {
                        IntVec3  motePosition = this.TargetB.Cell;
                        ThingDef moteDef      = null;
                        if (cardinalDir == IntVec3.North)
                        {
                            moteDef = Util_FishIndustry.MoteFishingRodNorthDef;
                        }
                        else if (cardinalDir == IntVec3.East)
                        {
                            moteDef = Util_FishIndustry.MoteFishingRodEastDef;
                        }
                        else if (cardinalDir == IntVec3.South)
                        {
                            moteDef = Util_FishIndustry.MoteFishingRodSouthDef;
                        }
                        else
                        {
                            moteDef = Util_FishIndustry.MoteFishingRodWestDef;
                        }
                        this.fishingRodMote = (Mote)ThingMaker.MakeThing(moteDef, null);
                        this.fishingRodMote.exactPosition = motePosition.ToVector3Shifted();
                        this.fishingRodMote.Scale         = 1f;
                        GenSpawn.Spawn(this.fishingRodMote, motePosition, this.Map);
                    }
                    else
                    {
                        this.fishingRodMote.Maintain();
                    }
                },
                defaultDuration     = fishingDuration,
                defaultCompleteMode = ToilCompleteMode.Delay
            };

            yield return(fishToil.WithProgressBarToilDelay(this.fishPositionIndex, true).FailOn(FishingForbiddenOrInvalidSpot));

            Toil catchFishToil = new Toil()
            {
                initAction = () =>
                {
                    Thing fishingCatch = null;

                    bool catchIsSuccessful = (Rand.Value <= catchSuccessRateInZone);
                    if (catchIsSuccessful == false)
                    {
                        MoteMaker.ThrowMetaIcon(this.pawn.Position, this.Map, ThingDefOf.Mote_IncapIcon);
                        this.pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
                        return;
                    }

                    float catchSelectorValue = Rand.Value;

                    if ((catchSelectorValue < 0.02) &&
                        Util_FishIndustry.GetFishSpeciesList(this.Map.Biome).Contains(Util_FishIndustry.TailteethPawnKindDef as PawnKindDef_FishSpecies))
                    {
                        // Get hurt by a tailteeth.
                        this.pawn.TakeDamage(new DamageInfo(DamageDefOf.Bite, Rand.Range(5, 12)));
                        Messages.Message(this.pawn.Name.ToStringShort + "FishIndustry.FisherBitten".Translate(), this.pawn, MessageTypeDefOf.NegativeHealthEvent);
                        this.pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
                        return;
                    }
                    else if (catchSelectorValue < 0.04)
                    {
                        // Find oysters.
                        fishingCatch            = GenSpawn.Spawn(Util_FishIndustry.OysterDef, this.pawn.Position, this.Map);
                        fishingCatch.stackCount = Rand.RangeInclusive(5, 27);
                    }
                    else
                    {
                        // Catch a fish.
                        bool fishSpotIsOcean = (this.Map.terrainGrid.TerrainAt(this.TargetLocA) == TerrainDefOf.WaterOceanShallow) ||
                                               (this.Map.terrainGrid.TerrainAt(this.TargetLocA) == TerrainDefOf.WaterOceanDeep);
                        bool fishSpotIsMarshy = (this.Map.terrainGrid.TerrainAt(this.TargetLocA) == TerrainDef.Named("Marsh"));

                        PawnKindDef caugthFishDef = null;
                        if (fishSpotIsOcean)
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList(this.Map.Biome)
                                             where fishSpecies.livesInOcean
                                             select fishSpecies).RandomElementByWeight((PawnKindDef_FishSpecies def) => def.commonality);
                        }
                        else if (fishSpotIsMarshy)
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList(this.Map.Biome)
                                             where fishSpecies.livesInMarsh
                                             select fishSpecies).RandomElementByWeight((PawnKindDef_FishSpecies def) => def.commonality);
                        }
                        else
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList(this.Map.Biome)
                                             where fishSpecies.livesInRiver
                                             select fishSpecies).RandomElementByWeight((PawnKindDef_FishSpecies def) => def.commonality);
                        }
                        Pawn caughtFish = PawnGenerator.GeneratePawn(caugthFishDef);
                        ExecutionUtility.DoExecutionByCut(this.pawn, caughtFish);
                        Corpse corpse = caughtFish.ParentHolder as Corpse;
                        GenSpawn.Spawn(corpse, this.pawn.Position, this.Map);
                        fishingCatch = corpse;
                        fishingCatch.SetForbidden(false);
                        if (caughtFish.BodySize >= 0.1f)
                        {
                            Zone_Fishing fishingZone = this.TargetB.Cell.GetZone(this.Map) as Zone_Fishing;
                            if ((fishingZone != null) &&
                                fishingZone.fishingSpots.Contains(this.TargetLocA))
                            {
                                fishingZone.fishingSpots.Remove(this.TargetLocA);
                            }
                        }
                    }

                    IntVec3 storageCell;
                    if (StoreUtility.TryFindBestBetterStoreCellFor(fishingCatch, this.pawn, this.Map, StoragePriority.Unstored, this.pawn.Faction, out storageCell, true))
                    {
                        this.pawn.Reserve(fishingCatch, this.job);
                        this.pawn.Reserve(storageCell, this.job);
                        this.pawn.CurJob.SetTarget(TargetIndex.B, storageCell);
                        this.pawn.CurJob.SetTarget(TargetIndex.A, fishingCatch);
                        this.pawn.CurJob.count    = 9999;
                        this.pawn.CurJob.haulMode = HaulMode.ToCellStorage;
                    }
                    else
                    {
                        this.pawn.jobs.EndCurrentJob(JobCondition.Succeeded);
                    }
                }
            };

            yield return(catchFishToil);

            yield return(Toils_Haul.StartCarryThing(TargetIndex.A));

            Toil carryToCell = Toils_Haul.CarryHauledThingToCell(TargetIndex.B);

            yield return(carryToCell);

            yield return(Toils_Haul.PlaceHauledThingInCell(TargetIndex.B, carryToCell, true));
        }
        protected override IEnumerable <Toil> MakeNewToils()
        {
            const float baseFishingDuration = 2000f;

            int   fishingDuration            = (int)baseFishingDuration;
            float catchSomethingThreshold    = 0f;
            Building_FishingPier fishingPier = this.TargetThingA as Building_FishingPier;
            Passion     passion          = Passion.None;
            const float skillGainPerTick = 0.15f;
            float       skillGainFactor  = 0f;

            this.AddEndCondition(() =>
            {
                var targ = this.pawn.jobs.curJob.GetTarget(fishingPierIndex).Thing;
                if (targ is Building && !targ.Spawned)
                {
                    return(JobCondition.Incompletable);
                }
                return(JobCondition.Ongoing);
            });

            this.FailOnBurningImmobile(fishingPierIndex); // Bill giver or product burning in carry phase.

            this.rotateToFace = TargetIndex.B;

            yield return(Toils_Reserve.Reserve(fishingPierIndex));

            float fishingSkillLevel = 0f;

            fishingSkillLevel = this.pawn.skills.AverageOfRelevantSkillsFor(WorkTypeDefOf.Hunting);
            float fishingSkillDurationFactor = fishingSkillLevel / 20f;

            fishingDuration = (int)(baseFishingDuration * (1.5f - fishingSkillDurationFactor));

            yield return(Toils_Goto.GotoThing(fishingPierIndex, fishingPier.riverCell).FailOnDespawnedOrNull(fishingPierIndex));

            Toil fishToil = new Toil()
            {
                initAction = () =>
                {
                    ThingDef moteDef = null;
                    if (fishingPier.Rotation == Rot4.North)
                    {
                        moteDef = Util_FishIndustry.MoteFishingRodNorthDef;
                    }
                    else if (fishingPier.Rotation == Rot4.East)
                    {
                        moteDef = Util_FishIndustry.MoteFishingRodEastDef;
                    }
                    else if (fishingPier.Rotation == Rot4.South)
                    {
                        moteDef = Util_FishIndustry.MoteFishingRodSouthDef;
                    }
                    else
                    {
                        moteDef = Util_FishIndustry.MoteFishingRodWestDef;
                    }
                    this.fishingRodMote = (Mote)ThingMaker.MakeThing(moteDef, null);
                    this.fishingRodMote.exactPosition = fishingPier.fishingSpotCell.ToVector3Shifted();
                    this.fishingRodMote.Scale         = 1f;
                    GenSpawn.Spawn(this.fishingRodMote, fishingPier.fishingSpotCell, this.Map);
                    WorkTypeDef fishingWorkDef = WorkTypeDefOf.Hunting;
                    passion = this.pawn.skills.MaxPassionOfRelevantSkillsFor(fishingWorkDef);
                    if (passion == Passion.None)
                    {
                        skillGainFactor = 0.3f;
                    }
                    else if (passion == Passion.Minor)
                    {
                        skillGainFactor = 1f;
                    }
                    else
                    {
                        skillGainFactor = 1.5f;
                    }
                },
                tickAction = () =>
                {
                    if (passion == Passion.Minor)
                    {
                        this.pawn.needs.joy.GainJoy(NeedTunings.JoyPerXpForPassionMinor, JoyKindDefOf.Work);
                    }
                    else if (passion == Passion.Major)
                    {
                        this.pawn.needs.joy.GainJoy(NeedTunings.JoyPerXpForPassionMajor, JoyKindDefOf.Work);
                    }
                    this.pawn.skills.Learn(SkillDefOf.Shooting, skillGainPerTick * skillGainFactor);

                    if (this.ticksLeftThisToil == 1)
                    {
                        if (this.fishingRodMote != null)
                        {
                            this.fishingRodMote.Destroy();
                        }
                    }
                },
                defaultDuration     = fishingDuration,
                defaultCompleteMode = ToilCompleteMode.Delay
            };

            yield return(fishToil.WithProgressBarToilDelay(fishingPierIndex));

            Toil computeChanceToCatchToil = new Toil()
            {
                initAction = () =>
                {
                    catchSomethingThreshold = fishingSkillLevel / 20f;
                    // Reframe min and max chance (min 5%, max 75 % chance of success).
                    Mathf.Clamp(catchSomethingThreshold, 0.05f, 0.75f);
                },
                defaultCompleteMode = ToilCompleteMode.Instant
            };

            yield return(computeChanceToCatchToil);

            Toil catchFishToil = new Toil()
            {
                initAction = () =>
                {
                    Job   curJob       = this.pawn.jobs.curJob;
                    Thing fishingCatch = null;

                    // 90% chance to successfully catch something.
                    bool catchIsSuccessful = (Rand.Value >= 0.1f);
                    if (catchIsSuccessful == false)
                    {
                        MoteMaker.ThrowMetaIcon(this.pawn.Position, this.Map, ThingDefOf.Mote_IncapIcon);
                        this.pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
                        return;
                    }

                    float catchSelectorValue = Rand.Value;
                    if (catchSelectorValue > 0.04f)
                    {
                        // Catch a fish.
                        bool fishSpotIsMarshy = (this.Map.terrainGrid.TerrainAt(fishingPier.fishingSpotCell) == TerrainDef.Named("Marsh"));
                        bool isDaytime        = (this.Map.skyManager.CurSkyGlow >= 0.4f);

                        PawnKindDef caugthFishDef = null;
                        if (fishSpotIsMarshy && isDaytime)
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList()
                                             where fishSpecies.livesInMarsh
                                             where fishSpecies.catchableDuringDay
                                             select fishSpecies).RandomElementByWeight((PawnKindDef_FishSpecies def) => def.commonality);
                        }
                        else if (fishSpotIsMarshy && !isDaytime)
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList()
                                             where fishSpecies.livesInMarsh
                                             where fishSpecies.catchableDuringNight
                                             select fishSpecies).RandomElementByWeight((PawnKindDef_FishSpecies def) => def.commonality);
                        }
                        else if (!fishSpotIsMarshy && isDaytime)
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList()
                                             where fishSpecies.livesInSea
                                             where fishSpecies.catchableDuringDay
                                             select fishSpecies).RandomElementByWeight((PawnKindDef_FishSpecies def) => def.commonality);
                        }
                        else
                        {
                            caugthFishDef = (from fishSpecies in Util_FishIndustry.GetFishSpeciesList()
                                             where fishSpecies.livesInSea
                                             where fishSpecies.catchableDuringNight
                                             select fishSpecies).RandomElementByWeight((PawnKindDef_FishSpecies def) => def.commonality);
                        }
                        Pawn caughtFish = PawnGenerator.GeneratePawn(caugthFishDef);
                        GenSpawn.Spawn(caughtFish, this.pawn.Position, this.Map);
                        HealthUtility.GiveInjuriesToKill(caughtFish);
                        foreach (Thing thing in this.pawn.Position.GetThingList(this.Map))
                        {
                            Corpse fishCorpse = thing as Corpse;
                            if (fishCorpse != null)
                            {
                                fishingCatch = fishCorpse;
                            }
                        }
                        if (caughtFish.BodySize >= 0.1f)
                        {
                            fishingPier.fishStock--;
                            fishingPier.UpdateMaxFishStock();
                        }
                    }
                    else if (catchSelectorValue > 0.02)
                    {
                        fishingCatch            = GenSpawn.Spawn(Util_FishIndustry.OysterDef, this.pawn.Position, this.Map);
                        fishingCatch.stackCount = Rand.RangeInclusive(5, 27);
                    }
                    else
                    {
                        float bonusCatchValue = Rand.Value;
                        if (bonusCatchValue < 0.01f)
                        {
                            // Really small chance to find a sunken treasure!!!
                            fishingCatch            = GenSpawn.Spawn(ThingDefOf.Gold, this.pawn.Position, this.Map);
                            fishingCatch.stackCount = Rand.RangeInclusive(58, 289);
                            Thing treasureSilver = GenSpawn.Spawn(ThingDefOf.Silver, fishingPier.middleCell, this.Map);
                            treasureSilver.stackCount = Rand.RangeInclusive(237, 2154);
                            string eventText = this.pawn.Name.ToStringShort.CapitalizeFirst() + " has found a sunken treasure while fishing! What a good catch!\n";
                            Find.LetterStack.ReceiveLetter("Sunken treasure!", eventText, LetterType.Good, this.pawn);
                        }
                        else if (bonusCatchValue < 0.02f)
                        {
                            // Really small chance to find a complete power armor set + sniper or charge rifle.
                            Thing powerArmor = GenSpawn.Spawn(ThingDef.Named("Apparel_PowerArmor"), this.pawn.Position, this.Map);
                            fishingCatch = powerArmor; // Used to carry the power armor.
                            Thing powerArmorHelmet = GenSpawn.Spawn(ThingDef.Named("Apparel_PowerArmorHelmet"), this.pawn.Position, this.Map);
                            Thing rifle            = null;
                            if (Rand.Value < 0.5f)
                            {
                                rifle = GenSpawn.Spawn(ThingDef.Named("Gun_ChargeRifle"), this.pawn.Position, this.Map);
                            }
                            else
                            {
                                rifle = GenSpawn.Spawn(ThingDef.Named("Gun_SniperRifle"), this.pawn.Position, this.Map);
                            }
                            CompQuality qualityComp = powerArmor.TryGetComp <CompQuality>();
                            if (qualityComp != null)
                            {
                                qualityComp.SetQuality(QualityCategory.Masterwork, ArtGenerationContext.Outsider);
                            }
                            qualityComp = powerArmorHelmet.TryGetComp <CompQuality>();
                            if (qualityComp != null)
                            {
                                qualityComp.SetQuality(QualityCategory.Masterwork, ArtGenerationContext.Outsider);
                            }
                            qualityComp = rifle.TryGetComp <CompQuality>();
                            if (qualityComp != null)
                            {
                                qualityComp.SetQuality(QualityCategory.Masterwork, ArtGenerationContext.Outsider);
                            }

                            Faction faction    = Find.FactionManager.FirstFactionOfDef(FactionDefOf.SpacerHostile);
                            Pawn    deadMarine = PawnGenerator.GeneratePawn(PawnKindDefOf.SpaceSoldier, faction);
                            GenSpawn.Spawn(deadMarine, fishingPier.bankCell, this.Map);
                            HealthUtility.GiveInjuriesToKill(deadMarine);
                            List <Thing> thingsList = deadMarine.Position.GetThingList(this.Map);
                            foreach (Thing thing in thingsList)
                            {
                                if (thing.def.defName.Contains("Corpse"))
                                {
                                    CompRottable rotComp = thing.TryGetComp <CompRottable>();
                                    if (rotComp != null)
                                    {
                                        rotComp.RotProgress = 20f * GenDate.TicksPerDay; // 20 days so the corpse is dessicated.
                                    }
                                }
                            }
                            string eventText = this.pawn.Name.ToStringShort.CapitalizeFirst() + " has cought a dead body while fishing!\n\n'This is really disgusting but look at his gear! This guy was probably a MiningCo. security member. I wonder what happend to him...'\n";
                            Find.LetterStack.ReceiveLetter("Dead marine", eventText, LetterType.Good, this.pawn);
                        }
                        else
                        {
                            // Find a small amount of gold.
                            fishingCatch            = GenSpawn.Spawn(ThingDefOf.Gold, this.pawn.Position, this.Map);
                            fishingCatch.stackCount = Rand.RangeInclusive(1, 7);
                        }
                        // TODO: add chance to get hurt by a tailteeth (missing finger or even hand!).
                    }
                    IntVec3 storageCell;
                    if (StoreUtility.TryFindBestBetterStoreCellFor(fishingCatch, this.pawn, this.Map, StoragePriority.Unstored, this.pawn.Faction, out storageCell, true))
                    {
                        this.pawn.carryTracker.TryStartCarry(fishingCatch);
                        curJob.targetB = storageCell;
                        curJob.targetC = fishingCatch;
                        curJob.count   = 99999;
                    }
                    else
                    {
                        this.pawn.jobs.EndCurrentJob(JobCondition.Succeeded);
                    }
                }
            };

            yield return(catchFishToil);

            // Reserve the product and storage cell.
            yield return(Toils_Reserve.Reserve(TargetIndex.B));

            yield return(Toils_Reserve.Reserve(TargetIndex.C));

            Toil carryToCell = Toils_Haul.CarryHauledThingToCell(TargetIndex.B);

            yield return(carryToCell);

            yield return(Toils_Haul.PlaceHauledThingInCell(TargetIndex.B, carryToCell, true));

            yield return(Toils_Reserve.Release(fishingPierIndex));
        }
        /// <summary>
        /// Check if a new fishing pier can be built at this location.
        /// - the fishing pier bank cell must be on a bank.
        /// - the rest of the fishing pier and the fishing spot must be on water.
        /// - must not be too near from another fishing pier.
        /// </summary>
        public override AcceptanceReport AllowsPlacing(BuildableDef checkingDef, IntVec3 loc, Rot4 rot, Thing thingToIgnore = null)
        {
            // Check this biome contains some fishes.
            if (Util_FishIndustry.GetFishSpeciesList(this.Map.Biome).NullOrEmpty())
            {
                return(new AcceptanceReport("FishIndustry.FishingPier_InvalidBiome".Translate()));
            }

            // Check fishing pier bank cell is on a "solid" terrain.
            if (Util_FishIndustry.IsAquaticTerrain(this.Map, loc))
            {
                return(new AcceptanceReport("FishIndustry.FishingPier_MustTouchBank".Translate()));
            }
            // Check fishing pier middle and river cells are on water.
            if ((Util_FishIndustry.IsAquaticTerrain(this.Map, loc + new IntVec3(0, 0, 1).RotatedBy(rot)) == false) ||
                (Util_FishIndustry.IsAquaticTerrain(this.Map, loc + new IntVec3(0, 0, 2).RotatedBy(rot)) == false))
            {
                return(new AcceptanceReport("FishIndustry.FishingPier_PierMustOnWater".Translate()));
            }
            // Check fishing zone is on water.
            for (int xOffset = -1; xOffset <= 1; xOffset++)
            {
                for (int yOffset = 3; yOffset <= 5; yOffset++)
                {
                    if (Util_FishIndustry.IsAquaticTerrain(this.Map, loc + new IntVec3(xOffset, 0, yOffset).RotatedBy(rot)) == false)
                    {
                        return(new AcceptanceReport("FishIndustry.FishingPier_ZoneMustOnWater".Translate()));
                    }
                }
            }

            // Check if another fishing pier is not too close (mind the test on "fishing pier" def and "fishing pier spawner" blueprint and frame defs.
            List <Thing> fishingPierList = this.Map.listerThings.ThingsOfDef(Util_FishIndustry.FishingPierDef);
            List <Thing> fishingPierSpawnerBlueprintList      = this.Map.listerThings.ThingsOfDef(Util_FishIndustry.FishingPierSpawnerDef.blueprintDef);
            List <Thing> fishingPierSpawnerFrameList          = this.Map.listerThings.ThingsOfDef(Util_FishIndustry.FishingPierSpawnerDef.frameDef);
            List <Thing> fishingPierSpawnerOnMudBlueprintList = this.Map.listerThings.ThingsOfDef(Util_FishIndustry.FishingPierSpawnerOnMudDef.blueprintDef);
            List <Thing> fishingPierSpawnerOnMudFrameList     = this.Map.listerThings.ThingsOfDef(Util_FishIndustry.FishingPierSpawnerOnMudDef.frameDef);

            if (fishingPierList != null)
            {
                IEnumerable <Thing> fishingPierInTheArea = fishingPierList.Where(building => loc.InHorDistOf(building.Position, minDistanceBetweenTwoFishingPiers));
                if (fishingPierInTheArea.Count() > 0)
                {
                    return(new AcceptanceReport("FishIndustry.FishingPier_ToClose".Translate()));
                }
            }
            if (fishingPierSpawnerBlueprintList != null)
            {
                IEnumerable <Thing> fishingPierBlueprintInTheArea = fishingPierSpawnerBlueprintList.Where(building => loc.InHorDistOf(building.Position, minDistanceBetweenTwoFishingPiers));
                if (fishingPierBlueprintInTheArea.Count() > 0)
                {
                    return(new AcceptanceReport("FishIndustry.FishingPier_ToCloseBlueprint".Translate()));
                }
            }
            if (fishingPierSpawnerFrameList != null)
            {
                IEnumerable <Thing> fishingPierFrameInTheArea = fishingPierSpawnerFrameList.Where(building => loc.InHorDistOf(building.Position, minDistanceBetweenTwoFishingPiers));
                if (fishingPierFrameInTheArea.Count() > 0)
                {
                    return(new AcceptanceReport("FishIndustry.FishingPier_ToCloseFrame".Translate()));
                }
            }
            if (fishingPierSpawnerOnMudBlueprintList != null)
            {
                IEnumerable <Thing> fishingPierOnMudBlueprintInTheArea = fishingPierSpawnerOnMudBlueprintList.Where(building => loc.InHorDistOf(building.Position, minDistanceBetweenTwoFishingPiers));
                if (fishingPierOnMudBlueprintInTheArea.Count() > 0)
                {
                    return(new AcceptanceReport("FishIndustry.FishingPier_ToCloseBlueprint".Translate()));
                }
            }
            if (fishingPierSpawnerOnMudFrameList != null)
            {
                IEnumerable <Thing> fishingPierOnMudFrameInTheArea = fishingPierSpawnerOnMudFrameList.Where(building => loc.InHorDistOf(building.Position, minDistanceBetweenTwoFishingPiers));
                if (fishingPierOnMudFrameInTheArea.Count() > 0)
                {
                    return(new AcceptanceReport("FishIndustry.FishingPier_ToCloseFrame".Translate()));
                }
            }

            // Display fish stock respawn rate.
            if ((Find.TickManager.Paused == false) &&
                (Find.TickManager.TicksGame > lastTextThrowTick + Find.TickManager.TickRateMultiplier * Verse.GenTicks.TicksPerRealSecond))
            {
                lastTextThrowTick = Find.TickManager.TicksGame;
                float fishStockRespawnRateAsFloat = Util_FishIndustry.GetAquaticCellsProportionInRadius(loc + new IntVec3(0, 0, 1).RotatedBy(rot), this.Map, Building_FishingPier.optimalAquaticAreaRadius) / Building_FishingPier.optimalAquaticCellsProportion;
                int   fishStockRespawnRateAsInt   = Mathf.RoundToInt(fishStockRespawnRateAsFloat * 100f);
                if (fishStockRespawnRateAsInt > 100)
                {
                    fishStockRespawnRateAsInt = 100;
                }
                Color textColor = Color.red;
                if (fishStockRespawnRateAsInt >= 75)
                {
                    textColor = Color.green;
                }
                else if (fishStockRespawnRateAsInt >= 25)
                {
                    textColor = Color.yellow;
                }
                string fishStockRespawnRateAsText = fishStockRespawnRateAsInt + "%";
                MoteMaker.ThrowText(loc.ToVector3Shifted(), this.Map, fishStockRespawnRateAsText, textColor);
            }

            return(true);
        }