using System.Collections.Generic; using System.Linq; using UnityEngine; using Verse; using Verse.AI; using Verse.AI.Group; using RimWorld; using rjw; using Rimworld_Animations; using HarmonyLib; namespace Rimworld_Animations_Patch { public static class PawnExtension { public static bool IsInBed(this Pawn pawn, out Building bed) { bed = pawn.Position.GetThingList(pawn.Map).FirstOrDefault(x => x is Building_Bed) as Building; return bed != null; } public static bool IsSeated(this Pawn pawn, out Building seat) { seat = pawn.Position.GetThingList(pawn.Map).FirstOrDefault(x => x is Building && x.def.building.isSittable) as Building; return seat != null; } public static bool IsHavingSex(this Pawn pawn) { if (pawn?.jobs?.curDriver == null || pawn.Dead || pawn.jobs.curDriver is JobDriver_Sex == false) { return false; } JobDriver_Sex jobdriver = pawn.jobs.curDriver as JobDriver_Sex; return jobdriver.Partner != null && jobdriver.Partner != pawn; } public static bool IsMasturbating(this Pawn pawn) { if (pawn?.jobs?.curDriver == null || pawn.Dead || pawn.jobs.curDriver is JobDriver_Sex == false) { return false; } JobDriver_Sex jobdriver = pawn.jobs.curDriver as JobDriver_Sex; return jobdriver.Partner == null || jobdriver.Partner == pawn || (jobdriver.Partner is Pawn) == false; } public static Pawn GetSexInitiator(this Pawn pawn) { if (pawn?.jobs?.curDriver != null && pawn.Dead == false && pawn.jobs.curDriver is JobDriver_SexBaseInitiator) { return pawn; } JobDriver_SexBaseReciever jobDriver = pawn.jobs.curDriver as JobDriver_SexBaseReciever; if (jobDriver?.Partner?.jobs?.curDriver != null && jobDriver.Partner.Dead == false && jobDriver.Partner.jobs.curDriver is JobDriver_SexBaseInitiator) { return jobDriver.Partner; } return null; } public static Pawn GetSexReceiver(this Pawn pawn) { if (pawn.jobs.curDriver is JobDriver_SexBaseReciever) { return pawn; } JobDriver_SexBaseInitiator jobDriver = pawn.jobs.curDriver as JobDriver_SexBaseInitiator; if (jobDriver?.Partner?.jobs?.curDriver != null && jobDriver.Partner.Dead == false && jobDriver.Partner.jobs.curDriver is JobDriver_SexBaseReciever) { return jobDriver.Partner; } return null; } public static Pawn GetSexPartner(this Pawn pawn) { return (pawn.jobs.curDriver as JobDriver_Sex)?.Partner; } public static List GetAllSexParticipants(this Pawn pawn) { List participants = new List(); if (pawn?.jobs?.curDriver == null || (pawn.jobs.curDriver is JobDriver_Sex) == false) { return participants; } if (pawn.GetSexReceiver() != null) { List partners = (pawn.GetSexReceiver().jobs.curDriver as JobDriver_SexBaseReciever).parteners.ToList(); if (partners != null) { foreach (Pawn partner in partners) { if (partner != null) { participants = partners; } } } } if (pawn.GetSexInitiator() != null) { Pawn partner = (pawn.GetSexInitiator().jobs.curDriver as JobDriver_SexBaseInitiator).Partner; if (partner != null && partner.Dead == false) { participants.AddDistinct(partner); } } participants.AddDistinct(pawn); participants.SortBy(x => x.GetAnimationData() != null ? x.GetAnimationData().actorID : participants.IndexOf(x)); return participants; } public static bool IsLoverOfOther(this Pawn pawn, Pawn other) { if (pawn == null || other == null) { return false; } List lovers = SpouseRelationUtility.GetLoveRelations(pawn, false); return lovers.Any(x => x.otherPawn == other); } public static bool HasPrivacy(this Pawn pawn, float radius) { if (pawn.AnimalOrWildMan() || pawn.RaceProps.Humanlike == false) { return true; } if (pawn.IsHavingSex() == false && pawn.IsMasturbating() == false) { return true; } bool hasPrivacy = true; bool isExhibitionismAllowedByPrecept = false; Precept precept = new Precept(); if (HasPreceptForIssue(pawn, DefDatabase.GetNamedSilentFail("Exhibitionism"), out precept)) { isExhibitionismAllowedByPrecept = !IssueIsMajorTaboo(pawn, DefDatabase.GetNamedSilentFail("Exhibitionism"), out precept) && !IssueIsMinorTaboo(pawn, DefDatabase.GetNamedSilentFail("Exhibitionism"), out precept); } bool isExhibitionist = pawn.HasTrait("Exhibitionist") || xxx.has_quirk(pawn, "Exhibitionist") || isExhibitionismAllowedByPrecept; pawn.IsInBed(out Building bed); foreach (Thing thing in GenRadial.RadialDistinctThingsAround(pawn.Position, pawn.Map, radius, true)) { Pawn witness = thing as Pawn; // Caught having sex if (SexInteractionUtility.PawnCaughtLovinByWitness(pawn, witness)) { SexInteractionUtility.ResolveThoughtsForWhenSexIsWitnessed(pawn, witness, out bool witnessJoiningSex); // Try to invite intruder to join in if (witnessJoiningSex) { if (pawn.IsMasturbating()) { if (bed == null) { Job job = new Job(xxx.quick_sex, pawn); witness.jobs.TryTakeOrderedJob(job); } else { Job job = new Job(xxx.casual_sex, pawn, bed); witness.jobs.TryTakeOrderedJob(job); } } else if (pawn.GetSexReceiver() != null) { Job job = new Job(DefDatabase.GetNamed("JoinInSex", false), pawn.GetSexReceiver(), bed); witness.jobs.TryTakeOrderedJob(job); } } // The invitation failed else { hasPrivacy = false; } } } return hasPrivacy || isExhibitionist || BasicSettings.needPrivacy == false; } public static ActorAnimationData GetAnimationData(this Pawn pawn) { if (pawn.TryGetComp() == null) return null; if (pawn.TryGetComp().isAnimating == false) return null; AnimationDef animationDef = (AnimationDef)AccessTools.Field(typeof(CompBodyAnimator), "anim").GetValue(pawn.TryGetComp()); int actorID = (int)AccessTools.Field(typeof(CompBodyAnimator), "actor").GetValue(pawn.TryGetComp()); int currentStage = (int)AccessTools.Field(typeof(CompBodyAnimator), "curStage").GetValue(pawn.TryGetComp()); int stageTicks = (int)AccessTools.Field(typeof(CompBodyAnimator), "stageTicks").GetValue(pawn.TryGetComp()); Rot4 actorFacing = (Rot4)AccessTools.Field(typeof(CompBodyAnimator), "bodyFacing").GetValue(pawn.TryGetComp()); return new ActorAnimationData(animationDef, actorID, currentStage, stageTicks, actorFacing); } public static List GetHands(this Pawn pawn) { if (HandAnimationUtility.handDef == null) { HandAnimationUtility.handDef = DefDatabase.GetNamed("Hand", false); } return pawn.health.hediffSet.GetNotMissingParts().Where(x => x.def == HandAnimationUtility.handDef)?.ToList(); } public static bool HasPreceptForIssue(this Pawn pawn, IssueDef issueDef, out Precept precept) { precept = null; if (pawn?.Ideo == null || issueDef == null) { return false; } foreach (Precept _precept in pawn.Ideo.PreceptsListForReading) { if (_precept.def.issue == issueDef) { precept = _precept; return true; } } return false; } public static bool IssueIsMajorTaboo(this Pawn pawn, IssueDef issueDef, out Precept precept) { if (HasPreceptForIssue(pawn, issueDef, out precept)) { if (precept.def.defName.Contains("Forbidden") || precept.def.defName.Contains("Prohibited") || precept.def.defName.Contains("Abhorrent")) { return true; } } return false; } public static bool IssueIsMinorTaboo(this Pawn pawn, IssueDef issueDef, out Precept precept) { if (HasPreceptForIssue(pawn, issueDef, out precept)) { if (precept.def.defName.Contains("Horrible") || precept.def.defName.Contains("Despised") || precept.def.defName.Contains("Disapproved")) { return true; } } return false; } public static bool EnjoysViolence(this Pawn pawn) { if (pawn.IsAnimal() || pawn.RaceProps.IsMechanoid) { return true; } if (pawn?.story?.traits?.allTraits == null || pawn?.story?.traits?.allTraits.NullOrEmpty() == true) { return false; } List traits = new List() { "Brawler", "Psychopath", "Bloodlust" }; return pawn.story.traits.allTraits.Any(x => traits.Contains(x.def.defName)); } public static bool DislikesViolence(this Pawn pawn) { if (pawn.IsAnimal() || pawn.RaceProps.IsMechanoid) { return false; } if (pawn?.story?.traits?.allTraits == null || pawn?.story?.traits?.allTraits.NullOrEmpty() == true) { return false; } List traits = new List() { "Kind", "Wimp" }; return pawn.WorkTagIsDisabled(WorkTags.Violent) || pawn.story.traits.allTraits.Any(x => traits.Contains(x.def.defName)); } public static bool HasTrait(this Pawn pawn, string trait) { if (pawn?.story?.traits?.allTraits == null || pawn.story.traits.allTraits.NullOrEmpty()) { return false; } TraitDef traitDef = DefDatabase.GetNamedSilentFail(trait); if (traitDef == null) { traitDef = DefDatabase.GetNamedSilentFail(trait.ToLower()); } if (traitDef == null) { return false; } return pawn.story.traits.HasTrait(traitDef); } } }