public static bool RegisterInRegions(Thing thing, Map map)
        {
            if (!ListerThings.EverListable(thing.def, ListerThingsUse.Region))
            {
                return(false);
            }
            int tID = Thread.CurrentThread.ManagedThreadId;

            if (!tmpRegionsLists.TryGetValue(tID, out List <Region> tmpRegions))
            {
                tmpRegions           = new List <Region>();
                tmpRegionsLists[tID] = tmpRegions;
            }
            else
            {
                tmpRegions.Clear();
            }
            RegionListersUpdater.GetTouchableRegions(thing, map, tmpRegions, false);
            for (int i = 0; i < tmpRegions.Count; i++)
            {
                ListerThings listerThings = tmpRegions[i].ListerThings;
                lock (listerThings)
                {
                    if (!listerThings.Contains(thing))
                    {
                        listerThings.Add(thing);
                    }
                }
            }
            return(false);
        }
        private void TransformPawn(ThingDef @into, PawnKindDef intoKind)
        {
            Log.Message("Performing Transform on " + pawn + " to a " + into + "with a kind of " + intoKind);
            if (into == null || into == pawn.def)
            {
                return;
            }

            var map = pawn.Map;

            RegionListersUpdater.DeregisterInRegions(pawn, map);
            pawn.def = into;

            pawn.ChangeKind(intoKind);
            RegionListersUpdater.RegisterInRegions(pawn, map);

            map.mapPawns.UpdateRegistryForPawn(pawn);

            //save the pawn
            pawn.ExposeData();

            //magic to make image change
            pawn.ageTracker.AgeBiologicalTicks = pawn.ageTracker.AgeBiologicalTicks - 1;
            //pawn.SpawnSetup(map, true);


            didIt = true;
        }
Exemple #3
0
        /// <summary>
        /// destroying a resource outright causes too much overhead: fog, area reveal, pathing, roof updates, etc
        ///	we just want to replace it. So, we manually strip it out of the map and do some cleanup.
        /// The following is Thing.Despawn code with the unnecessary (for buildings, ar least) parts stripped out, plus key parts from Building.Despawn
        /// TODO: This approach may break with future releases (if thing despawning changes), so it's worth checking over.
        /// </summary>
        private static void SneakilyDestroyResource(Thing res)
        {
            var map = res.Map;

            RegionListersUpdater.DeregisterInRegions(res, map);
            map.spawnedThings.Remove(res);
            map.listerThings.Remove(res);
            map.thingGrid.Deregister(res);
            map.coverGrid.DeRegister(res);
            map.tooltipGiverList.Notify_ThingDespawned(res);
            if (res.def.graphicData != null && res.def.graphicData.Linked)
            {
                map.linkGrid.Notify_LinkerCreatedOrDestroyed(res);
                map.mapDrawer.MapMeshDirty(res.Position, MapMeshFlag.Things, true, false);
            }
            Find.Selector.Deselect(res);
            res.DirtyMapMesh(map);
            if (res.def.drawerType != DrawerType.MapMeshOnly)
            {
                map.dynamicDrawManager.DeRegisterDrawable(res);
            }
            ReflectionCache.Thing_State.SetValue(res, res.def.DiscardOnDestroyed ? ThingDiscardedState : ThingMemoryState);
            Find.TickManager.DeRegisterAllTickabilityFor(res);
            map.attackTargetsCache.Notify_ThingDespawned(res);
            StealAIDebugDrawer.Notify_ThingChanged(res);
            // building-specific cleanup
            var b = (Building)res;

            if (res.def.IsEdifice())
            {
                map.edificeGrid.DeRegister(b);
            }
            var sustainer = (Sustainer)ReflectionCache.Building_SustainerAmbient.GetValue(res);

            if (sustainer != null)
            {
                sustainer.End();
            }
            map.mapDrawer.MapMeshDirty(b.Position, MapMeshFlag.Buildings);
            map.glowGrid.MarkGlowGridDirty(b.Position);
            map.listerBuildings.Remove((Building)res);
            map.listerBuildingsRepairable.Notify_BuildingDeSpawned(b);
            map.designationManager.Notify_BuildingDespawned(b);
        }
