protected void ResetDisabledImplantRecipes(CustomPawn pawn)
        {
            disabledImplantRecipes.Clear();
            OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn);

            foreach (RecipeDef recipeDef in healthOptions.ImplantRecipes)
            {
                if (recipeDef.appliedOnFixedBodyParts != null)
                {
                    if (recipeDef.appliedOnFixedBodyParts.Count == 1)
                    {
                        foreach (var uniquePart in healthOptions.FindBodyPartsForDef(recipeDef.appliedOnFixedBodyParts[0]))
                        {
                            if (pawn.HasSameImplant(uniquePart.Record, recipeDef))
                            {
                                disabledImplantRecipes.Add(recipeDef);
                                break;
                            }
                            if (pawn.HasPartBeenReplaced(uniquePart.Record))
                            {
                                disabledImplantRecipes.Add(recipeDef);
                                break;
                            }
                        }
                    }
                }
            }
        }
Exemple #2
0
        protected int ProcessBodyPart(OptionsHealth options, BodyPartRecord record, int index, HashSet <UniqueBodyPart> ancestors)
        {
            int            partIndex        = options.CountOfMatchingBodyParts(record.def);
            FieldInfo      skinCoveredField = typeof(BodyPartDef).GetField("skinCovered", BindingFlags.Instance | BindingFlags.NonPublic);
            bool           skinCoveredValue = (bool)skinCoveredField.GetValue(record.def);
            FieldInfo      solidField       = typeof(BodyPartDef).GetField("solid", BindingFlags.Instance | BindingFlags.NonPublic);
            bool           isSolidValue     = (bool)solidField.GetValue(record.def);
            UniqueBodyPart part             = new UniqueBodyPart()
            {
                Index       = partIndex,
                Record      = record,
                SkinCovered = skinCoveredValue,
                Solid       = isSolidValue,
                Ancestors   = ancestors.ToList()
            };

            options.AddBodyPart(part);
            ancestors.Add(part);
            foreach (var c in record.parts)
            {
                index = ProcessBodyPart(options, c, index + 1, ancestors);
            }
            ancestors.Remove(part);
            return(index);
        }
        protected UniqueBodyPart FindReplacementBodyPart(OptionsHealth healthOptions, string name)
        {
            ReplacementBodyPart replacement = null;

            if (bodyPartReplacements.TryGetValue(name, out replacement))
            {
                return(healthOptions.FindBodyPart(replacement.def, replacement.index));
            }
            return(null);
        }
Exemple #4
0
        protected void InitializeImplantRecipes(OptionsHealth options, ThingDef pawnThingDef)
        {
            // Find all recipes that replace a body part.
            List <RecipeDef> recipes = new List <RecipeDef>();

            recipes.AddRange(DefDatabase <RecipeDef> .AllDefs.Where((RecipeDef def) => {
                if (def.addsHediff != null && def.appliedOnFixedBodyParts != null && def.appliedOnFixedBodyParts.Count > 0 &&
                    (def.recipeUsers.NullOrEmpty() || def.recipeUsers.Contains(pawnThingDef)))
                {
                    return(true);
                }
                else
                {
                    return(false);
                }
            }));

            // Remove duplicates: recipes that apply the same hediff on the same body parts.
            HashSet <int>    recipeHashes   = new HashSet <int>();
            List <RecipeDef> dedupedRecipes = new List <RecipeDef>();

            foreach (var recipe in recipes)
            {
                int hash = recipe.addsHediff.GetHashCode();
                foreach (var part in recipe.appliedOnFixedBodyParts)
                {
                    hash = hash * 31 + part.GetHashCode();
                }
                if (!recipeHashes.Contains(hash))
                {
                    dedupedRecipes.Add(recipe);
                    recipeHashes.Add(hash);
                }
            }
            recipes = new List <RecipeDef>(dedupedRecipes);

            // Iterate the recipes. Populate a list of all of the body parts that apply to a given recipe.
            foreach (var r in recipes)
            {
                // Add all of the body parts for that recipe to the list.
                foreach (var bodyPartDef in r.appliedOnFixedBodyParts)
                {
                    List <UniqueBodyPart> validBodyParts = options.FindBodyPartsForDef(bodyPartDef);
                    if (validBodyParts != null && validBodyParts.Count > 0)
                    {
                        options.AddImplantRecipe(r, validBodyParts);
                        foreach (var part in validBodyParts)
                        {
                            part.Replaceable = true;
                        }
                    }
                }
            }
        }
Exemple #5
0
        public OptionsHealth GetOptions(CustomPawn pawn)
        {
            OptionsHealth result = null;

            if (!optionsLookup.TryGetValue(pawn.Pawn.def, out result))
            {
                result = InitializeHealthOptions(pawn.Pawn.def);
                optionsLookup.Add(pawn.Pawn.def, result);
            }
            return(result);
        }
        protected void ResetDisabledBodyParts(IEnumerable <BodyPartRecord> parts, CustomPawn pawn)
        {
            disabledBodyParts.Clear();
            OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn);

            foreach (var part in parts)
            {
                UniqueBodyPart uniquePart = healthOptions.FindBodyPartsForRecord(part);
                if (pawn.HasPartBeenReplaced(part) || pawn.HasAtLeastOnePartBeenReplaced(uniquePart.Ancestors.Select((UniqueBodyPart p) => { return(p.Record); })))
                {
                    disabledBodyParts.Add(part);
                }
            }
        }
Exemple #7
0
        protected void InitializeHediffGiverInjuries(OptionsHealth options, HediffGiver giver)
        {
            if (giver == null)
            {
                Logger.Warning("Could not add injury/health condition because a HediffGiver was null");
                return;
            }
            if (giver.hediff == null)
            {
                Logger.Warning("Could not add injury/health condition because the hediff for " + giver.GetType().FullName + " was null");
                return;
            }
            InjuryOption option = new InjuryOption();

            option.HediffDef = giver.hediff;
            option.Label     = giver.hediff.LabelCap;
            option.Giver     = giver;
            if (giver.partsToAffect == null)
            {
                option.WholeBody = true;
            }
            if (giver.canAffectAnyLivePart)
            {
                option.WholeBody = false;
            }
            if (giver.partsToAffect != null && !giver.canAffectAnyLivePart)
            {
                List <BodyPartDef> validParts = new List <BodyPartDef>();
                foreach (var def in giver.partsToAffect)
                {
                    List <UniqueBodyPart> parts = options.FindBodyPartsForDef(def);
                    if (parts != null)
                    {
                        validParts.Add(def);
                    }
                }
                if (validParts.Count == 0)
                {
                    return;
                }
                else
                {
                    option.ValidParts = validParts;
                }
            }
            options.AddInjury(option);
        }
Exemple #8
0
        protected OptionsHealth InitializeHealthOptions(ThingDef pawnThingDef)
        {
            OptionsHealth result  = new OptionsHealth();
            BodyDef       bodyDef = pawnThingDef.race.body;

            result.BodyDef = bodyDef;

            HashSet <UniqueBodyPart> ancestors = new HashSet <UniqueBodyPart>();

            ProcessBodyPart(result, bodyDef.corePart, 1, ancestors);

            InitializeImplantRecipes(result, pawnThingDef);
            InitializeInjuryOptions(result, pawnThingDef);

            result.Sort();
            return(result);
        }
Exemple #9
0
        protected void InitializeHediffGiverInjuries(OptionsHealth options, HediffGiver giver)
        {
            InjuryOption option = new InjuryOption();

            option.HediffDef = giver.hediff;
            option.Label     = giver.hediff.LabelCap;
            option.Giver     = giver;
            if (giver.partsToAffect == null)
            {
                option.WholeBody = true;
            }
            if (giver.canAffectAnyLivePart)
            {
                option.WholeBody = false;
            }
            if (giver.partsToAffect != null && !giver.canAffectAnyLivePart)
            {
                List <BodyPartDef> validParts = new List <BodyPartDef>();
                foreach (var def in giver.partsToAffect)
                {
                    List <UniqueBodyPart> parts = options.FindBodyPartsForDef(def);
                    if (parts != null)
                    {
                        validParts.Add(def);
                    }
                }
                if (validParts.Count == 0)
                {
                    return;
                }
                else
                {
                    option.ValidParts = validParts;
                }
            }
            options.AddInjury(option);
        }
        protected void InitializeRecipes()
        {
            OptionsHealth health = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn);

            this.recipes.Clear();
            List <DialogManageImplants.ImplantRecipe> result = new List <DialogManageImplants.ImplantRecipe>();

            foreach (var recipe in health.ImplantRecipes)
            {
                DialogManageImplants.ImplantRecipe implant = new DialogManageImplants.ImplantRecipe();
                implant.Recipe   = recipe;
                implant.Selected = implantList.FirstOrDefault((Implant i) => { return(i.recipe == recipe); }) != null;
                implant.Disabled = false;
                implant.Parts    = new List <DialogManageImplants.ImplantBodyPart>();
                foreach (var part in health.FindBodyPartsForImplantRecipe(recipe))
                {
                    DialogManageImplants.ImplantBodyPart implantPart = new DialogManageImplants.ImplantBodyPart();
                    implantPart.UniquePart = part;
                    Implant foundImplant = implantList.FirstOrDefault((Implant i) => { return(i.recipe == recipe && i.BodyPartRecord == part.Record); });
                    if (foundImplant != null)
                    {
                        implantPart.Selected = true;
                        implantPart.Implant  = foundImplant;
                    }
                    else
                    {
                        implantPart.Selected = false;
                        implantPart.Implant  = null;
                    }
                    implantPart.Disabled = false;
                    implant.Parts.Add(implantPart);
                }
                result.Add(implant);
            }
            this.recipes = result;
        }
