Exemplo n.º 1
0
        /// <summary>
        /// GC().The best way to shrink Rimworld saves,I think.
        /// </summary>
        /// <param name="verbose">Determine if GC() should log details very very verbosely</param>
        /// <returns>Count of disposed World pawns</returns>
        public int GC(bool verbose = false)
        {
            /*
             * TODO Log
             * 1.talelog by interest             -X
             * 2.animal  - deconstruct relation  -Done
             * 3.deeperclean?remove hediffs      -X
             * 5.correct verbose log             -Done
             * 6.yield return "status"           -X
             * 7.UI compability                  -Done
             * 8.Filth cleaner                   -Done
             * 9.Fix:Faction Leader              -Done
             * 10.Fix:Faction Relations          -Done
             * 12.Warp->Wrap                     -Done
             */

            /*
             * TODO A18
             *  4.adjustable GC depth           -X
             *  11.GC boostup                   -Done
             *  13.remake GC System             -Done
             *  13.Keyed in Float menu items    -Done
             *  14.help contents                -Done
             *  15.Optimize Cleanser frame      -Done
             *  16.Optimize Floatmenu System    -Done
             *  17.Debug only options           -Done
             */

            /*
             * TODO 1.0
             *  18.Clean snow                   -Done
             *  19.whole-map clean              -Done
             *  20.remake log                   -Done
             *  21.Mod framework                -Done
             *  22.settings                     -Done
             *  23.MuteGC                       -Done
             *  24.MuteCL                       -Done
             *  25.remake FloatMenuUtil         -Done
             *  26.timer of gc                  -X
             *  27.toolbox integration          -Done
             *  28.MainButtonDef into xml       -Done
             *  29.try catch                    -Done
             *  30.Find.CurrentMap==null check  -Done
             *  31.MainButtonWorker             -Done
             *  32.Messages.Message(str,historical) settings -Done
             *  33.AvoidGrid rework             -Done
             *  34.Faction rework & cleanup     -Done
             *  35.Close letter stack           -Done
             */

            if (Current.ProgramState != ProgramState.Playing)
            {
                Verse.Log.Error("You must be kidding me...GC a save without loading one?");
                return(0);
            }

            /*Initialization*/
            Verse.Log.Message("[GC Log] Pre-Initializing GC...");
            this.reference = Find.WorldPawns.AllPawnsAliveOrDead.ToList();
            this.allFlags.Clear();
            this.verbose = verbose;
            if (verbose)
            {
                allFlagsCounter.Clear();
                allFlagsCounter.Add(Flags.None, 0);
                for (int j = 0; j < FlagsCountNotNull; j++)
                {
                    allFlagsCounter.Add((Flags)(1 << j), 0);
                }
            }

            /*Generate EntryPoints from Map Pawns*/
            Verse.Log.Message("[GC Log] Generating EntryPoints from Map Pawns...");
            List <Pawn> mapPawnEntryPoints;

            DiagnoseMapPawns(out mapPawnEntryPoints);
            if (verbose)
            {
                Verse.Log.Message("[GC Log][Verbose] " + allPawnsCounter.Count().ToString() + " Map Pawns marked during diagnosis");
            }

            /*Reset counters*/
            allPawnsCounter.Clear();
            if (verbose)
            {
                allFlagsCounter.Clear();
                allFlagsCounter.Add(Flags.None, 0);
                for (int j = 0; j < FlagsCountNotNull; j++)
                {
                    allFlagsCounter.Add((Flags)(1 << j), 0);
                }
            }

            /*Generate a list of pawns concerned by used Tales*/
            Verse.Log.Message("[GC Log] Collecting Pawns concerned by Used Tales...");
            List <Pawn> allUsedTalePawns;

            CleanserUtil.InitUsedTalePawns(out allUsedTalePawns);

            /*Diagnosis:marking entries on WorldPawns.*/
            Verse.Log.Message("[GC Log] Running diagnosis on WorldPawns...");
            foreach (Pawn p in reference)
            {
                if (p.IsColonist)
                {
                    addFlag(p, Flags.Colonist | Flags.RelationLvl2);
                }
                if (p.IsPrisonerOfColony)
                {
                    addFlag(p, Flags.Prisoner | Flags.RelationLvl2);
                }
                if (PawnUtility.IsFactionLeader(p))
                {
                    addFlag(p, Flags.KeptWorldPawn | Flags.FactionLeader | Flags.RelationLvl1);
                }
                if (PawnUtility.IsKidnappedPawn(p))
                {
                    addFlag(p, Flags.KeptWorldPawn | Flags.RelationLvl2);
                }
                if (p.Corpse != null)
                {
                    addFlag(p, Flags.CorpseOwner | Flags.RelationLvl1);
                }
                if (allUsedTalePawns.Contains(p))
                {
                    addFlag(p, Flags.TaleEntryOwner | Flags.RelationLvl0);
                }

                if (p.InContainerEnclosed)
                {
                    addFlag(p, Flags.KeptWorldPawn | Flags.RelationLvl0);
                }
                if (p.Spawned)
                {
                    addFlag(p, Flags.RelationLvl0);
                }
                if (p.IsPlayerControlledCaravanMember())
                {
                    addFlag(p, Flags.KeptWorldPawn | Flags.RelationLvl2);
                }
                if (PawnUtility.IsTravelingInTransportPodWorldObject(p))
                {
                    addFlag(p, Flags.KeptWorldPawn | Flags.RelationLvl2);
                }

                //Patch:A18 new entry
                if (PawnUtility.ForSaleBySettlement(p))
                {
                    addFlag(p, Flags.OnSale | Flags.RelationLvl0);
                }

                if (verbose)
                {
                    Verse.Log.Message("[worldPawn] " + p.LabelShort + " [flag] " + markedFlagsString(p));
                }
            }

            if (verbose)
            {
                Verse.Log.Message("[GC Log][Verbose] " + allPawnsCounter.Count().ToString() + " World Pawns marked during diagnosis");
            }


            int i;

            /*Expansion 1:Expand relation network from map pawns.*/
            Verse.Log.Message("[GC Log] Expanding Relation networks through Map Pawn Entry Points...");
            for (i = mapPawnEntryPoints.Count - 1; i > -1; i--)
            {
                if (containsFlag(mapPawnEntryPoints[i], Flags.RelationLvl2))
                {
                    expandRelation(mapPawnEntryPoints[i], Flags.RelationLvl1);
                    mapPawnEntryPoints.RemoveAt(i);
                }
            }

            for (i = mapPawnEntryPoints.Count - 1; i > -1; i--)
            {
                if (containsFlag(mapPawnEntryPoints[i], Flags.RelationLvl1))
                {
                    expandRelation(mapPawnEntryPoints[i], Flags.RelationLvl0);
                    mapPawnEntryPoints.RemoveAt(i);
                }
            }

            /*Its unnecessary to process RelationLvl0 in mapPawnEntryPoints,
             * for they are not related to any world pawns.
             */

            /*Expansion 2:Expand relation network from world pawns.*/
            Verse.Log.Message("[GC Log] Expanding Relation networks on marked World Pawns...");
            for (i = reference.Count - 1; i > -1; i--)
            {
                if (containsFlag(reference[i], Flags.RelationLvl2))
                {
                    expandRelation(reference[i], Flags.RelationLvl1);
                    reference.RemoveAt(i);
                }
            }

            for (i = reference.Count - 1; i > -1; i--)
            {
                if (containsFlag(reference[i], Flags.RelationLvl1))
                {
                    expandRelation(reference[i], Flags.RelationLvl0);
                    reference.RemoveAt(i);
                }
            }

            for (i = reference.Count - 1; i > -1; i--)
            {
                if (containsFlag(reference[i], Flags.RelationLvl0))
                {
                    reference.RemoveAt(i);
                }
            }


            int a = 0;

            /*VerboseMode:counting addFlag() calls.*/
            if (verbose)
            {
                foreach (KeyValuePair <Pawn, int> p in allPawnsCounter)
                {
                    a += p.Value;
                }
                Verse.Log.Message("[GC Log][Verbose] " + allPawnsCounter.Count().ToString() + " World Pawns marked during Expanding");
                if (debug)
                {
                    Verse.Log.Message("addFlag() called " + a + " times");
                }
            }


            /*Posfix:remove UsedTalePawns.*/
            Verse.Log.Message("[GC Log] Excluding Pawns concerned by Used Tales...");
            foreach (Pawn p in allUsedTalePawns)
            {
                reference.Remove(p);
            }

            /*Exclude quest pawns*/
            var questThings = Find.QuestManager.QuestsListForReading
                              .Where(q => q.State == QuestState.NotYetAccepted)
                              .SelectMany(q => q.QuestLookTargets)
                              .Where(x => x.Thing != null)
                              .Select(x => x.Thing)
                              .Distinct()
                              .ToList();

            Verse.Log.Message($"[GC Log] Excluding Quest Pawns: {reference.RemoveAll(p => questThings.Contains(p))}");


            /*GC Core:dispose all pawns left in reference list.*/
            Verse.Log.Message("[GC Log] Disposing World Pawns...");
            a = reference.Count;
            Pawn pawn;

            for (i = reference.Count - 1; i > -1; i--)
            {
                pawn = reference[i];
                //Patch:Mysterious WorldPawn.missing
                //Update:This patch is disabled due to safety concerns.
                //if(Find.WorldPawns.Contains(pawn))
                Find.WorldPawns.RemovePawn(pawn);
                if (!pawn.Destroyed)
                {
                    pawn.Destroy(DestroyMode.Vanish);
                }
                if (!pawn.Discarded)
                {
                    pawn.Discard(true);
                }
            }

            /*VerboseMode:Finalize output*/
            if (verbose)
            {
                string s = "[GC Log][Verbose] Flag calls stat:";
                allFlagsCounter.Remove(Flags.None);
                foreach (KeyValuePair <Flags, int> pair in allFlagsCounter)
                {
                    s += "\n  " + pair.Key.ToString() + " : " + pair.Value;
                }
                Verse.Log.Message(s);
            }

            Verse.Log.Message("[GC Log] GC() completed with " + a + " World Pawns disposed");

            return(a);
        }