Exemple #4
0
        private void TransformPawn(bool changeDef = true, bool keep = false)
        {
            IntVec3 where = pawn.Position;
            Map map = pawn.Map;

            //Body change to Astarte

            pawn.DeSpawn();
            //    pawn.DestroyOrPassToWorld();
            RegionListersUpdater.DeregisterInRegions(pawn, map);

            var thingDef = PawnThingDef();

            if (changeDef && thingDef != null)
            {
                HediffSet set = pawn.health.hediffSet;
                pawn.def    = thingDef;
                pawn.health = new Pawn_HealthTracker(pawn);
                pawn.health.hediffSet.DirtyCache();
                pawn.health.hediffSet = set;
            }
            //    pawn.SpawnSetup(map, true);

            RegionListersUpdater.RegisterInRegions(pawn, map);


            map.mapPawns.UpdateRegistryForPawn(pawn);
            //remove the 19

            if (BlowOffParts(keep))
            {
                RemoveAstarteParts();
            }

            //decache graphics
            pawn.Drawer.renderer.graphics.ResolveAllGraphics();

            //save the pawn
            pawn.ExposeData();

            GenSpawn.Spawn(pawn, where, map);
            //    pawn.Position = map.Center;
        }
        private void TransformPawn(bool changeDef = true, bool keep = false)
        {
            //Body change to Astarte
            var where = pawn.Position;

            var map = pawn.Map;

            pawn.DestroyOrPassToWorld();
            pawn.DeSpawn();
            RegionListersUpdater.DeregisterInRegions(pawn, map);

            var thingDef = PawnThingDef();

            if (changeDef && thingDef != null)
            {
                pawn.def = thingDef;
            }


            pawn.SpawnSetup(map, true);

            RegionListersUpdater.RegisterInRegions(pawn, map);


            map.mapPawns.UpdateRegistryForPawn(pawn);

            //remove the 19

            if (BlowOffParts(keep))
            {
                RemoveAstarteParts();
            }

            //decache graphics
            pawn.Drawer.renderer.graphics.ResolveAllGraphics();

            //save the pawn
            pawn.ExposeData();

            pawn.Position = map.Center;
        }
        public static bool DeregisterInRegions(Thing thing, Map map)
        {
            if (!ListerThings.EverListable(thing.def, ListerThingsUse.Region))
            {
                return(false);
            }
            List <Region> tmpRegions = new List <Region>();

            RegionListersUpdater.GetTouchableRegions(thing, map, tmpRegions, true);
            for (int i = 0; i < tmpRegions.Count; i++)
            {
                ListerThings listerThings = tmpRegions[i].ListerThings;
                lock (listerThings)
                {
                    if (listerThings.Contains(thing))
                    {
                        listerThings.Remove(thing);
                    }
                }
            }
            return(false);
        }
        private void TransformPawn(bool changeDef = true, bool keep = false)
        {
            //sets position, faction and map
            IntVec3 intv    = Pawn.Position;
            Faction faction = Pawn.Faction;
            Map     map     = Pawn.Map;

            RegionListersUpdater.DeregisterInRegions(Pawn, map);

            //Change Race to Props.raceDef
            if (changeDef && alienRace != null && alienRace != Pawn.def)
            {
                Pawn.def = alienRace;
                long ageB = Pawn.ageTracker.AgeBiologicalTicks;
                long ageC = Pawn.ageTracker.AgeChronologicalTicks;
                Pawn.ageTracker = new Pawn_AgeTracker(Pawn);
                Pawn.ageTracker.AgeBiologicalTicks    = ageB;
                Pawn.ageTracker.AgeChronologicalTicks = ageC;
                if (!Pawn.RaceProps.hasGenders)
                {
                    Pawn.gender = Gender.None;
                }
                if (Props.removeHair)
                {
                    //    Pawn.story.hairDef = DefDatabase<HairDef>.GetNamed("Shaved", true);
                    Pawn.story.hairDef = PawnHairChooser.RandomHairDefFor(Pawn, noHairFaction);
                    //   Pawn.Drawer.renderer.graphics.hairGraphic;
                }
                else
                {
                    if (Props.colourHairTwo || Props.colourHair)
                    {
                        if (compAlien != null)
                        {
                            ColorChannelGenerator Alienhair = alienRace.alienRace.generalSettings.alienPartGenerator.colorChannels.Find(x => x.name == "hair");
                            AlienPartGenerator.ExposableValueTuple <UnityEngine.Color, UnityEngine.Color> hair;
                            if (compAlien.ColorChannels.TryGetValue("hair", out hair))
                            {
                                if (Props.colourHair && Alienhair?.first != null)
                                {
                                    hair.first = Alienhair.first.NewRandomizedColor();
                                }
                                if (Props.colourHairTwo && Alienhair?.second != null)
                                {
                                    hair.second = Alienhair.second.NewRandomizedColor();
                                }
                                compAlien.ColorChannels.SetOrAdd("hair", hair);
                            }
                            Pawn.Notify_ColorChanged();
                        }
                    }
                }
                //Change BodyType to Props.bodyTypeDef
                if (Props.changeBody)
                {
                    if (Props.bodyTypeDef != null)
                    {
                        ChangeBodyType(Pawn, Props.bodyTypeDef);
                    }
                    else
                    {
                        ChangeBodyType(Pawn, alienRace.alienRace.generalSettings.alienPartGenerator.alienbodytypes[Rand.Range(0, alienRace.alienRace.generalSettings.alienPartGenerator.alienbodytypes.Count)]);
                    }
                }
            }
            //Remove Disallowed Traits
            int maxTraits;

            if (MoreTraitSlotsUtil.TryGetMaxTraitSlots(out int max))
            {
                maxTraits = max;
            }
            else
            {
                maxTraits = 4;
            }
            if (!Props.traitsToRemove.NullOrEmpty())
            {
                if (Pawn.story.traits.allTraits.Any(x => Props.traitsToRemove.Any(y => y.def == x.def)))
                {
                    foreach (ExtendedTraitEntry item in Props.traitsToRemove)
                    {
                        if (Pawn.story.traits.HasTrait(item.def))
                        {
                            Rand.PushState();
                            if (Rand.Chance(item.Chance))
                            {
                                Pawn.story.traits.allTraits.Remove(Pawn.story.traits.allTraits.Find(x => x.def == item.def));
                            }
                            Rand.PopState();
                        }
                    }
                }
            }
            if (!Props.traitsToAdd.NullOrEmpty())
            {
                if (Props.traitsToAdd.Any(x => !Pawn.story.traits.HasTrait(x.def)))
                {
                    foreach (ExtendedTraitEntry item in Props.traitsToAdd)
                    {
                        if (!Pawn.story.traits.HasTrait(item.def))
                        {
                            Rand.PushState();
                            if (Rand.Chance(item.Chance))
                            {
                                bool replace     = false;
                                int  countTraits = Pawn.story.traits.allTraits.Count;
                                if (countTraits >= maxTraits)
                                {
                                    replace = true;
                                }
                                //   Log.Message(string.Format("i have {0} of a max of {1} traits", countTraits, maxTraits));
                                Trait replacedTrait = Pawn.story.traits.allTraits.Where(x => Props.traitsToAdd.Any(y => y.def != x.def)).RandomElement();
                                if (replace)
                                {
                                    Pawn.story.traits.allTraits.Remove(replacedTrait);
                                }
                                Pawn.story.traits.allTraits.Add(new Trait(item.def, item.degree));
                            }
                            Rand.PopState();
                        }
                    }
                }
            }
            RegionListersUpdater.RegisterInRegions(Pawn, map);
            map.mapPawns.UpdateRegistryForPawn(Pawn);

            //decache graphics
            Pawn.Drawer.renderer.graphics.ResolveAllGraphics();
            Find.ColonistBar.drawer.Notify_RecachedEntries();

            //save the pawn
            Pawn.ExposeData();
            if (Pawn.Faction != faction)
            {
                Pawn.SetFaction(faction);
            }
            //    pawn.Position = intv;
        }
