Exemple #1
0
    public DogsMateMod(ModContentPack content) : base(content)
    {
        instance       = this;
        currentVersion =
            VersionFromManifest.GetVersionFromModMetaData(ModLister.GetActiveModWithIdentifier("Mlie.DogsMate"));
        var harmony = new Harmony(nameof(DogsMateMod));

        harmony.PatchAll();
    }
    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);
    }