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()); }
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); }
// ===================== 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; } }
/// <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)); }
/// <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); }