Exemple #8
0
        private void TransformPawn(bool changeDef = true, bool keep = false)
        {
            //sets position, faction and map
            IntVec3 intv    = parent.pawn.Position;
            Faction faction = parent.pawn.Faction;
            Map     map     = parent.pawn.Map;

            RegionListersUpdater.DeregisterInRegions(parent.pawn, map);

            //Change Race to Props.raceDef
            var thingDef = Props.raceDef ?? null;

            if (changeDef && thingDef != null && thingDef != parent.pawn.def)
            {
                parent.pawn.def = thingDef;
                long ageB = Pawn.ageTracker.AgeBiologicalTicks;
                long ageC = Pawn.ageTracker.AgeChronologicalTicks;
                Pawn.ageTracker = new Pawn_AgeTracker(Pawn);
                Pawn.ageTracker.AgeBiologicalTicks    = ageB;
                Pawn.ageTracker.AgeChronologicalTicks = ageC;
                AlienRace.ThingDef_AlienRace           alienRace = (AlienRace.ThingDef_AlienRace)thingDef;
                AlienRace.AlienPartGenerator.AlienComp alien     = parent.pawn.TryGetComp <AlienRace.AlienPartGenerator.AlienComp>();

                if (Props.colourSkinTwo || Props.colourSkin)
                {
                    if (Props.colourSkin)
                    {
                        if (alien != null)
                        {
                            alien.skinColor = alienRace.alienRace.generalSettings.alienPartGenerator.SkinColor(parent.pawn);
                        }
                    }
                    if (Props.colourSkinTwo)
                    {
                        if (alien != null)
                        {
                            alien.skinColorSecond = alienRace.alienRace.generalSettings.alienPartGenerator.SkinColor(parent.pawn, false);
                        }
                    }
                    parent.pawn.Notify_ColorChanged();
                }
                if (Props.removeHair)
                {
                    parent.pawn.story.hairDef = PawnHairChooser.RandomHairDefFor(Pawn, noHairFaction);
                }
                else
                {
                    if (Props.colourHairTwo || Props.colourHair)
                    {
                        if (Props.colourHair)
                        {
                            if (alien != null)
                            {
                                Pawn.story.hairColor = alienRace.alienRace.generalSettings.alienPartGenerator.alienhaircolorgen.NewRandomizedColor();;
                            }
                        }
                        if (Props.colourHairTwo)
                        {
                            if (alien != null)
                            {
                                alien.hairColorSecond = alienRace.alienRace.generalSettings.alienPartGenerator.alienhaircolorgen.NewRandomizedColor();
                            }
                        }
                        parent.pawn.Notify_ColorChanged();
                    }
                }

                string head = alienRace.alienRace.graphicPaths.GetCurrentGraphicPath(Pawn.ageTracker.CurLifeStageRace.def).head;
                Traverse.Create(Pawn.story).Field("headGraphicPath").SetValue(alienRace.alienRace.generalSettings.alienPartGenerator.RandomAlienHead(head, Pawn));
            }
            //Remove Disallowed Traits
            int maxTraits;

            if (MoreTraitSlotsUtil.TryGetMaxTraitSlots(out int max))
            {
                maxTraits = max;
            }
            else
            {
                maxTraits = 4;
            }
            if (parent.pawn.story.traits.allTraits.Any(x => Props.traitsToRemove.Any(y => y.def == x.def)))
            {
                foreach (ExtendedTraitEntry item in Props.traitsToRemove)
                {
                    if (parent.pawn.story.traits.HasTrait(item.def))
                    {
                        Rand.PushState();
                        if (Rand.Chance(item.Chance))
                        {
                            parent.pawn.story.traits.allTraits.Remove(parent.pawn.story.traits.allTraits.Find(x => x.def == item.def));
                        }
                        Rand.PopState();
                    }
                }
            }
            if (Props.traitsToAdd.Any(x => !parent.pawn.story.traits.HasTrait(x.def)))
            {
                foreach (ExtendedTraitEntry item in Props.traitsToAdd)
                {
                    if (!parent.pawn.story.traits.HasTrait(item.def))
                    {
                        Rand.PushState();
                        if (Rand.Chance(item.Chance))
                        {
                            bool replace     = false;
                            int  countTraits = parent.pawn.story.traits.allTraits.Count;
                            if (countTraits >= maxTraits)
                            {
                                replace = true;
                            }
                            //   Log.Message(string.Format("i have {0} of a max of {1} traits", countTraits, maxTraits));
                            Trait replacedTrait = parent.pawn.story.traits.allTraits.Where(x => Props.traitsToAdd.Any(y => y.def != x.def)).RandomElement();
                            if (replace)
                            {
                                parent.pawn.story.traits.allTraits.Remove(replacedTrait);
                            }
                            parent.pawn.story.traits.allTraits.Add(new Trait(item.def, item.degree));
                        }
                        Rand.PopState();
                    }
                }
            }
            RegionListersUpdater.RegisterInRegions(parent.pawn, map);
            map.mapPawns.UpdateRegistryForPawn(parent.pawn);
            //Change BodyType to Props.bodyTypeDef
            if (!Pawn.RaceProps.hasGenders)
            {
                Pawn.gender = Gender.None;
            }
            if (Props.bodyTypeDef != null)
            {
                ChangeBodyType(parent.pawn, Props.bodyTypeDef);
            }

            //decache graphics
            parent.pawn.Drawer.renderer.graphics.ResolveAllGraphics();
            Find.ColonistBar.drawer.Notify_RecachedEntries();

            //save the pawn
            parent.pawn.ExposeData();
            if (parent.pawn.Faction != faction)
            {
                parent.pawn.SetFaction(faction);
            }
            //    pawn.Position = intv;
        }