Exemple #11
0
        protected void InitializeInjuryOptions(OptionsHealth options, ThingDef pawnThingDef)
        {
            HashSet <HediffDef> addedDefs = new HashSet <HediffDef>();

            // Go through all of the hediff giver sets for the pawn's race and intialize injuries from
            // each giver.
            if (pawnThingDef.race.hediffGiverSets != null)
            {
                foreach (var giverSetDef in pawnThingDef.race.hediffGiverSets)
                {
                    foreach (var giver in giverSetDef.hediffGivers)
                    {
                        InitializeHediffGiverInjuries(options, giver);
                    }
                }
            }
            // Go through all hediff stages, looking for hediff givers.
            foreach (var hd in DefDatabase <HediffDef> .AllDefs)
            {
                if (hd.stages != null)
                {
                    foreach (var stage in hd.stages)
                    {
                        if (stage.hediffGivers != null)
                        {
                            foreach (var giver in stage.hediffGivers)
                            {
                                InitializeHediffGiverInjuries(options, giver);
                            }
                        }
                    }
                }
            }
            // Go through all of the chemical defs, looking for hediff givers.
            foreach (var chemicalDef in DefDatabase <ChemicalDef> .AllDefs)
            {
                if (chemicalDef.onGeneratedAddictedEvents != null)
                {
                    foreach (var giver in chemicalDef.onGeneratedAddictedEvents)
                    {
                        InitializeHediffGiverInjuries(options, giver);
                    }
                }
            }

            // Get all of the hediffs that can be added via the "forced hediff" scenario part and
            // add them to a hash set so that we can quickly look them up.
            ScenPart_ForcedHediff   scenPart       = new ScenPart_ForcedHediff();
            IEnumerable <HediffDef> scenPartDefs   = Reflection.ScenPart_ForcedHediff.PossibleHediffs(scenPart);
            HashSet <HediffDef>     scenPartDefSet = new HashSet <HediffDef>(scenPartDefs);

            // Add injury options.
            foreach (var hd in DefDatabase <HediffDef> .AllDefs)
            {
                // TODO: Missing body part seems to be a special case.  The hediff giver doesn't itself remove
                // limbs, so disable it until we can add special-case handling.
                if (hd.defName == "MissingBodyPart")
                {
                    continue;
                }
                // Filter out defs that were already added via the hediff giver sets.
                if (addedDefs.Contains(hd))
                {
                    continue;
                }
                // Filter out implants.
                if (hd.hediffClass == typeof(Hediff_AddedPart))
                {
                    continue;
                }

                // If it's an old injury, use the old injury properties to get the label.
                HediffCompProperties p = hd.CompPropsFor(typeof(HediffComp_GetsPermanent));
                HediffCompProperties_GetsPermanent getsPermanentProperties = p as HediffCompProperties_GetsPermanent;
                String label;
                if (getsPermanentProperties != null)
                {
                    if (getsPermanentProperties.permanentLabel != null)
                    {
                        label = getsPermanentProperties.permanentLabel.CapitalizeFirst();
                    }
                    else
                    {
                        Log.Warning("Prepare Carefully could not find label for old injury: " + hd.defName);
                        continue;
                    }
                }
                // If it's not an old injury, make sure it's one of the available hediffs that can
                // be added via ScenPart_ForcedHediff.  If it's not, filter it out.
                else
                {
                    if (!scenPartDefSet.Contains(hd))
                    {
                        continue;
                    }
                    label = hd.LabelCap;
                }

                // Add the injury option..
                InjuryOption option = new InjuryOption();
                option.HediffDef = hd;
                option.Label     = label;
                if (getsPermanentProperties != null)
                {
                    option.IsOldInjury = true;
                }
                else
                {
                    option.ValidParts = new List <BodyPartDef>();
                }
                options.AddInjury(option);
            }

            // Disambiguate duplicate injury labels.
            HashSet <string> labels          = new HashSet <string>();
            HashSet <string> duplicateLabels = new HashSet <string>();

            foreach (var option in options.InjuryOptions)
            {
                if (labels.Contains(option.Label))
                {
                    duplicateLabels.Add(option.Label);
                }
                else
                {
                    labels.Add(option.Label);
                }
            }
            foreach (var option in options.InjuryOptions)
            {
                HediffCompProperties p = option.HediffDef.CompPropsFor(typeof(HediffComp_GetsPermanent));
                HediffCompProperties_GetsPermanent props = p as HediffCompProperties_GetsPermanent;
                if (props != null)
                {
                    if (duplicateLabels.Contains(option.Label))
                    {
                        string label = "EdB.PC.Dialog.Injury.OldInjury.Label".Translate(props.permanentLabel.CapitalizeFirst(), option.HediffDef.LabelCap);
                        option.Label = label;
                    }
                }
            }
        }
