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 Privacy_Please { 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); 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.UnworriedAboutHumanSex() == false) { return true; } if (pawn.IsHavingSex() == false && pawn.IsMasturbating() == false) { return true; } if (pawn.GetLord() != null && (pawn.GetLord().LordJob is LordJob_Ritual || pawn.GetLord().LordJob is LordJob_Joinable_Party) && BasicSettings.ignoreRitualAndPartySex) { return true; } bool hasPrivacy = true; pawn.IsInBed(out Building bed); foreach (Thing thing in GenRadial.RadialDistinctThingsAround(pawn.Position, pawn.Map, radius, true)) { Pawn witness = thing as Pawn; if (witness == null) continue; // Caught having sex if (SexInteractionUtility.PawnCaughtLovinByWitness(pawn, witness)) { // Try to invite intruder to join in if (SexInteractionUtility.InvitePasserbyForSex(pawn, witness, out bool brokeTaboo)) { 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); } } // Voyeurs and cuckolds like to watch else if (CasualSex_Helper.CanHaveSex(witness) && xxx.IsTargetPawnOkay(witness) && (xxx.has_quirk(witness, "Voyeur") || (xxx.has_quirk(witness, "Cuckold") && SexInteractionUtility.SexParticipantsIncludesACheatingPartner(witness, pawn.GetAllSexParticipants())))) { Job job = new Job(DefDatabase.GetNamed("WatchSex", false), pawn.GetSexReceiver(), bed); witness.jobs.TryTakeOrderedJob(job); } // Do nothing if witness is a lover and no taboo was not broken else if (brokeTaboo == false && witness.IsLoverOfOther(pawn)) { } // Privacy was breached else { hasPrivacy = false; } } } return BasicSettings.needPrivacy == false || hasPrivacy || xxx.has_quirk(pawn, "Exhibitionist") || pawn?.ideo?.Ideo.HasPrecept(ModPreceptDefOf.Exhibitionism_Acceptable) == true || pawn?.ideo?.Ideo.HasPrecept(ModPreceptDefOf.Exhibitionism_Approved) == true; } public static bool HasPreceptForIssue(this Pawn pawn, string issueDefName, out Precept precept) { precept = null; if (pawn?.Ideo == null) { return false; } foreach (Precept _precept in pawn.Ideo.PreceptsListForReading) { if (_precept.def.issue.defName == issueDefName) { precept = _precept; 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()); } return HasTrait(pawn, traitDef); } public static bool HasTrait(this Pawn pawn, TraitDef traitDef) { if (pawn?.story?.traits?.allTraits == null || pawn.story.traits.allTraits.NullOrEmpty()) { return false; } if (traitDef == null) { return false; } return pawn.story.traits.HasTrait(traitDef); } public static bool UnworriedAboutHumanSex(this Pawn pawn) { return (pawn.Dead || pawn.AnimalOrWildMan() || pawn.RaceProps.IsMechanoid || pawn.Awake() == false || pawn.Suspended == false || (pawn.Faction != null && pawn.Faction.HostileTo(Faction.OfPlayer))) == false; } } }