Exemple #9
0
        /// <summary>
        /// safely change the pawns race
        /// </summary>
        /// <param name="pawn"></param>
        /// <param name="race"></param>
        /// <param name="reRollTraits">if race related traits should be reRolled</param>
        public static void ChangePawnRace([NotNull] Pawn pawn, ThingDef race, bool reRollTraits = false)
        {
            if (pawn == null)
            {
                throw new ArgumentNullException(nameof(pawn));
            }
            MorphDef oldMorph = pawn.def.GetMorphOfRace();
            ThingDef oldRace  = pawn.def;

            AspectTracker aTracker = pawn.GetAspectTracker();

            AspectDef oldMorphAspectDef = oldMorph?.group?.aspectDef;

            if (oldMorphAspectDef != null && aTracker != null)
            {
                Aspect aspect = aTracker.GetAspect(oldMorphAspectDef);
                if (aspect != null)
                {
                    aTracker.Remove(aspect);
                }
            }

            //var pos = pawn.Position;
            Faction faction = pawn.Faction;
            Map     map     = pawn.Map;

            if (map != null)
            {
                RegionListersUpdater.DeregisterInRegions(pawn, map);
            }
            var removed = false;

            if (map != null)
            {
                if (map.listerThings.Contains(pawn))
                {
                    map.listerThings.Remove(pawn); //make sure to update the lister things or else dying will break
                    removed = true;
                }
            }

            pawn.def = race;

            if (removed && !map.listerThings.Contains(pawn))
            {
                map.listerThings.Add(pawn);
            }

            if (map != null)
            {
                RegionListersUpdater.RegisterInRegions(pawn, map);
            }

            map?.mapPawns.UpdateRegistryForPawn(pawn);

            //add the group hediff if applicable
            AspectDef aspectDef = race.GetMorphOfRace()?.group?.aspectDef;

            if (aspectDef != null)
            {
                aTracker?.Add(aspectDef);
            }

            if (map != null)
            {
                var comp = map.GetComponent <MorphTracker>();
                comp.NotifyPawnRaceChanged(pawn, oldMorph);
            }

            //no idea what HarmonyPatches.Patch.ChangeBodyType is for, not listed in pasterbin
            pawn.RefreshGraphics();

            if (reRollTraits && race is ThingDef_AlienRace alienDef)
            {
                ReRollRaceTraits(pawn, alienDef);
            }

            //save location
            if (Current.ProgramState == ProgramState.Playing)
            {
                pawn.ExposeData();
            }
            if (pawn.Faction != faction)
            {
                pawn.SetFaction(faction);
            }
            foreach (IRaceChangeEventReceiver raceChangeEventReceiver in pawn.AllComps.OfType <IRaceChangeEventReceiver>())
            {
                raceChangeEventReceiver.OnRaceChange(oldRace);
            }
        }