Exemple #12
0
        public SaveRecordPawnV3(CustomPawn pawn)
        {
            this.id          = pawn.Id;
            this.thingDef    = pawn.Pawn.def.defName;
            this.pawnKindDef = pawn.Pawn.kindDef.defName;
            this.gender      = pawn.Gender;
            if (pawn.Adulthood != null)
            {
                this.adulthood = pawn.Adulthood.identifier;
            }
            else
            {
                this.adulthood = pawn.LastSelectedAdulthoodBackstory.identifier;
            }
            this.childhood        = pawn.Childhood.identifier;
            this.skinColor        = pawn.Pawn.story.SkinColor;
            this.melanin          = pawn.Pawn.story.melanin;
            this.hairDef          = pawn.HairDef.defName;
            this.hairColor        = pawn.GetColor(PawnLayers.Hair);
            this.headGraphicPath  = pawn.HeadGraphicPath;
            this.bodyType         = Enum.GetName(typeof(BodyType), pawn.BodyType);
            this.firstName        = pawn.FirstName;
            this.nickName         = pawn.NickName;
            this.lastName         = pawn.LastName;
            this.age              = 0;
            this.biologicalAge    = pawn.BiologicalAge;
            this.chronologicalAge = pawn.ChronologicalAge;
            foreach (var trait in pawn.Traits)
            {
                if (trait != null)
                {
                    this.traitNames.Add(trait.def.defName);
                    this.traitDegrees.Add(trait.Degree);
                }
            }
            foreach (var skill in pawn.Pawn.skills.skills)
            {
                this.skillNames.Add(skill.def.defName);
                this.skillValues.Add(pawn.GetUnmodifiedSkillLevel(skill.def));
                this.passions.Add(pawn.currentPassions[skill.def]);
                this.originalPassions.Add(pawn.originalPassions[skill.def]);
            }
            for (int layer = 0; layer < PawnLayers.Count; layer++)
            {
                ThingDef apparelThingDef = pawn.GetAcceptedApparel(layer);
                ThingDef apparelStuffDef = pawn.GetSelectedStuff(layer);
                Color    color           = pawn.GetColor(layer);
                if (apparelThingDef != null)
                {
                    this.apparelLayers.Add(layer);
                    this.apparel.Add(apparelThingDef.defName);
                    this.apparelStuff.Add(apparelStuffDef != null ? apparelStuffDef.defName : "");
                    this.apparelColors.Add(color);
                }
            }
            OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn);

            foreach (Implant implant in pawn.Implants)
            {
                var saveRecord = new SaveRecordImplantV3(implant);
                if (implant.BodyPartRecord != null)
                {
                    UniqueBodyPart part = healthOptions.FindBodyPartsForRecord(implant.BodyPartRecord);
                    if (part != null && part.Index > 0)
                    {
                        saveRecord.bodyPartIndex = part.Index;
                    }
                }
                this.implants.Add(saveRecord);
            }
            foreach (Injury injury in pawn.Injuries)
            {
                var saveRecord = new SaveRecordInjuryV3(injury);
                if (injury.BodyPartRecord != null)
                {
                    UniqueBodyPart part = healthOptions.FindBodyPartsForRecord(injury.BodyPartRecord);
                    if (part != null && part.Index > 0)
                    {
                        saveRecord.bodyPartIndex = part.Index;
                    }
                }
                this.injuries.Add(saveRecord);
            }
        }
        public void CalculatePawnCost(ColonistCostDetails cost, CustomPawn pawn)
        {
            cost.Clear();
            cost.name = pawn.NickName;

            // Start with the market value plus a bit of a mark-up.
            cost.marketValue  = pawn.Pawn.MarketValue;
            cost.marketValue += 300;

            // Calculate passion cost.  Each passion above 8 makes all passions
            // cost more.  Minor passion counts as one passion.  Major passion
            // counts as 3.
            double skillCount           = pawn.currentPassions.Keys.Count();
            double passionLevelCount    = 0;
            double passionLevelCost     = 20;
            double passionateSkillCount = 0;

            foreach (SkillDef def in pawn.currentPassions.Keys)
            {
                Passion passion = pawn.currentPassions[def];
                int     level   = pawn.GetSkillLevel(def);

                if (passion == Passion.Major)
                {
                    passionLevelCount    += 3.0;
                    passionateSkillCount += 1.0;
                }
                else if (passion == Passion.Minor)
                {
                    passionLevelCount    += 1.0;
                    passionateSkillCount += 1.0;
                }
            }
            double levelCost = passionLevelCost;

            if (passionLevelCount > 8)
            {
                double penalty = passionLevelCount - 8;
                levelCost += penalty * 0.4;
            }
            cost.marketValue += levelCost * passionLevelCount;

            // Calculate trait cost.
            if (pawn.TraitCount > Constraints.MaxVanillaTraits)
            {
                int    extraTraitCount = pawn.TraitCount - Constraints.MaxVanillaTraits;
                double extraTraitCost  = 100;
                for (int i = 0; i < extraTraitCount; i++)
                {
                    cost.marketValue += extraTraitCost;
                    extraTraitCost    = Math.Ceiling(extraTraitCost * 2.5);
                }
            }

            // Calculate cost of worn apparel.
            for (int layer = 0; layer < PawnLayers.Count; layer++)
            {
                if (PawnLayers.IsApparelLayer(layer))
                {
                    var def = pawn.GetAcceptedApparel(layer);
                    if (def == null)
                    {
                        continue;
                    }
                    EquipmentKey key = new EquipmentKey();
                    key.ThingDef = def;
                    key.StuffDef = pawn.GetSelectedStuff(layer);
                    EquipmentRecord record = PrepareCarefully.Instance.EquipmentDatabase.Find(key);
                    if (record == null)
                    {
                        continue;
                    }
                    EquipmentSelection selection = new EquipmentSelection(record, 1);
                    double             c         = CalculateEquipmentCost(selection);
                    if (def != null)
                    {
                        // TODO: Discounted materials should be based on the faction, not hard-coded.
                        // TODO: Should we continue with the discounting?
                        if (key.StuffDef != null)
                        {
                            if (key.StuffDef.defName == "Synthread")
                            {
                                if (freeApparel.Contains(key.ThingDef.defName))
                                {
                                    c = 0;
                                }
                                else if (cheapApparel.Contains(key.ThingDef.defName))
                                {
                                    c = c * 0.15d;
                                }
                            }
                        }
                    }
                    cost.apparel += c;
                }
            }

            // Calculate cost for any materials needed for implants.
            OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn);

            foreach (Implant option in pawn.Implants)
            {
                // Check if there are any ancestor parts that override the selection.
                UniqueBodyPart uniquePart = healthOptions.FindBodyPartsForRecord(option.BodyPartRecord);
                if (uniquePart == null)
                {
                    Log.Warning("Prepare Carefully could not find body part record when computing the cost of an implant: " + option.BodyPartRecord.def.defName);
                    continue;
                }
                if (pawn.AtLeastOneImplantedPart(uniquePart.Ancestors.Select((UniqueBodyPart p) => { return(p.Record); })))
                {
                    continue;
                }

                //  Figure out the cost of the part replacement based on its recipe's ingredients.
                if (option.recipe != null)
                {
                    RecipeDef def = option.recipe;
                    foreach (IngredientCount amount in def.ingredients)
                    {
                        int    count     = 0;
                        double totalCost = 0;
                        bool   skip      = false;
                        foreach (ThingDef ingredientDef in amount.filter.AllowedThingDefs)
                        {
                            if (ingredientDef == ThingDefOf.Medicine)
                            {
                                skip = true;
                                break;
                            }
                            count++;
                            EquipmentRecord entry = PrepareCarefully.Instance.EquipmentDatabase[new EquipmentKey(ingredientDef, null)];
                            if (entry != null)
                            {
                                totalCost += entry.cost * (double)amount.GetBaseCount();
                            }
                        }
                        if (skip || count == 0)
                        {
                            continue;
                        }
                        cost.bionics += (int)(totalCost / (double)count);
                    }
                }
            }

            cost.apparel = Math.Ceiling(cost.apparel);
            cost.bionics = Math.Ceiling(cost.bionics);

            // Use a multiplier to balance pawn cost vs. equipment cost.
            // Disabled for now.
            cost.Multiply(1.0);

            cost.ComputeTotal();
        }
        public CustomPawn LoadPawn(SaveRecordPawnV4 record)
        {
            PawnKindDef pawnKindDef = null;

            if (record.pawnKindDef != null)
            {
                pawnKindDef = DefDatabase <PawnKindDef> .GetNamedSilentFail(record.pawnKindDef);

                if (pawnKindDef == null)
                {
                    Log.Warning("Prepare Carefully could not find the pawn kind definition for the saved character: \"" + record.pawnKindDef + "\"");
                    return(null);
                }
            }

            ThingDef pawnThingDef = ThingDefOf.Human;

            if (record.thingDef != null)
            {
                ThingDef thingDef = DefDatabase <ThingDef> .GetNamedSilentFail(record.thingDef);

                if (thingDef != null)
                {
                    pawnThingDef = thingDef;
                }
            }

            PawnGenerationRequestWrapper generationRequest = new PawnGenerationRequestWrapper()
            {
                FixedBiologicalAge    = record.biologicalAge,
                FixedChronologicalAge = record.chronologicalAge,
                FixedGender           = record.gender
            };

            if (pawnKindDef != null)
            {
                generationRequest.KindDef = pawnKindDef;
            }
            Pawn source = PawnGenerator.GeneratePawn(generationRequest.Request);

            if (source.health != null)
            {
                source.health.Reset();
            }

            CustomPawn pawn = new CustomPawn(source);

            if (record.id == null)
            {
                pawn.GenerateId();
            }
            else
            {
                pawn.Id = record.id;
            }

            if (record.type != null)
            {
                try {
                    pawn.Type = (CustomPawnType)Enum.Parse(typeof(CustomPawnType), record.type);
                }
                catch (Exception) {
                    pawn.Type = CustomPawnType.Colonist;
                }
            }
            else
            {
                pawn.Type = CustomPawnType.Colonist;
            }

            pawn.Gender = record.gender;
            if (record.age > 0)
            {
                pawn.ChronologicalAge = record.age;
                pawn.BiologicalAge    = record.age;
            }
            if (record.chronologicalAge > 0)
            {
                pawn.ChronologicalAge = record.chronologicalAge;
            }
            if (record.biologicalAge > 0)
            {
                pawn.BiologicalAge = record.biologicalAge;
            }

            pawn.FirstName = record.firstName;
            pawn.NickName  = record.nickName;
            pawn.LastName  = record.lastName;

            if (record.originalFactionDef != null)
            {
                pawn.OriginalFactionDef = DefDatabase <FactionDef> .GetNamedSilentFail(record.originalFactionDef);
            }
            pawn.OriginalKindDef = pawnKindDef;

            if (pawn.Type == CustomPawnType.World)
            {
                if (record.faction != null)
                {
                    if (record.faction.def != null)
                    {
                        FactionDef factionDef = DefDatabase <FactionDef> .GetNamedSilentFail(record.faction.def);

                        if (factionDef != null)
                        {
                            bool randomFaction = false;
                            if (record.faction.index != null)
                            {
                                CustomFaction customFaction = null;
                                if (!record.faction.leader)
                                {
                                    customFaction = PrepareCarefully.Instance.Providers.Factions.FindCustomFactionByIndex(factionDef, record.faction.index.Value);
                                }
                                else
                                {
                                    customFaction = PrepareCarefully.Instance.Providers.Factions.FindCustomFactionWithLeaderOptionByIndex(factionDef, record.faction.index.Value);
                                }
                                if (customFaction != null)
                                {
                                    pawn.Faction = customFaction;
                                }
                                else
                                {
                                    Log.Warning("Prepare Carefully could not place at least one preset character into a saved faction because there were not enough available factions of that type in the world");
                                    randomFaction = true;
                                }
                            }
                            else
                            {
                                randomFaction = true;
                            }
                            if (randomFaction)
                            {
                                CustomFaction customFaction = PrepareCarefully.Instance.Providers.Factions.FindRandomCustomFactionByDef(factionDef);
                                if (customFaction != null)
                                {
                                    pawn.Faction = customFaction;
                                }
                            }
                        }
                        else
                        {
                            Log.Warning("Prepare Carefully could not place at least one preset character into a saved faction because that faction is not available in the world");
                        }
                    }
                }
            }

            HairDef h = FindHairDef(record.hairDef);

            if (h != null)
            {
                pawn.HairDef = h;
            }
            else
            {
                Log.Warning("Could not load hair definition \"" + record.hairDef + "\"");
                Failed = true;
            }

            pawn.HeadGraphicPath = record.headGraphicPath;
            if (pawn.Pawn.story != null)
            {
                pawn.Pawn.story.hairColor = record.hairColor;
            }

            if (record.melanin >= 0.0f)
            {
                pawn.MelaninLevel = record.melanin;
            }
            else
            {
                pawn.MelaninLevel = PawnColorUtils.FindMelaninValueFromColor(record.skinColor);
            }
            // Set the skin color for Alien Races and alien comp values.
            if (pawn.AlienRace != null)
            {
                pawn.SkinColor = record.skinColor;
                if (record.alien != null)
                {
                    ThingComp alienComp = ProviderAlienRaces.FindAlienCompForPawn(pawn.Pawn);
                    if (alienComp != null)
                    {
                        ProviderAlienRaces.SetCrownTypeOnComp(alienComp, record.alien.crownType);
                        ProviderAlienRaces.SetSkinColorOnComp(alienComp, record.alien.skinColor);
                        ProviderAlienRaces.SetSkinColorSecondOnComp(alienComp, record.alien.skinColorSecond);
                        ProviderAlienRaces.SetHairColorSecondOnComp(alienComp, record.alien.hairColorSecond);
                    }
                }
            }

            Backstory backstory = FindBackstory(record.childhood);

            if (backstory != null)
            {
                pawn.Childhood = backstory;
            }
            else
            {
                Log.Warning("Could not load childhood backstory definition \"" + record.childhood + "\"");
                Failed = true;
            }
            if (record.adulthood != null)
            {
                backstory = FindBackstory(record.adulthood);
                if (backstory != null)
                {
                    pawn.Adulthood = backstory;
                }
                else
                {
                    Log.Warning("Could not load adulthood backstory definition \"" + record.adulthood + "\"");
                    Failed = true;
                }
            }

            BodyTypeDef bodyType = null;

            try {
                bodyType = DefDatabase <BodyTypeDef> .GetNamedSilentFail(record.bodyType);
            }
            catch (Exception) {
            }
            if (bodyType == null)
            {
                if (pawn.Adulthood != null)
                {
                    bodyType = pawn.Adulthood.BodyTypeFor(pawn.Gender);
                }
                else
                {
                    bodyType = pawn.Childhood.BodyTypeFor(pawn.Gender);
                }
            }
            if (bodyType != null)
            {
                pawn.BodyType = bodyType;
            }

            pawn.ClearTraits();
            for (int i = 0; i < record.traitNames.Count; i++)
            {
                string traitName = record.traitNames[i];
                Trait  trait     = FindTrait(traitName, record.traitDegrees[i]);
                if (trait != null)
                {
                    pawn.AddTrait(trait);
                }
                else
                {
                    Log.Warning("Could not load trait definition \"" + traitName + "\"");
                    Failed = true;
                }
            }

            foreach (var skill in record.skills)
            {
                SkillDef def = FindSkillDef(pawn.Pawn, skill.name);
                if (def == null)
                {
                    Log.Warning("Could not load skill definition \"" + skill.name + "\" from saved preset");
                    Failed = true;
                    continue;
                }
                pawn.currentPassions[def]  = skill.passion;
                pawn.originalPassions[def] = skill.passion;
                pawn.SetOriginalSkillLevel(def, skill.value);
                pawn.SetUnmodifiedSkillLevel(def, skill.value);
            }

            foreach (var layer in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(pawn))
            {
                if (layer.Apparel)
                {
                    pawn.SetSelectedApparel(layer, null);
                    pawn.SetSelectedStuff(layer, null);
                }
            }
            List <PawnLayer> apparelLayers = PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(pawn).FindAll((layer) => { return(layer.Apparel); });

            foreach (var apparelRecord in record.apparel)
            {
                // Find the pawn layer for the saved apparel record.
                PawnLayer layer = apparelLayers.FirstOrDefault((apparelLayer) => { return(apparelLayer.Name == apparelRecord.layer); });
                if (layer == null)
                {
                    Log.Warning("Could not find a matching pawn layer for the saved apparel \"" + apparelRecord.layer + "\"");
                    Failed = true;
                    continue;
                }
                if (apparelRecord.apparel.NullOrEmpty())
                {
                    Log.Warning("Saved apparel entry for layer \"" + apparelRecord.layer + "\" had an empty apparel def");
                    Failed = true;
                    continue;
                }
                // Set the defaults.
                pawn.SetSelectedApparel(layer, null);
                pawn.SetSelectedStuff(layer, null);
                pawn.SetColor(layer, Color.white);

                ThingDef def = DefDatabase <ThingDef> .GetNamedSilentFail(apparelRecord.apparel);

                if (def == null)
                {
                    Log.Warning("Could not load thing definition for apparel \"" + apparelRecord.apparel + "\"");
                    Failed = true;
                    continue;
                }
                ThingDef stuffDef = null;
                if (!string.IsNullOrEmpty(apparelRecord.stuff))
                {
                    stuffDef = DefDatabase <ThingDef> .GetNamedSilentFail(apparelRecord.stuff);

                    if (stuffDef == null)
                    {
                        Log.Warning("Could not load stuff definition \"" + apparelRecord.stuff + "\" for apparel \"" + apparelRecord.apparel + "\"");
                        Failed = true;
                        continue;
                    }
                }
                pawn.SetSelectedApparel(layer, def);
                pawn.SetSelectedStuff(layer, stuffDef);
                pawn.SetColor(layer, apparelRecord.color);
            }

            OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn);

            for (int i = 0; i < record.implants.Count; i++)
            {
                SaveRecordImplantV3 implantRecord  = record.implants[i];
                UniqueBodyPart      uniqueBodyPart = healthOptions.FindBodyPartByName(implantRecord.bodyPart, implantRecord.bodyPartIndex != null ? implantRecord.bodyPartIndex.Value : 0);
                if (uniqueBodyPart == null)
                {
                    uniqueBodyPart = FindReplacementBodyPart(healthOptions, implantRecord.bodyPart);
                }
                if (uniqueBodyPart == null)
                {
                    Log.Warning("Prepare Carefully could not add the implant because it could not find the needed body part \"" + implantRecord.bodyPart + "\""
                                + (implantRecord.bodyPartIndex != null ? " with index " + implantRecord.bodyPartIndex : ""));
                    Failed = true;
                    continue;
                }
                BodyPartRecord bodyPart = uniqueBodyPart.Record;
                if (implantRecord.recipe != null)
                {
                    RecipeDef recipeDef = FindRecipeDef(implantRecord.recipe);
                    if (recipeDef == null)
                    {
                        Log.Warning("Prepare Carefully could not add the implant because it could not find the recipe definition \"" + implantRecord.recipe + "\"");
                        Failed = true;
                        continue;
                    }
                    bool found = false;
                    foreach (var p in recipeDef.appliedOnFixedBodyParts)
                    {
                        if (p.defName.Equals(bodyPart.def.defName))
                        {
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                    {
                        Log.Warning("Prepare carefully could not apply the saved implant recipe \"" + implantRecord.recipe + "\" to the body part \"" + bodyPart.def.defName + "\".  Recipe does not support that part.");
                        Failed = true;
                        continue;
                    }
                    Implant implant = new Implant();
                    implant.BodyPartRecord = bodyPart;
                    implant.recipe         = recipeDef;
                    implant.label          = implant.Label;
                    pawn.AddImplant(implant);
                }
            }

            foreach (var injuryRecord in record.injuries)
            {
                HediffDef def = DefDatabase <HediffDef> .GetNamedSilentFail(injuryRecord.hediffDef);

                if (def == null)
                {
                    Log.Warning("Prepare Carefully could not add the injury because it could not find the hediff definition \"" + injuryRecord.hediffDef + "\"");
                    Failed = true;
                    continue;
                }
                InjuryOption option = healthOptions.FindInjuryOptionByHediffDef(def);
                if (option == null)
                {
                    Log.Warning("Prepare Carefully could not add the injury because it could not find a matching injury option for the saved hediff \"" + injuryRecord.hediffDef + "\"");
                    Failed = true;
                    continue;
                }
                BodyPartRecord bodyPart = null;
                if (injuryRecord.bodyPart != null)
                {
                    UniqueBodyPart uniquePart = healthOptions.FindBodyPartByName(injuryRecord.bodyPart,
                                                                                 injuryRecord.bodyPartIndex != null ? injuryRecord.bodyPartIndex.Value : 0);
                    if (uniquePart == null)
                    {
                        uniquePart = FindReplacementBodyPart(healthOptions, injuryRecord.bodyPart);
                    }
                    if (uniquePart == null)
                    {
                        Log.Warning("Prepare Carefully could not add the injury because it could not find the needed body part \"" + injuryRecord.bodyPart + "\""
                                    + (injuryRecord.bodyPartIndex != null ? " with index " + injuryRecord.bodyPartIndex : ""));
                        Failed = true;
                        continue;
                    }
                    bodyPart = uniquePart.Record;
                }
                Injury injury = new Injury();
                injury.Option         = option;
                injury.BodyPartRecord = bodyPart;
                if (injuryRecord.severity != null)
                {
                    injury.Severity = injuryRecord.Severity;
                }
                if (injuryRecord.painFactor != null)
                {
                    injury.PainFactor = injuryRecord.PainFactor;
                }
                pawn.AddInjury(injury);
            }

            pawn.CopySkillsAndPassionsToPawn();
            pawn.ClearPawnCaches();

            return(pawn);
        }
        public SaveRecordPawnV5(CustomPawn pawn)
        {
            this.id       = pawn.Id;
            this.thingDef = pawn.Pawn.def.defName;
            this.type     = pawn.Type.ToString();
            if (pawn.Type == CustomPawnType.World && pawn.Faction != null)
            {
                this.faction = new SaveRecordFactionV4()
                {
                    def    = pawn.Faction?.Def?.defName,
                    index  = pawn.Faction.Index,
                    leader = pawn.Faction.Leader
                };
            }
            this.pawnKindDef        = pawn.OriginalKindDef?.defName ?? pawn.Pawn.kindDef.defName;
            this.originalFactionDef = pawn.OriginalFactionDef?.defName;
            this.gender             = pawn.Gender;
            this.adulthood          = pawn.Adulthood?.identifier ?? pawn.LastSelectedAdulthoodBackstory?.identifier;
            this.childhood          = pawn.Childhood?.identifier;
            this.skinColor          = pawn.Pawn.story.SkinColor;
            this.melanin            = pawn.Pawn.story.melanin;
            this.hairDef            = pawn.HairDef.defName;
            this.hairColor          = pawn.Pawn.story.hairColor;
            this.headGraphicPath    = pawn.HeadGraphicPath;
            this.bodyType           = pawn.BodyType.defName;
            this.firstName          = pawn.FirstName;
            this.nickName           = pawn.NickName;
            this.lastName           = pawn.LastName;
            this.age              = 0;
            this.biologicalAge    = pawn.BiologicalAge;
            this.chronologicalAge = pawn.ChronologicalAge;
            foreach (var trait in pawn.Traits)
            {
                if (trait != null)
                {
                    this.traits.Add(new SaveRecordTraitV5()
                    {
                        def    = trait.def.defName,
                        degree = trait.Degree
                    });
                }
            }
            foreach (var skill in pawn.Pawn.skills.skills)
            {
                this.skills.Add(new SaveRecordSkillV4()
                {
                    name    = skill.def.defName,
                    value   = pawn.GetUnmodifiedSkillLevel(skill.def),
                    passion = pawn.currentPassions[skill.def]
                });
            }
            foreach (var layer in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(pawn))
            {
                if (layer.Apparel)
                {
                    ThingDef apparelThingDef = pawn.GetAcceptedApparel(layer);
                    Color    color           = pawn.GetColor(layer);
                    if (apparelThingDef != null)
                    {
                        ThingDef apparelStuffDef = pawn.GetSelectedStuff(layer);
                        this.apparel.Add(new SaveRecordApparelV4()
                        {
                            layer   = layer.Name,
                            apparel = apparelThingDef.defName,
                            stuff   = apparelStuffDef?.defName ?? "",
                            color   = color
                        });
                    }
                }
            }
            OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn);

            foreach (Implant implant in pawn.Implants)
            {
                var saveRecord = new SaveRecordImplantV3(implant);
                if (implant.BodyPartRecord != null)
                {
                    UniqueBodyPart part = healthOptions.FindBodyPartsForRecord(implant.BodyPartRecord);
                    if (part != null && part.Index > 0)
                    {
                        saveRecord.bodyPartIndex = part.Index;
                    }
                }
                this.implants.Add(saveRecord);
            }
            foreach (Injury injury in pawn.Injuries)
            {
                var saveRecord = new SaveRecordInjuryV3(injury);
                if (injury.BodyPartRecord != null)
                {
                    UniqueBodyPart part = healthOptions.FindBodyPartsForRecord(injury.BodyPartRecord);
                    if (part != null && part.Index > 0)
                    {
                        saveRecord.bodyPartIndex = part.Index;
                    }
                }
                this.injuries.Add(saveRecord);
            }

            pawnCompsSaver = new PawnCompsSaver(pawn.Pawn, DefaultPawnCompRules.RulesForSaving);
        }
        public void DrawAddButton()
        {
            if (RectButtonAdd.Contains(Event.current.mousePosition))
            {
                GUI.color = Style.ColorButtonHighlight;
            }
            else
            {
                GUI.color = Style.ColorButton;
            }
            GUI.DrawTexture(RectButtonAdd, Textures.TextureButtonAdd);

            // Add button.
            if (Widgets.ButtonInvisible(RectButtonAdd, false))
            {
                CustomPawn customPawn = PrepareCarefully.Instance.State.CurrentPawn;

                Action addEntryAction = () => { };

                OptionsHealth  healthOptions             = PrepareCarefully.Instance.Providers.Health.GetOptions(customPawn);
                string         selectedHediffType        = this.selectedHediffType;
                RecipeDef      selectedRecipe            = null;
                InjuryOption   selectedInjury            = null;
                BodyPartRecord selectedBodyPart          = null;
                bool           bodyPartSelectionRequired = true;
                InjurySeverity selectedSeverity          = null;

                Dialog_Options <InjurySeverity> severityDialog;
                Dialog_Options <BodyPartRecord> bodyPartDialog;
                Dialog_Options <InjuryOption>   injuryOptionDialog;
                //Dialog_Options<RecipeDef> implantRecipeDialog;
                DialogManageImplants    manageImplantsDialog;
                Dialog_Options <string> hediffTypeDialog;

                ResetDisabledInjuryOptions(customPawn);

                Action addInjuryAction = () => {
                    if (bodyPartSelectionRequired)
                    {
                        AddInjuryToPawn(selectedInjury, selectedSeverity, selectedBodyPart);
                    }
                    else
                    {
                        if (selectedInjury.ValidParts != null && selectedInjury.ValidParts.Count > 0)
                        {
                            foreach (var p in selectedInjury.ValidParts)
                            {
                                var part = healthOptions.FindBodyPartsForDef(p).FirstOrDefault();
                                if (part != null)
                                {
                                    AddInjuryToPawn(selectedInjury, selectedSeverity, part.Record);
                                }
                                else
                                {
                                    Log.Warning("Could not find body part record for definition: " + p.defName);
                                }
                            }
                        }
                        else
                        {
                            AddInjuryToPawn(selectedInjury, selectedSeverity, null);
                        }
                    }
                };

                severityDialog = new Dialog_Options <InjurySeverity>(severityOptions)
                {
                    ConfirmButtonLabel = "EdB.PC.Common.Add".Translate(),
                    CancelButtonLabel  = "EdB.PC.Common.Cancel".Translate(),
                    HeaderLabel        = "EdB.PC.Panel.Health.SelectSeverity".Translate(),
                    NameFunc           = (InjurySeverity option) => {
                        if (!string.IsNullOrEmpty(option.Label))
                        {
                            return(option.Label);
                        }
                        else
                        {
                            return(selectedInjury.HediffDef.LabelCap);
                        }
                    },
                    SelectedFunc = (InjurySeverity option) => {
                        return(option == selectedSeverity);
                    },
                    SelectAction = (InjurySeverity option) => {
                        selectedSeverity = option;
                    },
                    ConfirmValidation = () => {
                        if (selectedSeverity == null)
                        {
                            return("EdB.PC.Panel.Health.Error.MustSelectSeverity");
                        }
                        else
                        {
                            return(null);
                        }
                    },
                    CloseAction = () => {
                        addInjuryAction();
                    }
                };

                bodyPartDialog = new Dialog_Options <BodyPartRecord>(null)
                {
                    ConfirmButtonLabel = "EdB.PC.Common.Add".Translate(),
                    CancelButtonLabel  = "EdB.PC.Common.Cancel".Translate(),
                    HeaderLabel        = "EdB.PC.Dialog.BodyPart.Header".Translate(),
                    NameFunc           = (BodyPartRecord option) => {
                        return(option.LabelCap);
                    },
                    SelectedFunc = (BodyPartRecord option) => {
                        return(option == selectedBodyPart);
                    },
                    SelectAction = (BodyPartRecord option) => {
                        selectedBodyPart = option;
                    },
                    EnabledFunc = (BodyPartRecord option) => {
                        return(!disabledBodyParts.Contains(option));
                    },
                    ConfirmValidation = () => {
                        if (selectedBodyPart == null)
                        {
                            return("EdB.PC.Dialog.BodyPart.Error.Required");
                        }
                        else
                        {
                            return(null);
                        }
                    },
                    CloseAction = () => {
                        if (selectedHediffType == HediffTypeInjury)
                        {
                            if (this.severityOptions.Count > 1)
                            {
                                Find.WindowStack.Add(severityDialog);
                            }
                            else
                            {
                                if (severityOptions.Count > 0)
                                {
                                    selectedSeverity = this.severityOptions[0];
                                }
                                addInjuryAction();
                            }
                        }
                        else if (selectedHediffType == HediffTypeImplant)
                        {
                            ImplantAdded(new Implant(selectedBodyPart, selectedRecipe));
                        }
                    }
                };

                injuryOptionDialog = new Dialog_Options <InjuryOption>(healthOptions.InjuryOptions)
                {
                    ConfirmButtonLabel = "EdB.PC.Common.Next".Translate(),
                    CancelButtonLabel  = "EdB.PC.Common.Cancel".Translate(),
                    HeaderLabel        = "EdB.PC.Dialog.Injury.Header".Translate(),
                    NameFunc           = (InjuryOption option) => {
                        return(option.Label);
                    },
                    SelectedFunc = (InjuryOption option) => {
                        return(selectedInjury == option);
                    },
                    SelectAction = (InjuryOption option) => {
                        selectedInjury = option;
                        if (option.ValidParts == null && !option.WholeBody)
                        {
                            bodyPartSelectionRequired = true;
                        }
                        else if (option.ValidParts != null && option.ValidParts.Count > 0)
                        {
                            bodyPartSelectionRequired = true;
                        }
                        else
                        {
                            bodyPartSelectionRequired = false;
                        }
                    },
                    EnabledFunc = (InjuryOption option) => {
                        return(!disabledInjuryOptions.Contains(option));
                    },
                    ConfirmValidation = () => {
                        if (selectedInjury == null)
                        {
                            return("EdB.PC.Dialog.Injury.Error.Required");
                        }
                        else
                        {
                            return(null);
                        }
                    },
                    CloseAction = () => {
                        ResetSeverityOptions(selectedInjury);
                        if (bodyPartSelectionRequired)
                        {
                            bodyPartDialog.Options = healthOptions.BodyPartsForInjury(selectedInjury);
                            int count = bodyPartDialog.Options.Count();
                            if (count > 1)
                            {
                                ResetDisabledBodyParts(bodyPartDialog.Options, customPawn);
                                Find.WindowStack.Add(bodyPartDialog);
                                return;
                            }
                            else if (count == 1)
                            {
                                selectedBodyPart = bodyPartDialog.Options.First();
                            }
                        }

                        if (severityOptions.Count > 1)
                        {
                            Find.WindowStack.Add(severityDialog);
                        }
                        else
                        {
                            if (severityOptions.Count > 0)
                            {
                                selectedSeverity = this.severityOptions[0];
                            }
                            addInjuryAction();
                        }
                    }
                };

                hediffTypeDialog = new Dialog_Options <string>(new string[] { HediffTypeInjury, HediffTypeImplant })
                {
                    ConfirmButtonLabel = "EdB.PC.Common.Next".Translate(),
                    CancelButtonLabel  = "EdB.PC.Common.Cancel".Translate(),
                    NameFunc           = (string type) => {
                        return(("EdB.PC.Panel.Health." + type).Translate());
                    },
                    SelectedFunc = (string type) => {
                        return(selectedHediffType == type);
                    },
                    SelectAction = (string type) => {
                        selectedHediffType = type;
                    },
                    ConfirmValidation = () => {
                        if (selectedHediffType == null)
                        {
                            return("EdB.PC.Panel.Health.Error.MustSelectOption");
                        }
                        else
                        {
                            return(null);
                        }
                    },
                    CloseAction = () => {
                        this.selectedHediffType = selectedHediffType;
                        if (selectedHediffType == HediffTypeInjury)
                        {
                            Find.WindowStack.Add(injuryOptionDialog);
                        }
                        else
                        {
                            ResetDisabledImplantRecipes(customPawn);
                            manageImplantsDialog = new DialogManageImplants(customPawn)
                            {
                                HeaderLabel = "EdB.PC.Dialog.Implant.Header".Translate(),
                                CloseAction = (List <Implant> implants) => {
                                    ApplyImplantsToPawn(customPawn, implants);
                                }
                            };
                            Find.WindowStack.Add(manageImplantsDialog);
                        }
                    }
                };
                Find.WindowStack.Add(hediffTypeDialog);
            }
        }
        public CustomPawn LoadPawn(SaveRecordPawnV3 record)
        {
            PawnKindDef pawnKindDef = null;

            if (record.pawnKindDef != null)
            {
                pawnKindDef = DefDatabase <PawnKindDef> .GetNamedSilentFail(record.pawnKindDef);

                if (pawnKindDef == null)
                {
                    Log.Warning("Prepare Carefully could not find the pawn kind definition for the saved character: \"" + record.pawnKindDef + "\"");
                    return(null);
                }
            }

            ThingDef pawnThingDef = ThingDefOf.Human;

            if (record.thingDef != null)
            {
                ThingDef thingDef = DefDatabase <ThingDef> .GetNamedSilentFail(record.thingDef);

                if (thingDef != null)
                {
                    pawnThingDef = thingDef;
                }
            }

            Pawn source;

            if (pawnKindDef != null)
            {
                source = new Randomizer().GenerateKindOfColonist(pawnKindDef);
            }
            else
            {
                source = new Randomizer().GenerateColonist();
            }
            source.health.Reset();

            CustomPawn pawn = new CustomPawn(source);

            if (pawn.Id == null)
            {
                pawn.GenerateId();
            }
            else
            {
                pawn.Id = record.id;
            }

            pawn.Type = CustomPawnType.Colonist;

            pawn.Gender = record.gender;
            if (record.age > 0)
            {
                pawn.ChronologicalAge = record.age;
                pawn.BiologicalAge    = record.age;
            }
            if (record.chronologicalAge > 0)
            {
                pawn.ChronologicalAge = record.chronologicalAge;
            }
            if (record.biologicalAge > 0)
            {
                pawn.BiologicalAge = record.biologicalAge;
            }

            pawn.FirstName = record.firstName;
            pawn.NickName  = record.nickName;
            pawn.LastName  = record.lastName;

            HairDef h = FindHairDef(record.hairDef);

            if (h != null)
            {
                pawn.HairDef = h;
            }
            else
            {
                Log.Warning("Could not load hair definition \"" + record.hairDef + "\"");
                Failed = true;
            }

            pawn.HeadGraphicPath      = record.headGraphicPath;
            pawn.Pawn.story.hairColor = record.hairColor;

            if (record.melanin >= 0.0f)
            {
                pawn.MelaninLevel = record.melanin;
            }
            else
            {
                pawn.MelaninLevel = PawnColorUtils.FindMelaninValueFromColor(record.skinColor);
            }
            // Set the skin color (only for Alien Races).
            if (pawn.AlienRace != null)
            {
                pawn.SkinColor = record.skinColor;
            }

            Backstory backstory = FindBackstory(record.childhood);

            if (backstory != null)
            {
                pawn.Childhood = backstory;
            }
            else
            {
                Log.Warning("Could not load childhood backstory definition \"" + record.childhood + "\"");
                Failed = true;
            }
            if (record.adulthood != null)
            {
                backstory = FindBackstory(record.adulthood);
                if (backstory != null)
                {
                    pawn.Adulthood = backstory;
                }
                else
                {
                    Log.Warning("Could not load adulthood backstory definition \"" + record.adulthood + "\"");
                    Failed = true;
                }
            }

            // Get the body type from the save record.  If there's no value in the save, then assign the
            // default body type from the pawn's backstories.
            // TODO: 1.0

            /*
             * BodyType? bodyType = null;
             * try {
             *  bodyType = (BodyType)Enum.Parse(typeof(BodyType), record.bodyType);
             * }
             * catch (Exception) {
             * }
             * if (!bodyType.HasValue) {
             *  if (pawn.Adulthood != null) {
             *      bodyType = pawn.Adulthood.BodyTypeFor(pawn.Gender);
             *  }
             *  else {
             *      bodyType = pawn.Childhood.BodyTypeFor(pawn.Gender);
             *  }
             * }
             * if (bodyType.HasValue) {
             *  pawn.BodyType = bodyType.Value;
             * }
             */
            BodyTypeDef bodyType = null;

            try {
                bodyType = DefDatabase <BodyTypeDef> .GetNamedSilentFail(record.bodyType);
            }
            catch (Exception) {
            }
            if (bodyType == null)
            {
                if (pawn.Adulthood != null)
                {
                    bodyType = pawn.Adulthood.BodyTypeFor(pawn.Gender);
                }
                else
                {
                    bodyType = pawn.Childhood.BodyTypeFor(pawn.Gender);
                }
            }
            if (bodyType != null)
            {
                pawn.BodyType = bodyType;
            }

            pawn.ClearTraits();
            for (int i = 0; i < record.traitNames.Count; i++)
            {
                string traitName = record.traitNames[i];
                Trait  trait     = FindTrait(traitName, record.traitDegrees[i]);
                if (trait != null)
                {
                    pawn.AddTrait(trait);
                }
                else
                {
                    Log.Warning("Could not load trait definition \"" + traitName + "\"");
                    Failed = true;
                }
            }

            for (int i = 0; i < record.skillNames.Count; i++)
            {
                string   name = record.skillNames[i];
                SkillDef def  = FindSkillDef(pawn.Pawn, name);
                if (def == null)
                {
                    Log.Warning("Could not load skill definition \"" + name + "\"");
                    Failed = true;
                    continue;
                }
                pawn.currentPassions[def]  = record.passions[i];
                pawn.originalPassions[def] = record.passions[i];
                pawn.SetOriginalSkillLevel(def, record.skillValues[i]);
                pawn.SetUnmodifiedSkillLevel(def, record.skillValues[i]);
            }
            if (record.originalPassions != null && record.originalPassions.Count == record.skillNames.Count)
            {
                for (int i = 0; i < record.skillNames.Count; i++)
                {
                    string   name = record.skillNames[i];
                    SkillDef def  = FindSkillDef(pawn.Pawn, name);
                    if (def == null)
                    {
                        Log.Warning("Could not load skill definition \"" + name + "\"");
                        Failed = true;
                        continue;
                    }
                    //pawn.originalPassions[def] = record.originalPassions[i];
                }
            }

            foreach (var layer in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(pawn))
            {
                if (layer.Apparel)
                {
                    pawn.SetSelectedApparel(layer, null);
                    pawn.SetSelectedStuff(layer, null);
                }
            }
            for (int i = 0; i < record.apparelLayers.Count; i++)
            {
                int       layerIndex = record.apparelLayers[i];
                PawnLayer layer      = PrepareCarefully.Instance.Providers.PawnLayers.FindLayerFromDeprecatedIndex(layerIndex);
                if (layer == null)
                {
                    Log.Warning("Could not find pawn layer from saved pawn layer index: \"" + layerIndex + "\"");
                    Failed = true;
                    continue;
                }
                ThingDef def = DefDatabase <ThingDef> .GetNamedSilentFail(record.apparel[i]);

                if (def == null)
                {
                    Log.Warning("Could not load thing definition for apparel \"" + record.apparel[i] + "\"");
                    Failed = true;
                    continue;
                }
                ThingDef stuffDef = null;
                if (!string.IsNullOrEmpty(record.apparelStuff[i]))
                {
                    stuffDef = DefDatabase <ThingDef> .GetNamedSilentFail(record.apparelStuff[i]);

                    if (stuffDef == null)
                    {
                        Log.Warning("Could not load stuff definition \"" + record.apparelStuff[i] + "\" for apparel \"" + record.apparel[i] + "\"");
                        Failed = true;
                        continue;
                    }
                }
                pawn.SetSelectedApparel(layer, def);
                pawn.SetSelectedStuff(layer, stuffDef);
                pawn.SetColor(layer, record.apparelColors[i]);
            }

            OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn);

            for (int i = 0; i < record.implants.Count; i++)
            {
                SaveRecordImplantV3 implantRecord  = record.implants[i];
                UniqueBodyPart      uniqueBodyPart = healthOptions.FindBodyPartByName(implantRecord.bodyPart, implantRecord.bodyPartIndex != null ? implantRecord.bodyPartIndex.Value : 0);
                if (uniqueBodyPart == null)
                {
                    uniqueBodyPart = FindReplacementBodyPart(healthOptions, implantRecord.bodyPart);
                }
                if (uniqueBodyPart == null)
                {
                    Log.Warning("Prepare Carefully could not add the implant because it could not find the needed body part \"" + implantRecord.bodyPart + "\""
                                + (implantRecord.bodyPartIndex != null ? " with index " + implantRecord.bodyPartIndex : ""));
                    Failed = true;
                    continue;
                }
                BodyPartRecord bodyPart = uniqueBodyPart.Record;
                if (implantRecord.recipe != null)
                {
                    RecipeDef recipeDef = FindRecipeDef(implantRecord.recipe);
                    if (recipeDef == null)
                    {
                        Log.Warning("Prepare Carefully could not add the implant because it could not find the recipe definition \"" + implantRecord.recipe + "\"");
                        Failed = true;
                        continue;
                    }
                    bool found = false;
                    foreach (var p in recipeDef.appliedOnFixedBodyParts)
                    {
                        if (p.defName.Equals(bodyPart.def.defName))
                        {
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                    {
                        Log.Warning("Prepare carefully could not apply the saved implant recipe \"" + implantRecord.recipe + "\" to the body part \"" + bodyPart.def.defName + "\".  Recipe does not support that part.");
                        Failed = true;
                        continue;
                    }
                    Implant implant = new Implant();
                    implant.BodyPartRecord = bodyPart;
                    implant.recipe         = recipeDef;
                    implant.label          = implant.Label;
                    pawn.AddImplant(implant);
                }
            }

            foreach (var injuryRecord in record.injuries)
            {
                HediffDef def = DefDatabase <HediffDef> .GetNamedSilentFail(injuryRecord.hediffDef);

                if (def == null)
                {
                    Log.Warning("Prepare Carefully could not add the injury because it could not find the hediff definition \"" + injuryRecord.hediffDef + "\"");
                    Failed = true;
                    continue;
                }
                InjuryOption option = healthOptions.FindInjuryOptionByHediffDef(def);
                if (option == null)
                {
                    Log.Warning("Prepare Carefully could not add the injury because it could not find a matching injury option for the saved hediff \"" + injuryRecord.hediffDef + "\"");
                    Failed = true;
                    continue;
                }
                BodyPartRecord bodyPart = null;
                if (injuryRecord.bodyPart != null)
                {
                    UniqueBodyPart uniquePart = healthOptions.FindBodyPartByName(injuryRecord.bodyPart,
                                                                                 injuryRecord.bodyPartIndex != null ? injuryRecord.bodyPartIndex.Value : 0);
                    if (uniquePart == null)
                    {
                        uniquePart = FindReplacementBodyPart(healthOptions, injuryRecord.bodyPart);
                    }
                    if (uniquePart == null)
                    {
                        Log.Warning("Prepare Carefully could not add the injury because it could not find the needed body part \"" + injuryRecord.bodyPart + "\""
                                    + (injuryRecord.bodyPartIndex != null ? " with index " + injuryRecord.bodyPartIndex : ""));
                        Failed = true;
                        continue;
                    }
                    bodyPart = uniquePart.Record;
                }
                Injury injury = new Injury();
                injury.Option         = option;
                injury.BodyPartRecord = bodyPart;
                if (injuryRecord.severity != null)
                {
                    injury.Severity = injuryRecord.Severity;
                }
                if (injuryRecord.painFactor != null)
                {
                    injury.PainFactor = injuryRecord.PainFactor;
                }
                pawn.AddInjury(injury);
            }

            pawn.CopySkillsAndPassionsToPawn();
            pawn.ClearPawnCaches();

            return(pawn);
        }
        public SaveRecordPawnV4(CustomPawn pawn)
        {
            this.id       = pawn.Id;
            this.thingDef = pawn.Pawn.def.defName;
            this.type     = pawn.Type.ToString();
            if (pawn.Type == CustomPawnType.World && pawn.Faction != null)
            {
                this.faction        = new SaveRecordFactionV4();
                this.faction.def    = pawn.Faction.Def != null ? pawn.Faction.Def.defName : null;
                this.faction.index  = pawn.Faction.Index;
                this.faction.leader = pawn.Faction.Leader;
            }
            this.pawnKindDef        = pawn.OriginalKindDef != null ? pawn.OriginalKindDef.defName : pawn.Pawn.kindDef.defName;
            this.originalFactionDef = pawn.OriginalFactionDef != null ? pawn.OriginalFactionDef.defName : null;
            this.gender             = pawn.Gender;
            if (pawn.Adulthood != null)
            {
                this.adulthood = pawn.Adulthood.identifier;
            }
            else
            {
                this.adulthood = pawn.LastSelectedAdulthoodBackstory?.identifier;
            }
            this.childhood        = pawn.Childhood.identifier;
            this.skinColor        = pawn.Pawn.story.SkinColor;
            this.melanin          = pawn.Pawn.story.melanin;
            this.hairDef          = pawn.HairDef.defName;
            this.hairColor        = pawn.Pawn.story.hairColor;
            this.headGraphicPath  = pawn.HeadGraphicPath;
            this.bodyType         = pawn.BodyType.defName;
            this.firstName        = pawn.FirstName;
            this.nickName         = pawn.NickName;
            this.lastName         = pawn.LastName;
            this.age              = 0;
            this.biologicalAge    = pawn.BiologicalAge;
            this.chronologicalAge = pawn.ChronologicalAge;
            foreach (var trait in pawn.Traits)
            {
                if (trait != null)
                {
                    this.traitNames.Add(trait.def.defName);
                    this.traitDegrees.Add(trait.Degree);
                }
            }
            foreach (var skill in pawn.Pawn.skills.skills)
            {
                SaveRecordSkillV4 skillRecord = new SaveRecordSkillV4();
                skillRecord.name    = skill.def.defName;
                skillRecord.value   = pawn.GetUnmodifiedSkillLevel(skill.def);
                skillRecord.passion = pawn.currentPassions[skill.def];
                this.skills.Add(skillRecord);
            }
            foreach (var layer in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(pawn))
            {
                if (layer.Apparel)
                {
                    ThingDef apparelThingDef = pawn.GetAcceptedApparel(layer);
                    Color    color           = pawn.GetColor(layer);
                    if (apparelThingDef != null)
                    {
                        ThingDef            apparelStuffDef = pawn.GetSelectedStuff(layer);
                        SaveRecordApparelV4 apparelRecord   = new SaveRecordApparelV4();
                        apparelRecord.layer   = layer.Name;
                        apparelRecord.apparel = apparelThingDef.defName;
                        apparelRecord.stuff   = apparelStuffDef != null ? apparelStuffDef.defName : "";
                        apparelRecord.color   = color;
                        this.apparel.Add(apparelRecord);
                    }
                }
            }
            OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn);

            foreach (Implant implant in pawn.Implants)
            {
                var saveRecord = new SaveRecordImplantV3(implant);
                if (implant.BodyPartRecord != null)
                {
                    UniqueBodyPart part = healthOptions.FindBodyPartsForRecord(implant.BodyPartRecord);
                    if (part != null && part.Index > 0)
                    {
                        saveRecord.bodyPartIndex = part.Index;
                    }
                }
                this.implants.Add(saveRecord);
            }
            foreach (Injury injury in pawn.Injuries)
            {
                var saveRecord = new SaveRecordInjuryV3(injury);
                if (injury.BodyPartRecord != null)
                {
                    UniqueBodyPart part = healthOptions.FindBodyPartsForRecord(injury.BodyPartRecord);
                    if (part != null && part.Index > 0)
                    {
                        saveRecord.bodyPartIndex = part.Index;
                    }
                }
                this.injuries.Add(saveRecord);
            }

            ThingComp alienComp = ProviderAlienRaces.FindAlienCompForPawn(pawn.Pawn);

            if (alienComp != null)
            {
                alien                 = new SaveRecordAlienV4();
                alien.crownType       = ProviderAlienRaces.GetCrownTypeFromComp(alienComp);
                alien.skinColor       = ProviderAlienRaces.GetSkinColorFromComp(alienComp);
                alien.skinColorSecond = ProviderAlienRaces.GetSkinColorSecondFromComp(alienComp);
                alien.hairColorSecond = ProviderAlienRaces.GetHairColorSecondFromComp(alienComp);
            }
        }
        protected void ResetDisabledState()
        {
            OptionsHealth health = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn);

            // Iterate each selected implant in order to determine if it's valid--if it's not
            // trying to replace or install on top of an already-missing part.

            // The first pass looks for duplicate implants that both try replace the same part.
            Dictionary <BodyPartRecord, Implant> firstPassReplacedParts = new Dictionary <BodyPartRecord, Implant>();
            List <Implant> firstPassValidImplants = new List <Implant>();

            foreach (var implant in implantList)
            {
                UniqueBodyPart part = health.FindBodyPartsForRecord(implant.BodyPartRecord);
                if (part == null)
                {
                    continue;
                }
                if (firstPassReplacedParts.ContainsKey(part.Record))
                {
                    continue;
                }
                firstPassValidImplants.Add(implant);
                if (implant.ReplacesPart)
                {
                    firstPassReplacedParts.Add(implant.BodyPartRecord, implant);
                }
            }

            // Second pass removes implants whose ancestor parts have been removed and implants
            // that don't replace parts but whose target part has been removed.
            Dictionary <BodyPartRecord, Implant> secondPassReplacedParts = new Dictionary <BodyPartRecord, Implant>();
            List <Implant> secondPassValidImplants = new List <Implant>();

            foreach (var implant in firstPassValidImplants)
            {
                UniqueBodyPart part = health.FindBodyPartsForRecord(implant.BodyPartRecord);
                if (part == null)
                {
                    continue;
                }
                bool isValid = true;
                if (!implant.ReplacesPart && firstPassReplacedParts.ContainsKey(part.Record))
                {
                    isValid = false;
                }
                else
                {
                    foreach (var ancestor in part.Ancestors)
                    {
                        if (firstPassReplacedParts.ContainsKey(ancestor.Record))
                        {
                            isValid = false;
                            break;
                        }
                    }
                }
                if (!isValid)
                {
                    continue;
                }
                secondPassValidImplants.Add(implant);
                if (implant.ReplacesPart)
                {
                    secondPassReplacedParts.Add(implant.BodyPartRecord, implant);
                }
            }

            // Third pass fills the final collections.
            replacedParts.Clear();
            validImplants.Clear();
            foreach (var implant in secondPassValidImplants)
            {
                if (implant.ReplacesPart)
                {
                    replacedParts.Add(implant.BodyPartRecord, implant);
                }
                validImplants.Add(implant);
            }

            //Log.Warning("Valid implants");
            //foreach (var i in validImplants) {
            //    Log.Message("  " + i.recipe.LabelCap + ", " + i.PartName + (i.ReplacesPart ? ", replaces part" : ""));
            //}

            // Iterate each each body part option for each recipe to determine if that body part is missing,
            // based on the whether or not it or one of its ancestors has been replaced.  Only evaluate each
            // body part once.  The result will be used to determine if recipes and part options should be
            // disabled.
            HashSet <BodyPartRecord>             evaluatedParts = new HashSet <BodyPartRecord>();
            Dictionary <BodyPartRecord, Implant> blockedParts   = new Dictionary <BodyPartRecord, Implant>();

            foreach (var recipe in recipes)
            {
                foreach (var part in recipe.Parts)
                {
                    if (evaluatedParts.Contains(part.Part))
                    {
                        continue;
                    }
                    Implant blockingImplant = null;
                    if (!replacedParts.TryGetValue(part.Part, out blockingImplant))
                    {
                        foreach (var ancestor in part.UniquePart.Ancestors)
                        {
                            if (replacedParts.TryGetValue(ancestor.Record, out blockingImplant))
                            {
                                break;
                            }
                        }
                    }
                    evaluatedParts.Add(part.Part);
                    if (blockingImplant != null)
                    {
                        blockedParts.Add(part.Part, blockingImplant);
                    }
                }
            }

            // Go through each recipe and recipe part, marking the parts as disabled if
            // they are missing and marking the recipes as disabled if all of its parts
            // are disabled.
            foreach (var recipe in recipes)
            {
                recipe.Disabled        = false;
                recipe.BlockingImplant = null;
                int disabledCount = 0;
                int partCount     = recipe.Parts.Count;
                foreach (var part in recipe.Parts)
                {
                    part.Disabled        = false;
                    part.BlockingImplant = null;
                    Implant blockingImplant = null;
                    if (blockedParts.TryGetValue(part.Part, out blockingImplant))
                    {
                        if (!validImplants.Contains(part.Implant))
                        {
                            part.Disabled        = true;
                            part.BlockingImplant = blockingImplant;
                            disabledCount++;
                            if (partCount == 1)
                            {
                                recipe.BlockingImplant = blockingImplant;
                            }
                        }
                    }
                }
                if (disabledCount == recipe.Parts.Count)
                {
                    recipe.Disabled = true;
                }
            }

            // Evaluate each recipe's selected state.
            foreach (var recipe in recipes)
            {
                recipe.PartiallySelected = false;
                if (recipe.Selected)
                {
                    int selectedCount = 0;
                    foreach (var part in recipe.Parts)
                    {
                        if (part.Selected && !part.Disabled)
                        {
                            selectedCount++;
                            break;
                        }
                    }
                    if (selectedCount == 0)
                    {
                        recipe.PartiallySelected = true;
                    }
                }
            }

            ResetCachedBlockedSelectionAlert();
        }
