// We just use the FeedPatient Job from the medical branch for non-breastfeeding public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false) { Pawn pawn2 = (Pawn)t; if (pawn2 != null) { if (ChildrenUtility.CanBreastfeed(pawn)) { //Log.Message("Deciding to breastfeed baby."); return(new Job(DefDatabase <JobDef> .GetNamed("BreastfeedBaby")) { targetA = pawn2, }); } else { Thing t2; ThingDef def1; if (FoodUtility.TryFindBestFoodSourceFor(pawn, pawn2, pawn2.needs.food.CurCategory == HungerCategory.UrgentlyHungry, out t2, out def1, false, true, false, false, false)) { //Log.Message("Deciding to feed normal food to baby."); return(new Job(JobDefOf.FeedPatient) { targetA = t2, targetB = pawn2, count = FoodUtility.WillIngestStackCountOf(pawn2, def1, t2.def.ingestible.CachedNutrition) }); } } } return(null); }
// // Methods // public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false) { Pawn pawn2 = t as Pawn; // Make sure pawn can actually breastfeed before wasting time checking anything else if (!ChildrenUtility.CanBreastfeed(pawn)) { return(false); } if (pawn2 == null || pawn2 == pawn) { return(false); } if (!pawn2.RaceProps.Humanlike) { return(false); } if (pawn2.needs.food == null || pawn2.needs.food.CurLevelPercentage > pawn2.needs.food.PercentageThreshHungry + 0.02) { return(false); } if (!FeedPatientUtility.ShouldBeFed(pawn2)) { return(false); } if (!pawn.CanReserveAndReach(t, PathEndMode.ClosestTouch, Danger.Deadly, 1, -1, null, forced)) { return(false); } return(true); }
internal static void CanGetThought_Patch(ref Pawn pawn, ref ThoughtDef def, ref bool __result) { // Toddlers and younger can't get these thoughts if (pawn.ageTracker.CurLifeStageIndex <= 1 && ChildrenUtility.RaceUsesChildren(pawn)) { List <ThoughtDef> thoughtlist = new List <ThoughtDef> { ThoughtDefOf.AteWithoutTable, ThoughtDefOf.KnowPrisonerDiedInnocent, ThoughtDefOf.KnowPrisonerSold, ThoughtDefOf.Naked, ThoughtDefOf.SleepDisturbed, ThoughtDefOf.SleptOnGround, ThoughtDef.Named("CabinFever"), //ThoughtDef.Named("SharedBedroom") }; foreach (ThoughtDef thought in thoughtlist) { if (def == thought) { __result = false; } } } }
// // Methods // protected override ThoughtState CurrentStateInternal(Pawn p) { if (p.ageTracker.CurLifeStageIndex > AgeStage.Toddler || !ChildrenUtility.RaceUsesChildren(p)) { return(false); } Pawn mother = p.relations.GetFirstDirectRelationPawn(PawnRelationDefOf.Parent, x => x.gender == Gender.Female); Pawn father = p.relations.GetFirstDirectRelationPawn(PawnRelationDefOf.Parent, x => x.gender == Gender.Male); if (ArePawnsNear(p, mother) && ArePawnsNear(p, father)) { return(ThoughtState.ActiveAtStage(2)); } else if (ArePawnsNear(p, mother)) { return(ThoughtState.ActiveAtStage(0)); } else if (ArePawnsNear(p, father)) { return(ThoughtState.ActiveAtStage(1)); } else { return(false); } }
public static Building_Bed FindCribFor(Pawn baby, Pawn traveler) { Building_Bed crib = null; // Is a crib already assigned to the baby? if (baby.ownership != null && baby.ownership.OwnedBed != null && ChildrenUtility.IsBedCrib(baby.ownership.OwnedBed)) { Building_Bed bedThing = baby.ownership.OwnedBed; if (RestUtility.IsValidBedFor(bedThing, baby, traveler, false, false)) { crib = baby.ownership.OwnedBed; } } // If not, let's look for one else { foreach (var thingDef in RestUtility.AllBedDefBestToWorst) { if (RestUtility.CanUseBedEver(baby, thingDef) && thingDef.building.bed_maxBodySize <= 0.6f) { Building_Bed find_crib = (Building_Bed)GenClosest.ClosestThingReachable(baby.Position, baby.Map, ThingRequest.ForDef(thingDef), PathEndMode.OnCell, TraverseParms.For(traveler), 9999f, (Thing b) => (RestUtility.IsValidBedFor(b, baby, traveler, false, false)), null); if (find_crib != null) { crib = find_crib; } } } } return(crib); }
// // Methods // public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false) { Pawn pawn2 = t as Pawn; if (pawn2 == null || pawn2 == pawn) { return(false); } if (!pawn2.RaceProps.Humanlike || pawn2.ageTracker.CurLifeStageIndex > AgeStage.Toddler) { return(false); } if (pawn2.needs.food == null || pawn2.needs.food.CurLevelPercentage > pawn2.needs.food.PercentageThreshHungry + 0.02) { return(false); } if (!pawn2.InBed()) { return(false); } if (!FeedPatientUtility.ShouldBeFed(pawn2)) { return(false); } if (!pawn.CanReserveAndReach(t, PathEndMode.ClosestTouch, Danger.Deadly, 1, -1, null, forced)) { return(false); } if (!ChildrenUtility.CanBreastfeed(pawn)) { JobFailReason.Is("ReasonCannotBreastfeed".Translate(pawn.LabelShort)); return(false); } return(true); }
internal static void SBD(ref Pawn_HealthTracker __instance, ref bool __result) { Pawn pawn = (Pawn)AccessTools.Field(typeof(Pawn_HealthTracker), "pawn").GetValue(__instance); if (ChildrenUtility.RaceUsesChildren(pawn) && pawn.ageTracker.CurLifeStageIndex <= 2) { __result = __instance.hediffSet.PainTotal > 0.4f || !__instance.capacities.CanBeAwake || !__instance.capacities.CapableOf(PawnCapacityDefOf.Moving); } }
internal static void Notify_EquipmentAdded_Patch(ref ThingWithComps eq, ref Pawn_EquipmentTracker __instance) { Pawn pawn = __instance.ParentHolder as Pawn; if (pawn != null && ChildrenUtility.RaceUsesChildren(pawn) && eq.def.BaseMass > ChildrenUtility.ChildMaxWeaponMass(pawn) && pawn.ageTracker.CurLifeStageIndex <= AgeStage.Child && pawn.Faction.IsPlayer) { Messages.Message("MessageWeaponTooLarge".Translate(new object[] { eq.def.label, ((Pawn)__instance.ParentHolder).NameStringShort }), MessageTypeDefOf.CautionInput); } }
internal static void Notify_EquipmentAdded_Patch(ref ThingWithComps eq, ref Pawn_EquipmentTracker __instance) { Pawn pawn = __instance.ParentHolder as Pawn; if (eq.def.BaseMass > ChildrenUtility.ChildMaxWeaponMass(pawn) && pawn.ageTracker.CurLifeStageIndex <= AgeStage.Child) { Messages.Message("MessageWeaponTooLarge".Translate(new object[] { eq.def.label, ((Pawn)__instance.ParentHolder).NameStringShort }), MessageSound.Negative); } }
// // Fields // // // Methods // public override IEnumerable <BodyPartRecord> GetPartsToApplyOn(Pawn pawn, RecipeDef recipe) { BodyPartRecord part = pawn.RaceProps.body.corePart; if (recipe.appliedOnFixedBodyParts[0] != null) { part = pawn.RaceProps.body.AllParts.Find(x => x.def == recipe.appliedOnFixedBodyParts[0]); } if (part != null && ChildrenUtility.RaceUsesChildren(pawn) && pawn.gender == Gender.Female && pawn.ageTracker.CurLifeStageIndex >= AgeStage.Teenager) { yield return(part); } }
public static T FailOnBaby <T>(this T f) where T : IJobEndable { f.AddEndCondition(delegate { // return JobCondition.Incompletable; if (f.GetActor().ageTracker.CurLifeStageIndex <= 1 && ChildrenUtility.RaceUsesChildren(f.GetActor())) { return(JobCondition.Incompletable); } else { return(JobCondition.Ongoing); } }); return(f); }
internal static List <ThingStuffPair> FilterChildWeapons(Pawn pawn, List <ThingStuffPair> weapons) { var weapons_out = new List <ThingStuffPair>(); if (weapons.Count > 0) { foreach (ThingStuffPair weapon in weapons) { if (weapon.thing.BaseMass < ChildrenUtility.ChildMaxWeaponMass(pawn)) { weapons_out.Add(weapon); } } } return(weapons_out); }
// My own methods internal static Graphic GetChildHeadGraphics(PawnGraphicSet graphicSet, Shader shader, Color skinColor) { Graphic_Multi graphic = null; Pawn pawn = graphicSet.pawn; if (ChildrenUtility.IsHumanlikeChild(pawn)) { string str = "Male_Child"; string path = "Things/Pawn/Humanlike/Children/Heads/" + str; graphic = GraphicDatabase.Get <Graphic_Multi>(path, shader, Vector2.one, skinColor) as Graphic_Multi; } else { graphic = graphicSet.headGraphic as Graphic_Multi; } return(graphic); }
// We just use the FeedPatient Job from the medical branch for non-breastfeeding public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false) { Pawn pawn2 = (Pawn)t; if (pawn2 != null) { if (ChildrenUtility.CanBreastfeed(pawn)) { //Log.Message("Deciding to breastfeed baby."); return(new Job(DefDatabase <JobDef> .GetNamed("BreastfeedBaby")) { targetA = pawn2, }); } } return(null); }
internal static void _GeneratePawn(ref PawnGenerationRequest request, ref Pawn __result) { Pawn pawn = __result; if (pawn.ageTracker.CurLifeStageIndex <= AgeStage.Child && ChildrenUtility.RaceUsesChildren(pawn)) { // hostile children on/off if (!BnC_Settings.option_hostile_children_raider && Faction.OfPlayerSilentFail != null) { if (pawn.Faction != null && pawn.HostileTo(Faction.OfPlayer)) { Log.Message("[From BnC] hostile children growup : " + pawn.LabelIndefinite()); ChildrenUtility.GrowupHostileChild(ref request, ref pawn); return; } } // give innocence trait ChildrenUtility.Give_Innocent_trait(ref pawn); // give backstory if (pawn.ageTracker.CurLifeStageIndex == 2 && pawn.ageTracker.AgeBiologicalYears < 8 && ChildrenUtility.IsHumanlikeChild(pawn)) { pawn.story.childhood = BackstoryDatabase.allBackstories["CustomBackstory_NA_Childhood"]; } if (pawn.ageTracker.CurLifeStageIndex == AgeStage.Baby) { // if rjw is on return if (AnotherModCheck.RJW_On) { return; } } // Children hediff being injected pawn.health.AddHediff(HediffDef.Named("BabyState"), null, null); Hediff_Baby babystate = (Hediff_Baby)pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("BabyState")); if (babystate != null) { for (int i = 0; i != pawn.ageTracker.CurLifeStageIndex + 1; i++) { babystate.GrowUpTo(i, true); } } } }
internal static void GiveRandomBirthDefect(Pawn baby) { int r = Rand.Range(1, 9); // Bad back defect if (r == 1) { baby.health.AddHediff(HediffDefOf.BadBack, ChildrenUtility.GetPawnBodyPart(baby, "Spine"), null); } // cataract defect if (r == 2) { int r1 = Rand.Range(1, 3); if (r1 == 1 || r1 == 3) { baby.health.AddHediff(HediffDefOf.Cataract, ChildrenUtility.GetPawnBodyPart(baby, "LeftEye"), null); } if (r1 == 2 || r1 == 3) { baby.health.AddHediff(HediffDefOf.Cataract, ChildrenUtility.GetPawnBodyPart(baby, "RightEye"), null); } } // frail defect if (r == 3) { baby.health.AddHediff(HediffDefOf.Frail, null, null); } if (r == 4) { baby.health.AddHediff(HediffDef.Named("HearingLoss"), ChildrenUtility.GetPawnBodyPart(baby, "Head"), null); } if (r == 5) { baby.health.AddHediff(HediffDef.Named("DefectBlind"), ChildrenUtility.GetPawnBodyPart(baby, "LeftEye"), null); baby.health.AddHediff(HediffDef.Named("DefectBlind"), ChildrenUtility.GetPawnBodyPart(baby, "RightEye"), null); } if (r == 6) { baby.health.AddHediff(HediffDef.Named("DefectHeart"), ChildrenUtility.GetPawnBodyPart(baby, "Heart"), null); } if (r == 7) { baby.health.AddHediff(HediffDef.Named("DefectStillborn"), null, null); } }
protected override IEnumerable <Toil> MakeNewToils() { this.FailOnDespawnedOrNull(TargetIndex.A); this.FailOnSomeonePhysicallyInteracting(TargetIndex.A); this.FailOn(delegate { if (!ChildrenUtility.CanBreastfeed(pawn) || !pawn.CanReserve(TargetA, 1, -1, null, false)) { return(true); } else { return(false); } }); yield return(Toils_Reserve.Reserve(TargetIndex.A, 1, -1, null)); yield return(Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch)); Toil prepare = new Toil(); prepare.initAction = delegate { if (Victim.ageTracker.CurLifeStageIndex > AgeStage.Baby) { PawnUtility.ForceWait(Victim, breastFeedDuration, Victim); } }; prepare.defaultCompleteMode = ToilCompleteMode.Delay; prepare.defaultDuration = breastFeedDuration; yield return(prepare); yield return(new Toil { initAction = delegate { AddEndCondition(() => JobCondition.Succeeded); // Baby is full Victim.needs.food.CurLevelPercentage = 1f; }, defaultCompleteMode = ToilCompleteMode.Instant }); }
internal static void TryCastShot_Patch(ref Verb_Shoot __instance) { Pawn pawn = __instance.CasterPawn; if (pawn != null && ChildrenUtility.RaceUsesChildren(pawn) && pawn.ageTracker.CurLifeStageIndex <= AgeStage.Child) { // The weapon is too heavy and the child will (likely) drop it when trying to fire if (__instance.ownerEquipment.def.BaseMass > ChildrenUtility.ChildMaxWeaponMass(pawn)) { ThingWithComps benis; pawn.equipment.TryDropEquipment(__instance.ownerEquipment, out benis, pawn.Position, false); float recoilForce = (__instance.ownerEquipment.def.BaseMass - 3); if (recoilForce > 0) { string[] hitPart = { "Torso", "LeftShoulder", "LeftArm", "LeftHand", "RightShoulder", "RightArm", "RightHand", "Head", "Neck", "LeftEye", "RightEye", "Nose", }; int hits = Rand.Range(1, 4); while (hits > 0) { pawn.TakeDamage(new DamageInfo(DamageDefOf.Blunt, (int)((recoilForce + Rand.Range(0f, 3f)) / hits), -1, __instance.ownerEquipment, ChildrenUtility.GetPawnBodyPart(pawn, hitPart.RandomElement <String> ()), null)); hits--; } } } } }
internal static Graphic GetChildBodyGraphics(PawnGraphicSet graphicSet, Shader shader, Color skinColor) { Graphic_Multi graphic = null; Pawn pawn = graphicSet.pawn; if (ChildrenUtility.IsHumanlikeChild(pawn)) { string str = "Naked_Boy"; if (graphicSet.pawn.gender == Gender.Female) { str = "Naked_Girl"; } string path = "Things/Pawn/Humanlike/Children/Bodies/" + str; graphic = GraphicDatabase.Get <Graphic_Multi>(path, shader, Vector2.one, skinColor) as Graphic_Multi; } else { graphic = graphicSet.nakedGraphic as Graphic_Multi; } return(graphic); }
// // Methods // protected override ThoughtState CurrentStateInternal(Pawn p) { // Does not affect babies and toddlers if (p.ageTracker.CurLifeStageIndex < 2 || p.health.capacities.GetLevel(PawnCapacityDefOf.Hearing) <= 0.1f) { return(ThoughtState.Inactive); } // Find all crying babies in the vicinity int cryingBabies = 0; foreach (Pawn mapPawn in p.MapHeld.mapPawns.AllPawnsSpawned) { if (ChildrenUtility.RaceUsesChildren(mapPawn) && mapPawn.ageTracker.CurLifeStageIndex == 0 && mapPawn.health.hediffSet.HasHediff(HediffDef.Named("UnhappyBaby")) && mapPawn.PositionHeld.InHorDistOf(p.PositionHeld, 24) && mapPawn.PositionHeld.GetRoomOrAdjacent(mapPawn.MapHeld).ContainedAndAdjacentThings.Contains(p)) { cryingBabies += 1; } } if (cryingBabies > 0) { if (cryingBabies == 1) { return(ThoughtState.ActiveAtStage(0)); } else if (cryingBabies <= 3) { return(ThoughtState.ActiveAtStage(1)); } else if (cryingBabies >= 4) { return(ThoughtState.ActiveAtStage(2)); } } return(ThoughtState.Inactive); }
static void AddDirectRelation_GiveLacrating(Pawn_RelationsTracker __instance, PawnRelationDef def, Pawn otherPawn) { if (def == PawnRelationDefOf.Parent) { Pawn pawn = (Pawn)PawnFI.GetValue(__instance); Pawn mother = pawn.GetMother(); //Log.Message("mother : " + pawn.LabelIndefinite()); if (pawn.ageTracker.CurLifeStageIndex == AgeStage.Baby && mother == otherPawn) { if (mother.RaceProps.Humanlike && !mother.health.hediffSet.HasHediff(HediffDef.Named("Lactating"))) { if (AnotherModCheck.RJW_On) { try { ((Action)(() => { if (Genital_Helper.has_breasts(mother)) { mother.health.AddHediff(HediffDef.Named("Lactating"), ChildrenUtility.GetPawnBodyPart(mother, "Chest"), null); } if (Genital_Helper.has_vagina(mother)) { mother.health.AddHediff(HediffDef.Named("BnC_RJW_PostPregnancy"), ChildrenUtility.GetPawnBodyPart(mother, "Genitals"), null); } }))(); } catch (TypeLoadException) { } } else { mother.health.AddHediff(HediffDef.Named("Lactating"), ChildrenUtility.GetPawnBodyPart(mother, "Torso"), null); } } } } }
// // Methods // public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false) { Pawn pawn2 = t as Pawn; if (pawn2 == null || pawn2 == pawn) { return(false); } if (!pawn2.RaceProps.Humanlike || pawn2.ageTracker.CurLifeStageIndex > AgeStage.Toddler) { return(false); } if (pawn2.needs.food == null || pawn2.needs.food.CurLevelPercentage > pawn2.needs.food.PercentageThreshHungry + 0.02) { return(false); } if (!pawn2.InBed()) { return(false); } if (!FeedPatientUtility.ShouldBeFed(pawn2)) { return(false); } if (!pawn.CanReserveAndReach(t, PathEndMode.ClosestTouch, Danger.Deadly, 1, -1, null, forced)) { return(false); } Thing thing; ThingDef thingDef; if (!FoodUtility.TryFindBestFoodSourceFor(pawn, pawn2, pawn2.needs.food.CurCategory == HungerCategory.UrgentlyHungry, out thing, out thingDef, false, true, false, false, false) && !ChildrenUtility.CanBreastfeed(pawn)) { JobFailReason.Is("NoFood".Translate()); return(false); } return(true); }
internal static void ResolveAgeGraphics(PawnGraphicSet graphics) { LongEventHandler.ExecuteWhenFinished(delegate { //if (!graphics.pawn.RaceProps.Humanlike) { if (!ChildrenUtility.RaceUsesChildren(graphics.pawn)) { return; } // Beards String beard = ""; if (graphics.pawn.story.hairDef != null) { if (graphics.pawn.story.hairDef.hairTags.Contains("Beard")) { if (graphics.pawn.apparel.BodyPartGroupIsCovered(BodyPartGroupDefOf.UpperHead) && !graphics.pawn.story.hairDef.hairTags.Contains("DrawUnderHat")) { beard = "_BeardOnly"; } if (graphics.pawn.ageTracker.CurLifeStageIndex <= AgeStage.Teenager) { graphics.hairGraphic = GraphicDatabase.Get <Graphic_Multi> (DefDatabase <HairDef> .GetNamed("Mop").texPath, ShaderDatabase.Cutout, Vector2.one, graphics.pawn.story.hairColor); } else { graphics.hairGraphic = GraphicDatabase.Get <Graphic_Multi> (graphics.pawn.story.hairDef.texPath + beard, ShaderDatabase.Cutout, Vector2.one, graphics.pawn.story.hairColor); } } else { graphics.hairGraphic = GraphicDatabase.Get <Graphic_Multi> (graphics.pawn.story.hairDef.texPath, ShaderDatabase.Cutout, Vector2.one, graphics.pawn.story.hairColor); } } // Reroute the graphics for children // For babies and toddlers if (graphics.pawn.ageTracker.CurLifeStageIndex <= AgeStage.Baby) { string toddler_hair = "Boyish"; if (graphics.pawn.gender == Gender.Female) { toddler_hair = "Girlish"; } graphics.hairGraphic = GraphicDatabase.Get <Graphic_Multi> ("Things/Pawn/Humanlike/Children/Hairs/Child_" + toddler_hair, ShaderDatabase.Cutout, Vector2.one, graphics.pawn.story.hairColor); graphics.headGraphic = GraphicDatabase.Get <Graphic_Multi> ("Things/Pawn/Humanlike/null", ShaderDatabase.Cutout, Vector2.one, Color.white); // The pawn is a baby if (graphics.pawn.ageTracker.CurLifeStageIndex == AgeStage.Baby) { graphics.nakedGraphic = GraphicDatabase.Get <Graphic_Single> ("Things/Pawn/Humanlike/Children/Bodies/Newborn", ShaderDatabase.CutoutSkin, Vector2.one, graphics.pawn.story.SkinColor); } } // The pawn is a toddler if (graphics.pawn.ageTracker.CurLifeStageIndex == AgeStage.Toddler) { string upright = ""; if (graphics.pawn.ageTracker.AgeBiologicalYears >= 1) { upright = "Upright"; } graphics.nakedGraphic = GraphicDatabase.Get <Graphic_Multi> ("Things/Pawn/Humanlike/Children/Bodies/Toddler" + upright, ShaderDatabase.CutoutSkin, Vector2.one, graphics.pawn.story.SkinColor); } // The pawn is a child else if (graphics.pawn.ageTracker.CurLifeStageIndex == AgeStage.Child) { graphics.nakedGraphic = Children_Drawing.GetChildBodyGraphics(graphics, ShaderDatabase.CutoutSkin, graphics.pawn.story.SkinColor); graphics.headGraphic = Children_Drawing.GetChildHeadGraphics(graphics, ShaderDatabase.CutoutSkin, graphics.pawn.story.SkinColor); } }); }
public override void Tick() { // Something has gone horribly wrong if (pawn.health.hediffSet.HasHediff(HediffDef.Named("PostPregnancy"))) { Log.Error("HumanPregnancy Hediff was not properly removed when pawn " + pawn.NameStringShort + " gave birth."); // delet this if (pawn.health.hediffSet.HasHediff(HediffDef.Named("GivingBirth"))) { pawn.health.RemoveHediff(pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("GivingBirth"))); } pawn.health.RemoveHediff(this); } this.ageTicks++; if (this.pawn.IsHashIntervalTick(1000)) { if (this.pawn.needs.food != null && this.pawn.needs.food.CurCategory == HungerCategory.Starving && Rand.MTBEventOccurs(0.5f, TicksPerDay, MiscarryCheckInterval)) { if (this.Visible && PawnUtility.ShouldSendNotificationAbout(this.pawn)) { Messages.Message("MessageMiscarriedStarvation".Translate(new object[] { this.pawn.LabelIndefinite() }).CapitalizeFirst(), this.pawn, MessageSound.Negative); } Miscarry(false); return; } if (this.IsSeverelyWounded && Rand.MTBEventOccurs(0.5f, TicksPerDay, MiscarryCheckInterval)) { if (this.Visible && PawnUtility.ShouldSendNotificationAbout(this.pawn)) { Messages.Message("MessageMiscarriedPoorHealth".Translate(new object[] { this.pawn.LabelIndefinite() }).CapitalizeFirst(), this.pawn, MessageSound.Negative); } Miscarry(false); return; } } GestationProgress += (1.0f) / (pawn.RaceProps.gestationPeriodDays * TicksPerDay); // Check if pregnancy is far enough along to "show" for the body type if (!is_discovered) { if ( (pawn.story.bodyType == BodyType.Female && GestationProgress > 0.389f) || (pawn.story.bodyType == BodyType.Thin && GestationProgress > 0.375f) || ((pawn.story.bodyType == BodyType.Fat || pawn.story.bodyType == BodyType.Hulk) && GestationProgress > 0.50f)) { // Got the numbers by dividing the average show time (in weeks) per body type by 36 // (36 weeks being the real human gestation period) // https://www.momtricks.com/pregnancy/when-do-you-start-showing-in-pregnancy/ DiscoverPregnancy(); } } // Final stage of pregnancy if (CurStageIndex == 3) { if (!pawn.health.hediffSet.HasHediff(HediffDef.Named("GivingBirth"))) { // Notify the player birth is near if (Visible && PawnUtility.ShouldSendNotificationAbout(pawn)) { Messages.Message("MessageHavingContractions".Translate(new object[] { pawn.LabelIndefinite() }).CapitalizeFirst(), pawn, MessageSound.Standard); } // Give the mother the GivingBirth hediff pawn.health.AddHediff(HediffDef.Named("GivingBirth"), ChildrenUtility.GetPawnBodyPart(pawn, "Torso"), null); } // We're having contractions now else { // Has the pregnancy been tended to? if (pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("GivingBirth")).IsTended()) { // Then we get a safe pregnancy DoBirthSpawn(pawn, father, 1); } // natural birth (probably not a good idea!) else if (GestationProgress >= 1) { // Do risky pregnancy DoBirthSpawn(pawn, father, 0.9f); if (Rand.Value <= 0.1f) { pawn.health.AddHediff(HediffDef.Named("PlacentaBleed"), ChildrenUtility.GetPawnBodyPart(pawn, "Torso"), null); } } } } /*if (this.GestationProgress >= 1) { * if (this.Visible && PawnUtility.ShouldSendNotificationAbout (this.pawn)) { * Messages.Message ("MessageGaveBirth".Translate (new object[] { * this.pawn.LabelIndefinite () * }).CapitalizeFirst (), this.pawn, MessageSound.Benefit); * } * Hediff_Pregnant.DoBirthSpawn (this.pawn, this.father); * }*/ }
// // Methods // public void DoBirthSpawn(Pawn mother, Pawn father, float chance_successful = 1.0f) { if (mother == null) { Log.Error("No mother defined"); return; } if (father == null) { Log.Warning("No father defined"); } float birthing_quality = mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("GivingBirth")).TryGetComp <HediffComp_TendDuration> ().tendQuality; mother.health.AddHediff(HediffDef.Named("PostPregnancy"), null, null); mother.health.AddHediff(HediffDef.Named("Lactating"), ChildrenUtility.GetPawnBodyPart(pawn, "Torso"), null); int num = (mother.RaceProps.litterSizeCurve == null) ? 1 : Mathf.RoundToInt(Rand.ByCurve(mother.RaceProps.litterSizeCurve, 300)); if (num < 1) { num = 1; } // Make sure the pawn looks like mommy and daddy float skin_whiteness = Rand.Range(0, 1); // Pool of "genetic traits" the baby can inherit List <Trait> traitpool = new List <Trait>(); if (mother.RaceProps.Humanlike) { // Add mom's traits to the pool foreach (Trait momtrait in mother.story.traits.allTraits) { traitpool.Add(momtrait); } if (father != null) { // Add dad's traits to the pool foreach (Trait dadtrait in father.story.traits.allTraits) { traitpool.Add(dadtrait); } // Blend skin colour between mom and dad skin_whiteness = Rand.Range(mother.story.melanin, father.story.melanin); } else { // If dad doesn't exist, just use mom's skin colour skin_whiteness = mother.story.melanin; } // Clear out any traits that aren't genetic from the list if (traitpool.Count > 0) { foreach (Trait trait in traitpool.ToArray()) { bool is_genetic = false; foreach (TraitDef gentrait in genetic_traits) { if (gentrait.defName == trait.def.defName) { is_genetic = true; } } if (!is_genetic) { traitpool.Remove(trait); } } } } // Todo: Perhaps add a way to pass on the parent's body build // Best way to do it might be to represent thin/fat/normal/hulk // as a pair of two values, strength and weight // For example, if the mother has an average body type, she would // have a strength of .5f and a weight of .5f. A fat pawn would have // a strength of .5f and a weight of .75f. A thin pawn would have a // strength of .25f and a weight of .25f. A hulk pawn would have // strength of .75f and weight of .75f //List<float> strength_pool = new List<float>(); //List<float> weight_pool = new List<float>(); //// Get mother and fathers info here if possible //float avg_strength = strength_pool.Average(); //float avg_weight = weight_pool.Average(); // Surname passing string last_name = null; if (mother.RaceProps.Humanlike) { if (father == null) { last_name = NameTriple.FromString(mother.Name.ToStringFull).Last; } else { last_name = NameTriple.FromString(father.Name.ToStringFull).Last; } //Log.Message ("Debug: Newborn is born to the " + last_name + " family."); } //PawnGenerationRequest request = new PawnGenerationRequest (mother.kindDef, mother.Faction, PawnGenerationContext.NonPlayer, mother.Map, false, true, false, false, true, false, 1, false, true, true, null, 0, 0, null, skin_whiteness, last_name); PawnGenerationRequest request = new PawnGenerationRequest(mother.kindDef, mother.Faction, PawnGenerationContext.NonPlayer, mother.Map.Tile, false, true, false, false, false, false, 1, false, true, true, false, false, null, 0, 0, null, skin_whiteness, last_name); Pawn baby = null; for (int i = 0; i < num; i++) { baby = PawnGenerator.GeneratePawn(request); if (PawnUtility.TrySpawnHatchedOrBornPawn(baby, mother)) { if (baby.playerSettings != null && mother.playerSettings != null) { baby.playerSettings.AreaRestriction = mother.playerSettings.AreaRestriction; } if (baby.RaceProps.IsFlesh) { baby.relations.AddDirectRelation(PawnRelationDefOf.Parent, mother); if (father != null) { baby.relations.AddDirectRelation(PawnRelationDefOf.Parent, father); } } // Good until otherwise proven bad bool successful_birth = true; var disabledBaby = BackstoryDatabase.allBackstories ["CustomBackstory_NA_Childhood_Disabled"]; if (disabledBaby != null) { baby.story.childhood = disabledBaby; } else { Log.Error("Couldn't find the required Backstory: CustomBackstory_NA_Childhood_Disabled!"); baby.story.childhood = null; } baby.story.adulthood = null; baby.workSettings.Disable(WorkTypeDefOf.Hunting); //hushes up the "has no ranged weapon" alert // remove all traits baby.story.traits.allTraits.Clear(); // Add some genetic traits if (traitpool.Count > 0) { for (int j = 0; j != 2; j++) { Trait gentrait = traitpool.RandomElement(); if (!baby.story.traits.HasTrait(gentrait.def)) { baby.story.traits.GainTrait(gentrait); } } } // Move the baby in front of the mother, rather than on top if (mother.CurrentBed() != null) { baby.Position = baby.Position + new IntVec3(0, 0, 1).RotatedBy(mother.CurrentBed().Rotation); } // else { // baby.Position = baby.Position + new IntVec3 (0, 0, 1).RotatedBy (mother.Rotation); // } // The baby died from bad chance of success if (Rand.Value > chance_successful || chance_successful == 0) { successful_birth = false; } // Birth defects via drugs or alcohol if (mother.health.hediffSet.HasHediff(HediffDef.Named("BirthDefectTracker"))) { Hediff_BirthDefectTracker tracker = (Hediff_BirthDefectTracker)mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("BirthDefectTracker")); // The baby died in utero from chemical affect if (tracker.stillbirth) { successful_birth = false; } // The baby lived! So far, anyways else { // Should the baby get fetal alcohol syndrome? if (tracker.fetal_alcohol) { baby.health.AddHediff(HediffDef.Named("FetalAlcoholSyndrome")); } // If the mother got high while pregnant, crongrats retard // now your baby is addicted to crack if (tracker.drug_addictions.Count > 0) { foreach (HediffDef addiction in tracker.drug_addictions) { baby.health.AddHediff(addiction, null, null); } } } } // Inbred? if (father != null && mother.relations.FamilyByBlood.Contains <Pawn> (father)) { // 50% chance to get a birth defect from inbreeding if (Rand.Range(0, 1) == 1) { GiveRandomBirthDefect(baby); if (baby.health.hediffSet.HasHediff(HediffDef.Named("DefectStillborn"))) { successful_birth = false; } } } // The baby was born! Yay! if (successful_birth == true) { // Give father a happy memory if the birth was successful and he's not dead if (father != null && !father.health.Dead) { // The father is happy the baby was born father.needs.mood.thoughts.memories.TryGainMemory(ThoughtDef.Named("PartnerGaveBirth")); } // Send a message that the baby was born if (mother.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("HumanPregnancy")).Visible&& PawnUtility.ShouldSendNotificationAbout(mother)) { //Messages.Message ("MessageGaveBirth".Translate (new object[] {mother.LabelIndefinite ()}).CapitalizeFirst (), mother, MessageSound.Benefit); Find.LetterStack.ReceiveLetter("LabelGaveBirth".Translate(new object[] { baby.LabelIndefinite() }), "MessageHumanBirth".Translate(new object[] { mother.LabelIndefinite(), baby.Name.ToStringShort }), LetterDefOf.Good, baby, null); } // Try to give PPD. If not, give "New baby" thought float chance = 0.2f; if (mother.story.traits.HasTrait(TraitDefOf.Psychopath)) { chance -= 1; } if (mother.story.traits.HasTrait(TraitDef.Named("Nerves"))) { chance -= 0.2f * mother.story.traits.GetTrait(TraitDef.Named("Nerves")).Degree; } else if (mother.story.traits.HasTrait(TraitDef.Named("NaturalMood"))) { if (mother.story.traits.GetTrait(TraitDef.Named("NaturalMood")).Degree == 2) { chance -= 1; } if (mother.story.traits.GetTrait(TraitDef.Named("NaturalMood")).Degree == -2) { chance += 0.6f; } // For some reason this is broken /*} else if (mother.story.traits.HasTrait (TraitDef.Named ("Neurotic"))) { * if (mother.story.traits.GetTrait (TraitDef.Named ("Neurotic")).Degree == 1) { * chance += 0.2f; * } else * chance += 0.4f;*/ } // Because for whatever dumb reason the Math class doesn't have a Clamp method if (chance < 0) { chance = 0; } if (chance > 1) { chance = 1; } Log.Message("Debugging: Chance of PPD is " + chance * 100 + "%"); // Try to give PPD if (Rand.Value < chance) { mother.needs.mood.thoughts.memories.TryGainMemory(ThoughtDef.Named("PostPartumDepression"), null); bool verybad = false; if (mother.story.traits.HasTrait(TraitDef.Named("NaturalMood"))) { if (mother.story.traits.GetTrait(TraitDef.Named("NaturalMood")).Degree == -2) { verybad = true; } } else if (mother.story.traits.HasTrait(TraitDef.Named("Neurotic"))) { if (mother.story.traits.GetTrait(TraitDef.Named("Neurotic")).Degree == 2) { verybad = true; } } // This pawn gets an exceptionally bad case of PPD if (verybad) { foreach (Thought_Memory thought in mother.needs.mood.thoughts.memories.Memories) { if (thought.def.defName == "PostPartumDepression") { thought.SetForcedStage(thought.CurStageIndex + 1); } } } } else { // If we didn't get PPD, then the pawn gets a mood buff if (mother.health.hediffSet.HasHediff(HediffDef.Named("GaveBirthFlag")) == false) { mother.needs.mood.thoughts.memories.TryGainMemory(ThoughtDef.Named("IGaveBirthFirstTime")); } else { mother.needs.mood.thoughts.memories.TryGainMemory(ThoughtDef.Named("IGaveBirth")); } } } else { bool aborted = false; if (chance_successful < 0f) { aborted = true; } if (baby != null) { Miscarry(baby, aborted); } } } else { Find.WorldPawns.PassToWorld(baby, PawnDiscardDecideMode.Discard); } } // Post birth if (mother.Spawned) { // Spawn guck FilthMaker.MakeFilth(mother.Position, mother.Map, ThingDefOf.FilthAmnioticFluid, mother.LabelIndefinite(), 5); if (mother.caller != null) { mother.caller.DoCall(); } if (baby != null) { if (baby.caller != null) { baby.caller.DoCall(); } } Log.Message("Birth quality = " + birthing_quality); // Possible tearing from pregnancy if (birthing_quality < 0.75f) { if (birthing_quality < Rand.Value) { // Add a tear from giving birth if (birthing_quality < Rand.Value) { mother.health.AddHediff(HediffDef.Named("PregnancyTearMajor"), ChildrenUtility.GetPawnBodyPart(mother, "Torso"), null); } else { mother.health.AddHediff(HediffDef.Named("PregnancyTear"), ChildrenUtility.GetPawnBodyPart(mother, "Torso"), null); } } } } pawn.health.RemoveHediff(pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("GivingBirth"))); pawn.health.RemoveHediff(this); }
internal static void TryToImpregnate(Pawn initiator, Pawn partner) { // Lesbian/gay couples. Those cases should never result in pregnancy if (initiator.gender == partner.gender) { return; } // One of the two is sterile, so don't continue foreach (Pawn pawn in new List <Pawn> { initiator, partner }) { if (pawn.health.hediffSet.HasHediff(HediffDef.Named("Sterile"))) { return; } } Pawn male = initiator.gender == Gender.Male? initiator: partner; Pawn female = initiator.gender == Gender.Female ? initiator : partner; // Only humans can be impregnated for now if (!ChildrenUtility.RaceUsesChildren(female)) { return; } BodyPartRecord torso = female.RaceProps.body.AllParts.Find(x => x.def == BodyPartDefOf.Torso); HediffDef contraceptive = HediffDef.Named("Contraceptive"); // Make sure the woman is not pregnanct and not using a contraceptive if (female.health.hediffSet.HasHediff(HediffDefOf.Pregnant, torso) || female.health.hediffSet.HasHediff(contraceptive, null) || male.health.hediffSet.HasHediff(contraceptive, null)) { return; } // Check the pawn's age to see how likely it is she can carry a fetus // 25 and below is guaranteed, 50 and above is impossible, 37.5 is 50% chance float preg_chance = Math.Max(1 - (Math.Max(female.ageTracker.AgeBiologicalYearsFloat - 25, 0) / 25), 0) * 0.33f; // For debug testing //float preg_chance = 1; if (preg_chance < Rand.Value) { if (Prefs.DevMode) { Log.Message("Impregnation failed. Chance was " + preg_chance); } return; } if (Prefs.DevMode) { Log.Message("Impregnation succeeded. Chance was " + preg_chance); } // Spawn a bunch of hearts. Sharp eyed players may notice this means impregnation occurred. for (int i = 0; i <= 3; i++) { MoteMaker.ThrowMetaIcon(male.Position, male.MapHeld, ThingDefOf.Mote_Heart); MoteMaker.ThrowMetaIcon(female.Position, male.MapHeld, ThingDefOf.Mote_Heart); } // Do the actual impregnation. We apply it to the torso because Remove_Hediff in operations doesn't work on WholeBody (null body part) // for whatever reason. female.health.AddHediff(Hediff_HumanPregnancy.Create(female, male), torso); }
public override void Tick() { // Something has gone horribly wrong if (pawn.health.hediffSet.HasHediff(HediffDef.Named("PostPregnancy"))) { Log.Error("HumanPregnancy Hediff was not properly removed when pawn " + pawn.NameStringShort + " gave birth."); // delet this if (pawn.health.hediffSet.HasHediff(HediffDef.Named("GivingBirth"))) { pawn.health.RemoveHediff(pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("GivingBirth"))); } pawn.health.RemoveHediff(this); } this.ageTicks++; if (this.pawn.IsHashIntervalTick(1000)) { if (this.pawn.needs.food != null && this.pawn.needs.food.CurCategory == HungerCategory.Starving && Rand.MTBEventOccurs(0.5f, 60000, 1000)) { if (this.Visible && PawnUtility.ShouldSendNotificationAbout(this.pawn)) { Messages.Message("MessageMiscarriedStarvation".Translate(new object[] { this.pawn.LabelIndefinite() }).CapitalizeFirst(), this.pawn, MessageSound.Negative); } Miscarry(false); return; } if (this.IsSeverelyWounded && Rand.MTBEventOccurs(0.5f, 60000, 1000)) { if (this.Visible && PawnUtility.ShouldSendNotificationAbout(this.pawn)) { Messages.Message("MessageMiscarriedPoorHealth".Translate(new object[] { this.pawn.LabelIndefinite() }).CapitalizeFirst(), this.pawn, MessageSound.Negative); } Miscarry(false); return; } } GestationProgress += 1 / (pawn.RaceProps.gestationPeriodDays * 60000); // Discover from if (!is_discovered) { if ((GestationProgress > 0.25f && pawn.story.bodyType != BodyType.Fat) || (GestationProgress > 0.1f && pawn.story.bodyType == BodyType.Thin)) { DiscoverPregnancy(); } } // Final stage of pregnancy if (CurStageIndex == 3) { if (!pawn.health.hediffSet.HasHediff(HediffDef.Named("GivingBirth"))) { // Notify the player birth is near if (Visible && PawnUtility.ShouldSendNotificationAbout(pawn)) { Messages.Message("MessageHavingContractions".Translate(new object[] { pawn.LabelIndefinite() }).CapitalizeFirst(), pawn, MessageSound.Standard); } // Give the mother the GivingBirth hediff pawn.health.AddHediff(HediffDef.Named("GivingBirth"), ChildrenUtility.GetPawnBodyPart(pawn, "Torso"), null); } // We're having contractions now else { // Has the pregnancy been tended to? if (pawn.health.hediffSet.GetFirstHediffOfDef(HediffDef.Named("GivingBirth")).IsTended()) { // Then we get a safe pregnancy DoBirthSpawn(pawn, father, 1); } // natural birth (probably not a good idea!) else if (GestationProgress >= 1) { // Do risky pregnancy DoBirthSpawn(pawn, father, 0.9f); if (Rand.Value <= 0.1f) { pawn.health.AddHediff(HediffDef.Named("PlacentaBleed"), ChildrenUtility.GetPawnBodyPart(pawn, "Torso"), null); } } } } /*if (this.GestationProgress >= 1) { * if (this.Visible && PawnUtility.ShouldSendNotificationAbout (this.pawn)) { * Messages.Message ("MessageGaveBirth".Translate (new object[] { * this.pawn.LabelIndefinite () * }).CapitalizeFirst (), this.pawn, MessageSound.Benefit); * } * Hediff_Pregnant.DoBirthSpawn (this.pawn, this.father); * }*/ }
static void TryGiveJob_Patch(JobGiver_OptimizeApparel __instance, ref Job __result, Pawn pawn) { if (__result != null) { // Stop the game from automatically allocating pawns Wear jobs they cannot fulfil if ((pawn.ageTracker.CurLifeStageIndex <= AgeStage.Toddler && __result.targetA.Thing.TryGetComp <CompBabyGear>() == null && ChildrenUtility.RaceUsesChildren(pawn)) || (pawn.ageTracker.CurLifeStageIndex >= AgeStage.Child && __result.targetA.Thing.TryGetComp <CompBabyGear>() != null)) { __result = null; } } else { // makes pawn to remove baby clothes when too old for them. List <Apparel> wornApparel = pawn.apparel.WornApparel; if (pawn.ageTracker.CurLifeStageIndex >= AgeStage.Child) { for (int i = wornApparel.Count - 1; i >= 0; i--) { CompBabyGear compBabyGear = wornApparel[i].TryGetComp <CompBabyGear>(); if (compBabyGear != null) { __result = new Job(JobDefOf.RemoveApparel, wornApparel[i]) { haulDroppedApparel = true }; return; } } } } }
public static IEnumerable <Pawn> BedCandidates(Building_Bed bed) { if (bed.def.defName.Contains("Crib")) { IEnumerable <Pawn> candidates = bed.Map.mapPawns.FreeColonists.Where(x => x.ageTracker.CurLifeStageIndex <= 2 && x.Faction == Faction.OfPlayer && ChildrenUtility.RaceUsesChildren(x)); return(candidates); } else { return(bed.Map.mapPawns.FreeHumanlikesOfFaction(Faction.OfPlayer)); } }
public static T FailOnBaby <T>(this T f) where T : JobDriver { f.AddEndCondition(delegate { // return JobCondition.Incompletable; //CompBabyGear compBabyGear = f.job.GetTarget(TargetIndex.A).Thing.TryGetComp<CompBabyGear>(); Pawn personDressing = f.GetActor(); if (f.GetActor().ageTracker.CurLifeStageIndex <= 1 && f.job.GetTarget(TargetIndex.A).Thing.TryGetComp <CompBabyGear>() == null && ChildrenUtility.RaceUsesChildren(f.GetActor())) { Messages.Message("MessageAdultClothesOnBaby".Translate(new object[] { personDressing.Name.ToStringShort }), personDressing, MessageTypeDefOf.CautionInput); return(JobCondition.Incompletable); } //tried testing for bool "isBabyGear" but that caused a null error exception, so I switched to just testing if comp was null, will look into later if (f.GetActor().ageTracker.CurLifeStageIndex > 1 && f.job.GetTarget(TargetIndex.A).Thing.TryGetComp <CompBabyGear>() != null) { Messages.Message("MessageBabyClothesOnAdult".Translate(new object[] { personDressing.LabelShort }), personDressing, MessageTypeDefOf.CautionInput); return(JobCondition.Incompletable); } else { return(JobCondition.Ongoing); } }); return(f); }