Exemplo n.º 2
0
 private string GetCriticalPawnReason(Pawn pawn)
 {
     if (pawn.Discarded)
     {
         return(null);
     }
     if (PawnUtility.EverBeenColonistOrTameAnimal(pawn) && pawn.RaceProps.Humanlike)
     {
         return("Colonist");
     }
     if (PawnGenerator.IsBeingGenerated(pawn))
     {
         return("Generating");
     }
     if (PawnUtility.IsFactionLeader(pawn))
     {
         return("FactionLeader");
     }
     if (PawnUtility.IsKidnappedPawn(pawn))
     {
         return("Kidnapped");
     }
     if (pawn.IsCaravanMember())
     {
         return("CaravanMember");
     }
     if (PawnUtility.IsTravelingInTransportPodWorldObject(pawn))
     {
         return("TransportPod");
     }
     if (PawnUtility.ForSaleBySettlement(pawn))
     {
         return("ForSale");
     }
     if (Find.WorldPawns.ForcefullyKeptPawns.Contains(pawn))
     {
         return("ForceKept");
     }
     if (pawn.SpawnedOrAnyParentSpawned)
     {
         return("Spawned");
     }
     if (!pawn.Corpse.DestroyedOrNull())
     {
         return("CorpseExists");
     }
     if (pawn.RaceProps.Humanlike && Current.ProgramState == ProgramState.Playing)
     {
         if (Find.PlayLog.AnyEntryConcerns(pawn))
         {
             return("InPlayLog");
         }
         if (Find.BattleLog.AnyEntryConcerns(pawn))
         {
             return("InBattleLog");
         }
     }
     if (Current.ProgramState == ProgramState.Playing && Find.TaleManager.AnyActiveTaleConcerns(pawn))
     {
         return("InActiveTale");
     }
     return(null);
 }