Exemple #20
0
        public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record)
        {
            bool partialFailure = false;

            PawnKindDef pawnKindDef = null;

            if (record.pawnKindDef != null)
            {
                pawnKindDef = DefDatabase <PawnKindDef> .GetNamedSilentFail(record.pawnKindDef);

                if (pawnKindDef == null)
                {
                    Logger.Warning("Pawn kind definition for the saved character (" + record.pawnKindDef + ") not found.  Picking a random player colony pawn kind definition.");
                    pawnKindDef = PrepareCarefully.Instance.Providers.Factions.GetPawnKindsForFactionDef(FactionDefOf.PlayerColony).RandomElement();
                    if (pawnKindDef == null)
                    {
                        return(null);
                    }
                }
            }

            ThingDef pawnThingDef = ThingDefOf.Human;

            if (record.thingDef != null)
            {
                ThingDef thingDef = DefDatabase <ThingDef> .GetNamedSilentFail(record.thingDef);

                if (thingDef != null)
                {
                    pawnThingDef = thingDef;
                }
                else
                {
                    Logger.Warning("Pawn's thing definition {" + record.thingDef + "} was not found.  Defaulting to the thing definition for humans.");
                }
            }
            else
            {
                Logger.Warning("Pawn's thing definition was null.  Defaulting to the thing definition for humans.");
            }

            // Create the pawn generation request.
            PawnGenerationRequestWrapper generationRequest = new PawnGenerationRequestWrapper()
            {
                FixedBiologicalAge    = record.biologicalAge,
                FixedChronologicalAge = record.chronologicalAge,
                FixedGender           = record.gender
            };

            // Add a faction to the generation request, if possible.
            if (record.originalFactionDef != null)
            {
                FactionDef factionDef = DefDatabase <FactionDef> .GetNamedSilentFail(record.originalFactionDef);

                if (factionDef != null)
                {
                    Faction faction = PrepareCarefully.Instance.Providers.Factions.GetFaction(factionDef);
                    if (faction != null)
                    {
                        generationRequest.Faction = faction;
                    }
                    else
                    {
                        Logger.Warning("No faction found for faction definition {" + record.originalFactionDef + "}");
                    }
                }
                else
                {
                    Logger.Warning("No faction defition defition found for {" + record.originalFactionDef + "}");
                }
            }
            // Add a pawn kind definition to the generation request, if possible.
            if (pawnKindDef != null)
            {
                generationRequest.KindDef = pawnKindDef;
            }

            // Create the pawn.
            Pawn source = null;

            try {
                source = PawnGenerator.GeneratePawn(generationRequest.Request);
            }
            catch (Exception e) {
                Logger.Warning("Failed to generate a pawn from preset for pawn {" + (record.nickName) + "}. Will try to create it using fallback settings", e);
                generationRequest = new PawnGenerationRequestWrapper()
                {
                    FixedBiologicalAge    = record.biologicalAge,
                    FixedChronologicalAge = record.chronologicalAge,
                    FixedGender           = record.gender
                };
                try {
                    source = PawnGenerator.GeneratePawn(generationRequest.Request);
                }
                catch (Exception) {
                    Logger.Warning("Failed to generate a pawn using fallback settings from preset for pawn {" + (record.nickName) + "}", e);
                    return(null);
                }
            }

            if (source.health != null)
            {
                source.health.Reset();
            }

            CustomPawn pawn = new CustomPawn(source);

            if (record.id == null)
            {
                pawn.GenerateId();
            }
            else
            {
                pawn.Id = record.id;
            }

            if (record.type != null)
            {
                try {
                    pawn.Type = (CustomPawnType)Enum.Parse(typeof(CustomPawnType), record.type);
                }
                catch (Exception) {
                    pawn.Type = CustomPawnType.Colonist;
                }
            }
            else
            {
                pawn.Type = CustomPawnType.Colonist;
            }

            pawn.Gender = record.gender;
            if (record.age > 0)
            {
                pawn.ChronologicalAge = record.age;
                pawn.BiologicalAge    = record.age;
            }
            if (record.chronologicalAge > 0)
            {
                pawn.ChronologicalAge = record.chronologicalAge;
            }
            if (record.biologicalAge > 0)
            {
                pawn.BiologicalAge = record.biologicalAge;
            }

            pawn.FirstName = record.firstName;
            pawn.NickName  = record.nickName;
            pawn.LastName  = record.lastName;

            if (record.originalFactionDef != null)
            {
                pawn.OriginalFactionDef = DefDatabase <FactionDef> .GetNamedSilentFail(record.originalFactionDef);
            }
            pawn.OriginalKindDef = pawnKindDef;

            if (pawn.Type == CustomPawnType.Colonist)
            {
                Faction playerFaction = Faction.OfPlayerSilentFail;
                if (playerFaction != null)
                {
                    pawn.Pawn.SetFactionDirect(playerFaction);
                }
            }
            else if (pawn.Type == CustomPawnType.World)
            {
                if (record.faction != null)
                {
                    if (record.faction.def != null)
                    {
                        FactionDef factionDef = DefDatabase <FactionDef> .GetNamedSilentFail(record.faction.def);

                        if (factionDef != null)
                        {
                            bool randomFaction = false;
                            if (record.faction.index != null)
                            {
                                CustomFaction customFaction = null;
                                if (!record.faction.leader)
                                {
                                    customFaction = PrepareCarefully.Instance.Providers.Factions.FindCustomFactionByIndex(factionDef, record.faction.index.Value);
                                }
                                else
                                {
                                    customFaction = PrepareCarefully.Instance.Providers.Factions.FindCustomFactionWithLeaderOptionByIndex(factionDef, record.faction.index.Value);
                                }
                                if (customFaction != null)
                                {
                                    pawn.Faction = customFaction;
                                }
                                else
                                {
                                    Logger.Warning("Could not place at least one preset character into a saved faction because there were not enough available factions of that type in the world");
                                    randomFaction = true;
                                }
                            }
                            else
                            {
                                randomFaction = true;
                            }
                            if (randomFaction)
                            {
                                CustomFaction customFaction = PrepareCarefully.Instance.Providers.Factions.FindRandomCustomFactionByDef(factionDef);
                                if (customFaction != null)
                                {
                                    pawn.Faction = customFaction;
                                }
                            }
                        }
                        else
                        {
                            Logger.Warning("Could not place at least one preset character into a saved faction because that faction is not available in the world");
                        }
                    }
                }
            }

            HairDef h = DefDatabase <HairDef> .GetNamedSilentFail(record.hairDef);

            if (h != null)
            {
                pawn.HairDef = h;
            }
            else
            {
                Logger.Warning("Could not load hair definition \"" + record.hairDef + "\"");
                partialFailure = true;
            }

            pawn.HeadGraphicPath = record.headGraphicPath;
            if (pawn.Pawn.story != null)
            {
                pawn.Pawn.story.hairColor = record.hairColor;
            }

            if (record.melanin >= 0.0f)
            {
                pawn.MelaninLevel = record.melanin;
            }
            else
            {
                pawn.MelaninLevel = PawnColorUtils.FindMelaninValueFromColor(record.skinColor);
            }

            Backstory backstory = FindBackstory(record.childhood);

            if (backstory != null)
            {
                pawn.Childhood = backstory;
            }
            else
            {
                Logger.Warning("Could not load childhood backstory definition \"" + record.childhood + "\"");
                partialFailure = true;
            }
            if (record.adulthood != null)
            {
                backstory = FindBackstory(record.adulthood);
                if (backstory != null)
                {
                    pawn.Adulthood = backstory;
                }
                else
                {
                    Logger.Warning("Could not load adulthood backstory definition \"" + record.adulthood + "\"");
                    partialFailure = true;
                }
            }

            BodyTypeDef bodyType = null;

            try {
                bodyType = DefDatabase <BodyTypeDef> .GetNamedSilentFail(record.bodyType);
            }
            catch (Exception) {
            }
            if (bodyType == null)
            {
                if (pawn.Adulthood != null)
                {
                    bodyType = pawn.Adulthood.BodyTypeFor(pawn.Gender);
                }
                else
                {
                    bodyType = pawn.Childhood.BodyTypeFor(pawn.Gender);
                }
            }
            if (bodyType != null)
            {
                pawn.BodyType = bodyType;
            }

            // Load pawn comps
            //Logger.Debug("pre-copy comps xml: " + record.compsXml);
            String compsXml = "<saveable Class=\"" + typeof(PawnCompsLoader).FullName + "\">" + record.compsXml + "</saveable>";
            PawnCompInclusionRules rules = new PawnCompInclusionRules();

            rules.IncludeComps(record.savedComps);
            UtilityCopy.DeserializeExposable <PawnCompsLoader>(compsXml, new object[] { pawn.Pawn, rules });
            Dictionary <string, ThingComp> compLookup = new Dictionary <string, ThingComp>();

            foreach (var c in pawn.Pawn.AllComps)
            {
                if (!compLookup.ContainsKey(c.GetType().FullName))
                {
                    //Logger.Debug("Added comp to comp lookup with key: " + c.GetType().FullName);
                    compLookup.Add(c.GetType().FullName, c);
                }
            }
            HashSet <string> savedComps = record.savedComps != null ? new HashSet <string>(record.savedComps) : new HashSet <string>();

            DefaultPawnCompRules.PostLoadModifiers.Apply(pawn.Pawn, compLookup, savedComps);

            pawn.ClearTraits();
            if (record.traits != null)
            {
                for (int i = 0; i < record.traits.Count; i++)
                {
                    string traitName = record.traits[i].def;
                    Trait  trait     = FindTrait(traitName, record.traits[i].degree);
                    if (trait != null)
                    {
                        pawn.AddTrait(trait);
                    }
                    else
                    {
                        Logger.Warning("Could not load trait definition \"" + traitName + "\"");
                        partialFailure = true;
                    }
                }
            }
            else if (record.traitNames != null && record.traitDegrees != null && record.traitNames.Count == record.traitDegrees.Count)
            {
                for (int i = 0; i < record.traitNames.Count; i++)
                {
                    string traitName = record.traitNames[i];
                    Trait  trait     = FindTrait(traitName, record.traitDegrees[i]);
                    if (trait != null)
                    {
                        pawn.AddTrait(trait);
                    }
                    else
                    {
                        Logger.Warning("Could not load trait definition \"" + traitName + "\"");
                        partialFailure = true;
                    }
                }
            }

            foreach (var skill in record.skills)
            {
                SkillDef def = FindSkillDef(pawn.Pawn, skill.name);
                if (def == null)
                {
                    Logger.Warning("Could not load skill definition \"" + skill.name + "\" from saved preset");
                    partialFailure = true;
                    continue;
                }
                pawn.currentPassions[def]  = skill.passion;
                pawn.originalPassions[def] = skill.passion;
                pawn.SetOriginalSkillLevel(def, skill.value);
                pawn.SetUnmodifiedSkillLevel(def, skill.value);
            }

            foreach (var layer in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(pawn))
            {
                if (layer.Apparel)
                {
                    pawn.SetSelectedApparel(layer, null);
                    pawn.SetSelectedStuff(layer, null);
                }
            }
            List <PawnLayer> apparelLayers = PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(pawn).FindAll((layer) => { return(layer.Apparel); });

            foreach (var apparelRecord in record.apparel)
            {
                // Find the pawn layer for the saved apparel record.
                PawnLayer layer = apparelLayers.FirstOrDefault((apparelLayer) => { return(apparelLayer.Name == apparelRecord.layer); });
                if (layer == null)
                {
                    Logger.Warning("Could not find a matching pawn layer for the saved apparel \"" + apparelRecord.layer + "\"");
                    partialFailure = true;
                    continue;
                }
                if (apparelRecord.apparel.NullOrEmpty())
                {
                    Logger.Warning("Saved apparel entry for layer \"" + apparelRecord.layer + "\" had an empty apparel def");
                    partialFailure = true;
                    continue;
                }
                // Set the defaults.
                pawn.SetSelectedApparel(layer, null);
                pawn.SetSelectedStuff(layer, null);
                pawn.SetColor(layer, Color.white);

                ThingDef def = DefDatabase <ThingDef> .GetNamedSilentFail(apparelRecord.apparel);

                if (def == null)
                {
                    Logger.Warning("Could not load thing definition for apparel \"" + apparelRecord.apparel + "\"");
                    partialFailure = true;
                    continue;
                }
                ThingDef stuffDef = null;
                if (!string.IsNullOrEmpty(apparelRecord.stuff))
                {
                    stuffDef = DefDatabase <ThingDef> .GetNamedSilentFail(apparelRecord.stuff);

                    if (stuffDef == null)
                    {
                        Logger.Warning("Could not load stuff definition \"" + apparelRecord.stuff + "\" for apparel \"" + apparelRecord.apparel + "\"");
                        partialFailure = true;
                        continue;
                    }
                }
                pawn.SetSelectedApparel(layer, def);
                pawn.SetSelectedStuff(layer, stuffDef);
                pawn.SetColor(layer, apparelRecord.color);
            }

            OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn);

            for (int i = 0; i < record.implants.Count; i++)
            {
                SaveRecordImplantV3 implantRecord  = record.implants[i];
                UniqueBodyPart      uniqueBodyPart = healthOptions.FindBodyPartByName(implantRecord.bodyPart, implantRecord.bodyPartIndex != null ? implantRecord.bodyPartIndex.Value : 0);
                if (uniqueBodyPart == null)
                {
                    uniqueBodyPart = FindReplacementBodyPart(healthOptions, implantRecord.bodyPart);
                }
                if (uniqueBodyPart == null)
                {
                    Logger.Warning("Could not add the implant because it could not find the needed body part \"" + implantRecord.bodyPart + "\""
                                   + (implantRecord.bodyPartIndex != null ? " with index " + implantRecord.bodyPartIndex : ""));
                    partialFailure = true;
                    continue;
                }
                BodyPartRecord bodyPart = uniqueBodyPart.Record;
                if (implantRecord.recipe != null)
                {
                    RecipeDef recipeDef = FindRecipeDef(implantRecord.recipe);
                    if (recipeDef == null)
                    {
                        Logger.Warning("Could not add the implant because it could not find the recipe definition \"" + implantRecord.recipe + "\"");
                        partialFailure = true;
                        continue;
                    }
                    bool found = false;
                    foreach (var p in recipeDef.appliedOnFixedBodyParts)
                    {
                        if (p.defName.Equals(bodyPart.def.defName))
                        {
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                    {
                        Logger.Warning("Could not apply the saved implant recipe \"" + implantRecord.recipe + "\" to the body part \"" + bodyPart.def.defName + "\".  Recipe does not support that part.");
                        partialFailure = true;
                        continue;
                    }
                    Implant implant = new Implant();
                    implant.BodyPartRecord = bodyPart;
                    implant.recipe         = recipeDef;
                    implant.label          = implant.Label;
                    pawn.AddImplant(implant);
                }
            }

            foreach (var injuryRecord in record.injuries)
            {
                HediffDef def = DefDatabase <HediffDef> .GetNamedSilentFail(injuryRecord.hediffDef);

                if (def == null)
                {
                    Logger.Warning("Could not add the injury because it could not find the hediff definition \"" + injuryRecord.hediffDef + "\"");
                    partialFailure = true;
                    continue;
                }
                InjuryOption option = healthOptions.FindInjuryOptionByHediffDef(def);
                if (option == null)
                {
                    Logger.Warning("Could not add the injury because it could not find a matching injury option for the saved hediff \"" + injuryRecord.hediffDef + "\"");
                    partialFailure = true;
                    continue;
                }
                BodyPartRecord bodyPart = null;
                if (injuryRecord.bodyPart != null)
                {
                    UniqueBodyPart uniquePart = healthOptions.FindBodyPartByName(injuryRecord.bodyPart,
                                                                                 injuryRecord.bodyPartIndex != null ? injuryRecord.bodyPartIndex.Value : 0);
                    if (uniquePart == null)
                    {
                        uniquePart = FindReplacementBodyPart(healthOptions, injuryRecord.bodyPart);
                    }
                    if (uniquePart == null)
                    {
                        Logger.Warning("Could not add the injury because it could not find the needed body part \"" + injuryRecord.bodyPart + "\""
                                       + (injuryRecord.bodyPartIndex != null ? " with index " + injuryRecord.bodyPartIndex : ""));
                        partialFailure = true;
                        continue;
                    }
                    bodyPart = uniquePart.Record;
                }
                Injury injury = new Injury();
                injury.Option         = option;
                injury.BodyPartRecord = bodyPart;
                if (injuryRecord.severity != null)
                {
                    injury.Severity = injuryRecord.Severity;
                }
                if (injuryRecord.painFactor != null)
                {
                    injury.PainFactor = injuryRecord.PainFactor;
                }
                pawn.AddInjury(injury);
            }

            pawn.CopySkillsAndPassionsToPawn();
            pawn.ClearPawnCaches();

            return(pawn);
        }