Exemple #10
0
        private void TransformPawn(PawnKindDef kindDef, bool changeDef = true, bool keep = false)
        {
            //sets position, faction and map
            IntVec3 intv    = parent.pawn.Position;
            Faction faction = parent.pawn.Faction;
            Map     map     = parent.pawn.Map;

            RegionListersUpdater.DeregisterInRegions(parent.pawn, map);

            //Change Race to Props.raceDef
            if (changeDef && kindDef != null && kindDef != parent.pawn.kindDef)
            {
                parent.pawn.def     = kindDef.race;
                parent.pawn.kindDef = kindDef;
                long ageB = Pawn.ageTracker.AgeBiologicalTicks;
                long ageC = Pawn.ageTracker.AgeChronologicalTicks;
                Pawn.ageTracker = new Pawn_AgeTracker(Pawn);
                Pawn.ageTracker.AgeBiologicalTicks    = ageB;
                Pawn.ageTracker.AgeChronologicalTicks = ageC;
            }
            RegionListersUpdater.RegisterInRegions(parent.pawn, map);
            map.mapPawns.UpdateRegistryForPawn(parent.pawn);

            //decache graphics
            parent.pawn.Drawer.renderer.graphics.ResolveAllGraphics();

            // remove non whitelisted hediffs
            if (!Pawn.health.hediffSet.hediffs.NullOrEmpty())
            {
                if (!Props.whitelists.NullOrEmpty())
                {
                    foreach (MetroidWhitelistDef list in Props.whitelists)
                    {
                        if (parent.pawn.health.hediffSet.hediffs.Any(x => !list.whitelist.Contains(x.def) && x != this.parent))
                        {
                            List <Hediff> removeable = parent.pawn.health.hediffSet.hediffs.Where(x => !list.whitelist.Contains(x.def) && x != this.parent).ToList();
                            foreach (Hediff item in removeable)
                            {
                                if (item != this.parent)
                                {
                                    Pawn.health.RemoveHediff(item);
                                }
                            }
                        }
                    }
                }
                else
                {
                    List <Hediff> removeable = parent.pawn.health.hediffSet.hediffs;
                    foreach (Hediff item in removeable)
                    {
                        if (item != this.parent)
                        {
                            Pawn.health.RemoveHediff(item);
                        }
                    }
                }
            }

            //save the pawn
            parent.pawn.ExposeData();
            if (parent.pawn.Faction != faction)
            {
                parent.pawn.SetFaction(faction);
            }
            //spawn Husk if set
            if (Props.huskDef != null)
            {
                GenSpawn.Spawn(ThingMaker.MakeThing(Props.huskDef), parent.pawn.Position, parent.pawn.Map);
            }
        }
        /// <summary>
        /// safely change the pawns race
        /// </summary>
        /// <param name="pawn">The pawn.</param>
        /// <param name="race">The race.</param>
        /// <param name="reRollTraits">if race related traits should be reRolled</param>
        /// <exception cref="ArgumentNullException">pawn</exception>
        public static void ChangePawnRace([NotNull] Pawn pawn, [NotNull] ThingDef race, bool reRollTraits = false)
        {
            if (pawn == null)
            {
                throw new ArgumentNullException(nameof(pawn));
            }
            if (race == null)
            {
                throw new ArgumentNullException(nameof(race));
            }
            MorphDef oldMorph = pawn.def.GetMorphOfRace();
            ThingDef oldRace  = pawn.def;

            AspectTracker aTracker = pawn.GetAspectTracker();

            AspectDef oldMorphAspectDef = oldMorph?.group?.aspectDef;

            if (oldMorphAspectDef != null && aTracker != null)
            {
                Aspect aspect = aTracker.GetAspect(oldMorphAspectDef);
                if (aspect != null)
                {
                    aTracker.Remove(aspect);
                }
            }

            TransformerUtility.ScaleInjuriesToNewRace(pawn, race);

            //var pos = pawn.Position;
            Faction faction = pawn.Faction;
            Map     map     = pawn.Map;

            if (map != null)
            {
                RegionListersUpdater.DeregisterInRegions(pawn, map);
            }
            var removed = false;

            if (map?.listerThings != null)
            {
                if (map.listerThings.Contains(pawn))
                {
                    map.listerThings.Remove(pawn); //make sure to update the lister things or else dying will break
                    removed = true;
                }
            }

            pawn.def = race;

            if (removed && !map.listerThings.Contains(pawn))
            {
                map.listerThings.Add(pawn);
            }

            if (map != null)
            {
                RegionListersUpdater.RegisterInRegions(pawn, map);
            }

            map?.mapPawns.UpdateRegistryForPawn(pawn);

            //add the group hediff if applicable
            AspectDef aspectDef = race.GetMorphOfRace()?.group?.aspectDef;

            if (aspectDef != null)
            {
                aTracker?.Add(aspectDef);
            }

            if (map != null)
            {
                var comp = map.GetComponent <MorphTracker>();
                comp.NotifyPawnRaceChanged(pawn, oldMorph);
            }


            //always revert to human settings first so the race change is consistent
            ValidateReversion(pawn);

            //check if the body def changed and handle any apparel changes
            if (oldRace.race.body != race.race.body)
            {
                ValidateApparelForChangedPawn(pawn, oldRace);
            }


            if (race != ThingDefOf.Human)
            {
                ValidateExplicitRaceChange(pawn, race, oldRace);
            }

            var mTracker = pawn.GetComp <MorphTrackingComp>();

            if (mTracker == null)
            {
                Warning($"changing the race of {pawn.Name} but they have no {nameof(MorphTrackingComp)}!");
            }
            else
            {
                mTracker.needsRaceCompCheck = true;
            }

            //no idea what HarmonyPatches.Patch.ChangeBodyType is for, not listed in pasterbin
            pawn.RefreshGraphics();

            if (reRollTraits && race is ThingDef_AlienRace alienDef)
            {
                ReRollRaceTraits(pawn, alienDef);
            }

            //save location
            if (Current.ProgramState == ProgramState.Playing)
            {
                pawn.ExposeData();
            }
            if (pawn.Faction != faction)
            {
                pawn.SetFaction(faction);
            }
            foreach (IRaceChangeEventReceiver raceChangeEventReceiver in pawn.AllComps.OfType <IRaceChangeEventReceiver>())
            {
                raceChangeEventReceiver.OnRaceChange(oldRace);
            }
        }