Exemplo n.º 3
0
        private static void _AppendThoughts_Humanlike(Pawn victim, DamageInfo?dinfo, Hediff hediff, PawnDiedOrDownedThoughtsKind thoughtsKind, List <IndividualThoughtToAdd> outIndividualThoughts, List <ThoughtDef> outAllColonistsThoughts)
        {
            var  IsExecution        = typeof(PawnDiedOrDownedThoughtsUtility).GetMethod("IsExecution", BindingFlags.Static | BindingFlags.NonPublic);
            var  IsInnocentPrisoner = typeof(PawnDiedOrDownedThoughtsUtility).GetMethod("IsInnocentPrisoner", BindingFlags.Static | BindingFlags.NonPublic);
            bool flag  = (bool)IsExecution.Invoke(null, new object[] { dinfo, hediff });
            bool flag2 = (bool)IsInnocentPrisoner.Invoke(null, new object[] { victim });
            bool flag3 = dinfo.HasValue && dinfo.Value.Def.externalViolence && dinfo.Value.Instigator != null && dinfo.Value.Instigator is Pawn;

            if (flag3)
            {
                Pawn pawn = (Pawn)dinfo.Value.Instigator;
                if (!pawn.Dead && pawn.needs.mood != null && pawn.story != null && pawn != victim)
                {
                    if (thoughtsKind == PawnDiedOrDownedThoughtsKind.Died)
                    {
                        outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.KilledHumanlikeBloodlust, pawn, null, 1f, 1f));
                    }
                    if (thoughtsKind == PawnDiedOrDownedThoughtsKind.Died && victim.HostileTo(pawn))
                    {
                        if (victim.Faction != null && PawnUtility.IsFactionLeader(victim) && victim.Faction.HostileTo(pawn.Faction))
                        {
                            outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.DefeatedHostileFactionLeader, pawn, victim, 1f, 1f));
                        }
                        else if (victim.Faction != null && victim.Faction.HostileTo(pawn.Faction) && !flag2)
                        {
                            outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOfPsychology.KilledHumanlikeEnemy, pawn, victim, 1f, 1f));
                        }
                        if (victim.kindDef.combatPower > 250f)
                        {
                            outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.DefeatedMajorEnemy, pawn, victim, 1f, 1f));
                        }
                    }
                }
            }
            if (thoughtsKind == PawnDiedOrDownedThoughtsKind.Died && victim.Spawned)
            {
                List <Pawn> allPawnsSpawned = victim.Map.mapPawns.AllPawnsSpawned;
                for (int i = 0; i < allPawnsSpawned.Count; i++)
                {
                    Pawn pawn2 = allPawnsSpawned[i];
                    if (pawn2 != victim && pawn2.needs.mood != null)
                    {
                        if (!flag && (pawn2.MentalStateDef != MentalStateDefOf.SocialFighting || ((MentalState_SocialFighting)pawn2.MentalState).otherPawn != victim))
                        {
                            if (pawn2.Position.InHorDistOf(victim.Position, 12f) && GenSight.LineOfSight(victim.Position, pawn2.Position, victim.Map, false) && pawn2.Awake() && pawn2.health.capacities.CapableOf(PawnCapacityDefOf.Sight))
                            {
                                if (pawn2.Faction == victim.Faction)
                                {
                                    outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.WitnessedDeathAlly, pawn2, null, 1f, 1f));
                                    outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOfPsychology.WitnessedDeathAllyBleedingHeart, pawn2, null, 1f, 1f));
                                }
                                else if (victim.Faction == null || (!victim.Faction.HostileTo(pawn2.Faction) || pawn2.story.traits.HasTrait(TraitDefOfPsychology.BleedingHeart)))
                                {
                                    outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.WitnessedDeathNonAlly, pawn2, null, 1f, 1f));
                                    outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOfPsychology.WitnessedDeathNonAllyBleedingHeart, pawn2, null, 1f, 1f));
                                }
                                if (pawn2.relations.FamilyByBlood.Contains(victim))
                                {
                                    outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.WitnessedDeathFamily, pawn2, null, 1f, 1f));
                                }
                                outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.WitnessedDeathBloodlust, pawn2, null, 1f, 1f));
                                if (!pawn2.story.traits.HasTrait(TraitDefOfPsychology.BleedingHeart) && !pawn2.story.traits.HasTrait(TraitDefOf.Psychopath) && !pawn2.story.traits.HasTrait(TraitDefOf.Bloodlust) && !pawn2.story.traits.HasTrait(TraitDefOfPsychology.Desensitized))
                                {
                                    if (((pawn2.GetHashCode() ^ (GenLocalDate.DayOfYear(pawn2) + GenLocalDate.Year(pawn2) + (int)(GenLocalDate.DayPercent(pawn2) * 5) * 60) * 391) % 1000) == 0)
                                    {
                                        pawn2.story.traits.GainTrait(new Trait(TraitDefOfPsychology.Desensitized));
                                        pawn2.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOfPsychology.RecentlyDesensitized, null);
                                    }
                                }
                            }
                            else if (victim.Faction == Faction.OfPlayer && victim.Faction == pawn2.Faction && victim.HostFaction != pawn2.Faction)
                            {
                                outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.KnowColonistDied, pawn2, null, 1f, 1f));
                                outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOfPsychology.KnowColonistDiedBleedingHeart, pawn2, null, 1f, 1f));
                            }
                            if (flag2 && pawn2.Faction == Faction.OfPlayer && !pawn2.IsPrisoner)
                            {
                                outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.KnowPrisonerDiedInnocent, pawn2, null, 1f, 1f));
                                outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOfPsychology.KnowPrisonerDiedInnocentBleedingHeart, pawn2, null, 1f, 1f));
                            }
                        }
                    }
                }
            }
            if (thoughtsKind == PawnDiedOrDownedThoughtsKind.Abandoned && victim.IsColonist)
            {
                outAllColonistsThoughts.Add(ThoughtDefOf.ColonistAbandoned);
                outAllColonistsThoughts.Add(ThoughtDefOfPsychology.ColonistAbandonedBleedingHeart);
            }
            if (thoughtsKind == PawnDiedOrDownedThoughtsKind.AbandonedToDie)
            {
                if (victim.IsColonist)
                {
                    outAllColonistsThoughts.Add(ThoughtDefOf.ColonistAbandonedToDie);
                    outAllColonistsThoughts.Add(ThoughtDefOfPsychology.ColonistAbandonedToDieBleedingHeart);
                }
                else if (victim.IsPrisonerOfColony)
                {
                    outAllColonistsThoughts.Add(ThoughtDefOf.PrisonerAbandonedToDie);
                    outAllColonistsThoughts.Add(ThoughtDefOfPsychology.PrisonerAbandonedToDieBleedingHeart);
                }
            }
        }
