public static bool Guard_Hediff_AnimalFertilityReduced(Pawn male, Pawn female) { if ( HybridDef.TryGetHybrids(male.kindDef, female.kindDef, out var hybridList) && hybridList.TryGetRandomElement(out var hybrid) && hybrid.fertilizationFailesIfGreaterThanZeroCurve.TryGetRandomValue( out var fertilizationFailesIfGreaterThanZero) && fertilizationFailesIfGreaterThanZero >= 0f ) { DogsMateMod.Debug( $"fertilizationFailesIfGreaterThanZero: {fertilizationFailesIfGreaterThanZero:0.000}, " + "failed" ); return(true); } var maleHediff = SeverityOf(male); var femaleHediff = SeverityOf(female); if (maleHediff <= 0f && femaleHediff <= 0f) { return(true); } if (maleHediff >= 1f || femaleHediff >= 1f) { DogsMateMod.Debug( $"male fertility: {(1f - maleHediff) * 100:0.0}%, " + $"female fertility: {(1f - femaleHediff) * 100:0.0}%, " + "can't fertilize" ); return(false); } var threshold = Mathf.Sqrt((1f - maleHediff) * (1f - femaleHediff) * (1f - Mathf.Max(maleHediff, femaleHediff))); DogsMateMod.Debug( $"male fertility: {(1f - maleHediff) * 100:0.0}%, " + $"female fertility: {(1f - femaleHediff) * 100:0.0}%, " + $"fertilation chance: {threshold * 50:0.0}%" // RimWorld lets every other fertilation fail. ); var value = Rand.Value; return(value <= threshold); }
public static bool Replace_DoBirthSpawn(Pawn mother, Pawn father) { if (father is null || mother.kindDef == father.kindDef) { return(true); } var litterCount = Math.Min(GetLitterCount(mother), GetLitterCount(father)); List <(PawnKindDef p, List <HybridDef> h)> hybridKinds = null; if ( DogsMateMod.TryGetCompatibleFemales(father.kindDef, out var dict) && dict.TryGetValue(mother.kindDef, out var hybridDefs) && hybridDefs.Count > 0 ) { hybridKinds = hybridDefs.Select(h => h.children.Where(c => c.IsUsable).Select(a => (a, h))) .SelectMany(x => x).Select(ah => ah.a.pawnKinds.Where(p => p != null).Select(p => (p, ah.h))) .SelectMany(x => x).GroupBy(ph => ph.p) .Select(g => (g.Key, g.Select(ph => ph.h).Where(h => h.IsUsable).ToList())).ToList(); DogsMateMod.Debug( $"father=<{father.kindDef.ToStringSafe()}> " + $"mother=<{mother.kindDef.ToStringSafe()}> " + $"hybrids=<{hybridKinds.Select(ph => ph.p.label).ToCommaList()}>" ); } Pawn child = null; for (var childIndex = 0; childIndex < litterCount; ++childIndex) { PawnKindDef childKind; HybridDef hybridDef = null; if (hybridKinds != null) { hybridKinds.TryGetRandomElement(out var ph); ph.h.TryGetRandomElement(out hybridDef); childKind = ph.p; } else if (Rand.Value > 0.5f) { childKind = mother.kindDef; } else { childKind = father.kindDef; } bool newChildIsGood; var newChild = PawnGenerator.GeneratePawn(new PawnGenerationRequest( childKind, mother.Faction, forceGenerateNewPawn: false, newborn: true )); if (PawnUtility.TrySpawnHatchedOrBornPawn(newChild, mother)) { if (newChild.playerSettings != null && mother.playerSettings != null) { newChild.playerSettings.AreaRestriction = mother.playerSettings.AreaRestriction; } if (newChild.RaceProps.IsFlesh) { newChild.relations.AddDirectRelation(PawnRelationDefOf.Parent, mother); newChild.relations.AddDirectRelation(PawnRelationDefOf.Parent, father); } if (hybridDef != null) { AddHediffs(newChild, hybridDef.childrenHediffs); switch (newChild.gender) { case Gender.Male: AddHediffs(newChild, hybridDef.maleChildrenHediffs); break; case Gender.Female: AddHediffs(newChild, hybridDef.femaleChildrenHediffs); break; } } newChildIsGood = true; } else { Find.WorldPawns.PassToWorld(newChild, PawnDiscardDecideMode.Discard); newChildIsGood = false; } TaleRecorder.RecordTale(TaleDefOf.GaveBirth, mother, newChild); if (newChildIsGood) { child = newChild; } } if (!mother.Spawned) { return(false); } FilthMaker.TryMakeFilth(mother.Position, mother.Map, ThingDefOf.Filth_AmnioticFluid, mother.LabelIndefinite(), 5); mother.caller?.DoCall(); child?.caller?.DoCall(); return(false); }
private static bool IsValidFemale(Thing femaleThing, Pawn malePawn, IReadOnlyDictionary <PawnKindDef, IReadOnlyCollection <HybridDef> > specialDict) { if (!(femaleThing is Pawn femalePawn)) { return(false); } if (malePawn == femalePawn) { return(false); } if (!specialDict.ContainsKey(femalePawn.kindDef)) { DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> " + $"female=<{femalePawn.ToStringSafe()}={femalePawn.kindDef.ToStringSafe()}> " + "not a valid mate" ); return(false); } if (femalePawn.Downed) { DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> " + $"female=<{femalePawn.ToStringSafe()}={femalePawn.kindDef.ToStringSafe()}> " + "female downed" ); return(false); } if (!femalePawn.CanCasuallyInteractNow()) { DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> " + $"female=<{femalePawn.ToStringSafe()}={femalePawn.kindDef.ToStringSafe()}> " + "cannot interact casually with female" ); return(false); } if (femalePawn.IsForbidden(malePawn)) { DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> " + $"female=<{femalePawn.ToStringSafe()}={femalePawn.kindDef.ToStringSafe()}> " + "female is forbidden" ); return(false); } if (femalePawn.Faction != malePawn.Faction) { DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> " + $"female=<{femalePawn.ToStringSafe()}={femalePawn.kindDef.ToStringSafe()}> " + "not same faction" ); return(false); } if (!PawnUtility.FertileMateTarget(malePawn, femalePawn)) { DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> " + $"female=<{femalePawn.ToStringSafe()}={femalePawn.kindDef.ToStringSafe()}> " + "female is not fertile" ); return(false); } DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> " + $"female=<{femalePawn.ToStringSafe()}={femalePawn.kindDef.ToStringSafe()}> " + "is valid pair" ); return(true); }
public static bool Replace_TryGiveJob(ref Job __result, Pawn pawn) { var malePawn = pawn; if (malePawn.GetComp <CompEggLayer>()?.props != null) { DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> is egg layer -> using default implementation"); return(true); } if (!DogsMateMod.TryGetCompatibleFemales(malePawn.kindDef, out var specialDict)) { DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> is not special -> using default implementation"); return(true); } if (malePawn.gender != Gender.Male) { DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> is male -> fail"); __result = null; return(false); } if (!malePawn.ageTracker.CurLifeStage.reproductive) { DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> cannot reproduce in this life stage -> fail"); __result = null; return(false); } if (GenClosest.ClosestThingReachable( malePawn.Position, malePawn.Map, ThingRequest.ForGroup(ThingRequestGroup .Pawn), // original implementation tests for "ThingRequest.ForDef(malePawn.def)" PathEndMode.Touch, TraverseParms.For(malePawn, Danger.Some), // original implementation allows "Danger.Deadly" 30f, femaleThing => IsValidFemale(femaleThing, malePawn, specialDict) ) is Pawn validFemalePawn) { DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> " + $"female=<{validFemalePawn.ToStringSafe()}={validFemalePawn.kindDef.ToStringSafe()}> " + "will mate -> success" ); __result = JobMaker.MakeJob(JobDefOf.Mate, validFemalePawn); } else { DogsMateMod.Debug( $"male=<{malePawn.ToStringSafe()}={malePawn.kindDef.ToStringSafe()}> " + "no valid female found -> fail" ); __result = null; } return(false); }