Exemplo n.º 4
0
            public static bool Replacement(ref string __result, Pawn pawn, Pawn fromPOV)
            {
                if (pawn.Dead)
                {
                    __result = "Dead".Translate();
                    return(false);
                }
                if (pawn.Destroyed)
                {
                    __result = "Missing".Translate();
                    return(false);
                }
                if (PawnUtility.IsKidnappedPawn(pawn))
                {
                    __result = "Kidnapped".Translate();
                    return(false);
                }
                if (pawn.kindDef == PawnKindDefOf.Slave)
                {
                    __result = "Slave".Translate();
                    return(false);
                }
                if (PawnUtility.IsFactionLeader(pawn))
                {
                    __result = "FactionLeader".Translate();
                    return(false);
                }
                Faction faction = pawn.Faction;

                if (faction == fromPOV.Faction)
                {
                    __result = string.Empty;
                    return(false);
                }
                if (faction == null || fromPOV.Faction == null)
                {
                    __result = "Neutral".Translate();
                    return(false);
                }

                #region ADDED
                if (faction == Faction.OfPlayer)
                {
                    __result = "Colony".Translate();
                    return(false);
                }
                #endregion

                switch (faction.RelationKindWith(fromPOV.Faction))
                {
                case FactionRelationKind.Hostile:
                    __result = "Hostile".Translate() + ", " + faction.Name;
                    return(false);

                case FactionRelationKind.Neutral:
                    __result = "Neutral".Translate() + ", " + faction.Name;
                    return(false);

                case FactionRelationKind.Ally:
                    __result = "Ally".Translate() + ", " + faction.Name;
                    return(false);

                default:
                    __result = "";
                    return(false);
                }
            }
Exemplo n.º 5
0
        private void HandleExcitementOfKiller(Pawn recipient)
        {
            // Killer != null => DamageInfo != null
            // The obligatory .Value is extremely triggering.
            if (Killer != null)
            {
                if (Killer == PrimaryVictim)
                {
                    // We don't handle suicide cases.
                    return;
                }

                if (recipient == Killer && KillingBlowDamageDef.ExternalViolenceFor(Victim))
                {
                    // Confirmed NewsReceipient is Killer

                    // IsCapableOfThought has already been called.
                    // NewsReceipient is definitely capable of thought.

                    // Why this check tho
                    if (recipient.story != null)
                    {
                        // Bloodlust thoughts for Bloodlust guys, currently only for human victims
                        // We can expand upon this, and add in witnessed death (animals) with bloodlust
                        if (Victim.RaceProps.Humanlike)
                        {
                            // Try to add Bloodlust thoughts; will be auto-rejected if recipient does not have Bloodlust
                            new IndividualThoughtToAdd(ThoughtDefOf.KilledHumanlikeBloodlust, recipient).Add();

                            // Try to add Defeated Hostile Leader thoughts
                            if (Victim.HostileTo(Killer) && Victim.Faction != null && PawnUtility.IsFactionLeader(Victim) && Victim.Faction.HostileTo(Killer.Faction))
                            {
                                new IndividualThoughtToAdd(ThoughtDefOf.DefeatedHostileFactionLeader, Killer, Victim).Add();
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 6
0
        /// <summary>
        /// Attempts to give the killer their appropriate excitement (or disappointment). Aborts if there is no killer.
        /// </summary>
        /// <param name="recipient"></param>
        private void TryProcessKillerHigh(Pawn recipient)
        {
            // Killer != null => DamageInfo != null
            if (Killer != null)
            {
                // Currently you can't really kill yourself.
                // That would be something interesting, but we don't do that here.
                if (recipient == Killer)
                {
                    // IDK, is this something we can consider checking?
                    if (KillingBlowDamageDef.ExternalViolenceFor(Victim))
                    {
                        // Why this check tho
                        if (recipient.story != null)
                        {
                            // Bloodlust thoughts for Bloodlust guys, currently only for human victims
                            // We can expand upon this, and add in witnessed death (animals) with bloodlust
                            if (Victim.RaceProps.Humanlike)
                            {
                                // Try to add Bloodlust thoughts; will be auto-rejected if recipient does not have Bloodlust
                                new IndividualThoughtToAdd(ThoughtDefOf.KilledHumanlikeBloodlust, recipient).Add();

                                // Try to add Defeated Hostile Leader thoughts
                                if (Victim.HostileTo(Killer) && Victim.Faction != null && PawnUtility.IsFactionLeader(Victim) && Victim.Faction.HostileTo(Killer.Faction))
                                {
                                    new IndividualThoughtToAdd(ThoughtDefOf.DefeatedHostileFactionLeader, Killer, Victim).Add();
                                }
                            }
                        }
                    }
                }
            }

            /*
             * // Note that "animal bonds" is a type of relationships.
             * if (Killer != Victim && Killer == recipient)
             * {
             *  HandleExcitementOfKiller(recipient);
             * }
             */
        }
Exemplo n.º 7
0
        IEnumerable <Thing> GenerateThingEnumer(int forTile, Faction forFaction)
        {
            int numKinds             = kindCountRange.RandomInRange;
            int count                = countRange.RandomInRange;
            List <PawnKindDef> kinds = new List <PawnKindDef>();

            for (int j = 0; j < numKinds; j++)
            {
                if (!(from k in DefDatabase <PawnKindDef> .AllDefs
                      where !kinds.Contains(k) && PawnKindAllowed(k, forTile)
                      select k).TryRandomElementByWeight((PawnKindDef k) => SelectionChance(k), out PawnKindDef result))
                {
                    break;
                }
                kinds.Add(result);
            }
            for (int i = 0; i < count; i++)
            {
                if (!kinds.TryRandomElement(out PawnKindDef kind))
                {
                    break;
                }
                PawnKindDef kind2 = kind;
                int         tile  = forTile;

                Pawn pawnOriginal = Find.WorldPawns.AllPawnsAlive.Where(p => !p.IsPlayerControlledCaravanMember() && (PawnUtility.ForSaleBySettlement(p) || p.kindDef == PawnKindDefOf.Slave || (PawnUtility.IsKidnappedPawn(p) && p.RaceProps.Humanlike) && !PawnUtility.IsFactionLeader(p))).RandomElement();

                PawnGenerationRequest request = new PawnGenerationRequest(kind2, null, PawnGenerationContext.NonPlayer, tile);
                Pawn pawn = PawnGenerator.GeneratePawn(request); //Generate the animal!



                if (pawnOriginal == null)
                {
                    Gender newGender = pawn.gender;

                    if (Rand.RangeInclusive(0, 100) <= 25)
                    {
                        switch (pawn.gender)
                        {
                        case (Gender.Male):
                            newGender = Gender.Female;
                            break;

                        case (Gender.Female):
                            newGender = Gender.Male;
                            break;

                        default:
                            break;
                        }
                    }

                    float animalAge            = pawn.ageTracker.AgeBiologicalYearsFloat;
                    float animalLifeExpectancy = pawn.def.race.lifeExpectancy;
                    float humanLifeExpectancy  = 80f;


                    float age = animalAge * humanLifeExpectancy / animalLifeExpectancy;
                    age = Mathf.Max(age, 17); //make sure the human is at least 17 years old
                    float chronoAge = pawn.ageTracker.AgeChronologicalYears * age / animalAge;
                    var   pkds      = new List <PawnKindDef>
                    {
                        PawnKindDefOf.Slave,
                        PawnKindDefOf.Colonist,
                        PawnKindDefOf.SpaceRefugee,
                        PawnKindDefOf.Villager,
                        PawnKindDefOf.Drifter,
                        PawnKindDefOf.AncientSoldier
                    };


                    PawnKindDef rKind = pkds.RandElement();

                    var hRequest = new PawnGenerationRequest(rKind, Faction.OfPlayer,
                                                             PawnGenerationContext.NonPlayer, fixedBiologicalAge: age,
                                                             fixedChronologicalAge: chronoAge, fixedGender: newGender);


                    pawnOriginal = PawnGenerator.GeneratePawn(hRequest);


                    pawn.Name = pawnOriginal.Name;
                }
                else
                {
                    pawn.Name = pawnOriginal.Name;
                    Find.WorldPawns.RemovePawn(pawnOriginal);
                }

                var pm = TfSys.TransformedPawn.Create(pawnOriginal, pawn); //pawnOriginal is human, pawn is animal
                FormerHumanUtilities.MakeAnimalSapient(pawnOriginal, pawn, Rand.Range(0.5f, 1f));
                Find.World.GetComponent <PawnmorphGameComp>().AddTransformedPawn(pm);

                yield return(pawn);
            }
        }