From 4e8368ef3581b80c7e94f50e3002c903312f1217 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Sun, 21 Jul 2019 17:02:05 -0700
Subject: [PATCH 01/15] Make humpshroom grow in a reasonable amount of time,
 growable outside of hydroponics beds, and spawn naturally.

---
 Defs/ThingDefs/Drugs.xml | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/Defs/ThingDefs/Drugs.xml b/Defs/ThingDefs/Drugs.xml
index 1ca2747c7..2452f0099 100644
--- a/Defs/ThingDefs/Drugs.xml
+++ b/Defs/ThingDefs/Drugs.xml
@@ -19,12 +19,13 @@
 		<plant>
 			<fertilityMin>0.01</fertilityMin>
 			<fertilitySensitivity>0.25</fertilitySensitivity>
-			<growDays>40.00</growDays>
+			<growDays>10.00</growDays>
 			<harvestTag>Standard</harvestTag>
 			<harvestedThingDef>HumpShroom</harvestedThingDef>
 			<harvestYield>2</harvestYield>
 			<sowMinSkill>4</sowMinSkill>
 			<sowTags>
+				<li>Ground</li>
 				<li>Hydroponic</li>
 			</sowTags>
 			<topWindExposure>0.1</topWindExposure>
@@ -32,6 +33,8 @@
 				<min>0.3</min>
 				<max>1.0</max>
 			</visualSizeRange>
+			<wildClusterRadius>3</wildClusterRadius>
+			<wildClusterWeight>5</wildClusterWeight>
 		</plant>
 		<comps>
 			<li Class="CompProperties_Glower">
-- 
GitLab


From 5bcde8bd2d45e07f6d162de1fceff17f116f2773 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Sun, 21 Jul 2019 17:04:11 -0700
Subject: [PATCH 02/15] Give humpshroom addicts going through withdrawl a tiny
 amount of satisfaction so they aren't completely disabled.

---
 Source/Common/SexUtility.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Source/Common/SexUtility.cs b/Source/Common/SexUtility.cs
index 2b0b77911..dcb663df3 100644
--- a/Source/Common/SexUtility.cs
+++ b/Source/Common/SexUtility.cs
@@ -572,7 +572,7 @@ namespace rjw
 			if (pawn.health.hediffSet.HasHediff(HediffDef.Named("HumpShroomAddiction")) && !pawn.health.hediffSet.HasHediff(HediffDef.Named("HumpShroomEffect")))
 			{
 				//Log.Message("[RJW]xxx::satisfy 0 pawn is " + xxx.get_pawnname(pawn));
-				satisfaction = 0;
+				satisfaction = 0.1f;
 			}
 
 			//--Log.Message("xxx::satisfy( " + pawn_name + ", " + partner_name + ", " + violent + "," + isCoreLovin + " ) - setting pawn joy");
-- 
GitLab


From eb66b84733aed255f7ee901715df6f18402fb3f3 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Sun, 21 Jul 2019 17:11:47 -0700
Subject: [PATCH 03/15] Add some limited casual sex mechanics and better
 hookups with partners!

Added:
+ Pawns who don't have partners might hook up with other pawns who don't have partners.  At least partners who are around right now...

Changed:
+ Nymphos no longer cheat on partners or homewreck.
+ Pawns will consider AllPawns, not just FreeColonists, so that they can bang guests too.  Haven't tested with prisoners but come on it's only like 98% likely to have a bug.
+ Significantly increased the distance pawns will travel to find a hookup
+ Added vanilla Got Some Lovin' thought to partners after casual sex
+ Bug fix for xxx.HasNonPolyPartner() when neither RomanceDiversified or Psychology are active; it should have returned true when the pawn had any partner instead of false.
+ Refactored think about diseases into its own method.
---
 Source/Common/xxx.cs                     | 38 ++++++++++++++--
 Source/JobDrivers/JobDriver_JoinInBed.cs |  2 +-
 Source/JobGivers/JobGiver_JoinInBed.cs   | 55 ++++++++++++++++++------
 3 files changed, 77 insertions(+), 18 deletions(-)

diff --git a/Source/Common/xxx.cs b/Source/Common/xxx.cs
index 68dc0f6c7..c752d3434 100644
--- a/Source/Common/xxx.cs
+++ b/Source/Common/xxx.cs
@@ -129,6 +129,7 @@ namespace rjw
 		public static readonly ThoughtDef violated_corpse = DefDatabase<ThoughtDef>.GetNamed("ViolatedCorpse");
 		public static readonly ThoughtDef gave_virginity = DefDatabase<ThoughtDef>.GetNamed("FortunateGaveVirginity");
 		public static readonly ThoughtDef lost_virginity = DefDatabase<ThoughtDef>.GetNamed("UnfortunateLostVirginity");
+		public static readonly ThoughtDef VanillaGotSomeLovin = DefDatabase<ThoughtDef>.GetNamed("GotSomeLovin");
 		public static readonly ThoughtDef took_virginity = DefDatabase<ThoughtDef>.GetNamed("TookVirginity");
 
 		public static readonly JobDef fappin = DefDatabase<JobDef>.GetNamed("Fappin");
@@ -549,17 +550,36 @@ namespace rjw
 			return need_sex.CurLevel < need_sex.thresh_frustrated();
 		}
 
+		public static bool is_horny(Pawn pawn)
+		{
+			Need_Sex need_sex = pawn.needs.TryGetNeed<Need_Sex>();
+			if (need_sex == null) return false;
+			return need_sex.CurLevel < need_sex.thresh_horny();
+		}
+
 		public static bool HasNonPolyPartner(Pawn pawn)
 		{
-			if (!RomanceDiversifiedIsActive && !PsychologyIsActive)
+			if (!LovePartnerRelationUtility.HasAnyLovePartner(pawn))
 				return false;
 
+			if (!RomanceDiversifiedIsActive && !PsychologyIsActive)
+				return true;
+
 			foreach (DirectPawnRelation relation in pawn.relations.DirectRelations)
 			{
-				if (relation.def != PawnRelationDefOf.Lover && relation.def != PawnRelationDefOf.Fiance &&
-					relation.def != PawnRelationDefOf.Spouse) continue;
+				if (relation.def != PawnRelationDefOf.Lover &&
+					relation.def != PawnRelationDefOf.Fiance &&
+					relation.def != PawnRelationDefOf.Spouse)
+				{
+					continue;
+				}
+
 				if ((RomanceDiversifiedIsActive && relation.otherPawn.story.traits.HasTrait(polyamorous)) ||
-					(PsychologyIsActive && relation.otherPawn.story.traits.HasTrait(polygamous))) continue;
+					(PsychologyIsActive && relation.otherPawn.story.traits.HasTrait(polygamous)))
+				{
+					continue;
+				}
+
 				return true;
 			}
 			return false;
@@ -1529,9 +1549,19 @@ namespace rjw
 					}
 				}
 			}
+			else if (is_human(partner))
+			{
+				// human partner and not part of rape or necrophilia.  add the vanilla GotSomeLovin thought
+				pawn.needs.mood.thoughts.memories.TryGainMemory(VanillaGotSomeLovin, partner);
+			}
 
 			//--Log.Message("xxx::think_about_sex( " + xxx.get_pawnname(pawn) + ", " + xxx.get_pawnname(partner) + ", " + violent + " ) - setting disease thoughts");
 
+			ThinkAboutDiseases(pawn, partner);
+		}
+
+		private static void ThinkAboutDiseases(Pawn pawn, Pawn partner)
+		{
 			// Dead and non-humans have no diseases (yet).
 			if (partner.Dead || !is_human(partner)) return;
 
diff --git a/Source/JobDrivers/JobDriver_JoinInBed.cs b/Source/JobDrivers/JobDriver_JoinInBed.cs
index 2bde24449..11e513d23 100644
--- a/Source/JobDrivers/JobDriver_JoinInBed.cs
+++ b/Source/JobDrivers/JobDriver_JoinInBed.cs
@@ -68,7 +68,7 @@ namespace rjw
 				initAction = delegate
 				{
 					// Trying to add some interactions and social logs
-					SexUtility.ProcessSex(Top, Partner, false, true);
+					SexUtility.ProcessSex(Top, Partner, false /*rape*/, true/*isCoreLovin*/, false /*whoring*/);
 				},
 				defaultCompleteMode = ToilCompleteMode.Instant
 			};
diff --git a/Source/JobGivers/JobGiver_JoinInBed.cs b/Source/JobGivers/JobGiver_JoinInBed.cs
index b5a022069..2b8f89293 100644
--- a/Source/JobGivers/JobGiver_JoinInBed.cs
+++ b/Source/JobGivers/JobGiver_JoinInBed.cs
@@ -9,6 +9,9 @@ namespace rjw
 {
 	public class JobGiver_JoinInBed : ThinkNode_JobGiver
 	{
+		private const float NonNymphoHookupChance = 0.3f;
+		private const int MaxDistanceSquaredToFuck = 10000;
+
 		private static bool is_healthy(Pawn target)
 		{
 			return xxx.is_healthy(target) && (xxx.can_fuck(target) || xxx.can_be_fucked(target));
@@ -31,10 +34,12 @@ namespace rjw
 		{
 			Pawn best_fuckee = null;
 			float best_distance = 1.0e6f;
+			bool pawnIsNympho = xxx.is_nympho(pawn);
 			//Rand.PopState();
 			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 
-			List<Pawn> targets = map.mapPawns.FreeColonists.Where(x 
+			// Check AllPawns, not just colonists, to include guests.
+			List<Pawn> targets = map.mapPawns.AllPawns.Where(x 
 				=> x.InBed()
 				&& x != pawn
 				&& !x.Position.IsForbidden(pawn) 
@@ -42,6 +47,7 @@ namespace rjw
 				&& !x.Downed 
 				&& is_healthy(x) 
 				&& x.Map == pawn.Map 
+				&& !x.HostileTo(pawn)
 				&& (xxx.is_laying_down_alone(x) || xxx.in_same_bed(x, pawn))
 				).ToList();
 
@@ -58,32 +64,52 @@ namespace rjw
 				foreach (Pawn target in partners)
 				{
 					//Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(target) + " ) partner found");
-					if (pawn.Position.DistanceToSquared(target.Position) < 100
+					if (pawn.Position.DistanceToSquared(target.Position) < MaxDistanceSquaredToFuck
 						&& pawn.CanReserveAndReach(target, PathEndMode.OnCell, Danger.Some, 1, 0)
 						&& target.CanReserve(pawn, 1, 0))
+					{
+						Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(pawn) + " ) found lover (" + xxx.get_pawnname(target) + ")");
 						return target;
+					}
 					//Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(target) + " ) partner cant be fucked right now");
 				}
 			}
 
-			// No random lovin' for non-nymphos.
-			if (!xxx.is_nympho(pawn))
+			// No lovers around... see if the pawn fancies a hookup.  Nymphos and frustrated pawns always do!
+			bool canHookup = pawnIsNympho || xxx.is_frustrated(pawn) || (xxx.is_horny(pawn) && Rand.Value > NonNymphoHookupChance);
+			if (!canHookup)
 				return null;
 
-			foreach (Pawn q in targets)
+			// No cheating from casual hookups... would probably make colony relationship management too annoying
+			Pawn pawnBestLover = LovePartnerRelationUtility.ExistingMostLikedLovePartner(pawn, false /*allowDead*/);
+			bool canHookupWithoutCheating = !xxx.HasNonPolyPartner(pawn) || pawnBestLover == null || pawnBestLover.Map != pawn.Map;
+			if (!canHookupWithoutCheating)
+				return null;
+
+			foreach (Pawn targetPawn in targets)
 			{
-				if (pawn.CanReserveAndReach(q, PathEndMode.OnCell, Danger.Some, 1, 0) &&
-					q.CanReserve(pawn, 1, 0) &&
-					roll_to_skip(pawn, q))
+				// No homewrecking either!
+				if (xxx.HasNonPolyPartner(targetPawn) && 
+					LovePartnerRelationUtility.ExistingMostLikedLovePartner(targetPawn, false /*allowDead*/).Map == pawn.Map)
+					continue;
+
+				if (pawn.CanReserveAndReach(targetPawn, PathEndMode.OnCell, Danger.Some, 1, 0) &&
+					targetPawn.CanReserve(pawn, 1, 0) &&
+					roll_to_skip(pawn, targetPawn))
 				{
-					int dis = pawn.Position.DistanceToSquared(q.Position);
-					if (dis < best_distance)
+					int dis = pawn.Position.DistanceToSquared(targetPawn.Position);
+					if (dis < best_distance && dis < MaxDistanceSquaredToFuck)
 					{
-						best_fuckee = q;
+						best_fuckee = targetPawn;
 						best_distance = dis;
 					}
 				}
 			}
+
+			if (best_fuckee != null)
+			{
+				Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(pawn) + " ) found rando (" + xxx.get_pawnname(best_fuckee) + ")");
+			}
 			return best_fuckee;
 		}
 
@@ -91,8 +117,11 @@ namespace rjw
 		{
 			//--Log.Message("[RJW] JobGiver_JoinInBed( " + xxx.get_pawnname(pawn) + " ) called");
 
-			if (pawn.Drafted) return null;
-			if (!SexUtility.ReadyForLovin(pawn) && !xxx.is_frustrated(pawn)) return null;
+			if (pawn.Drafted)
+				return null;
+
+			if (!SexUtility.ReadyForLovin(pawn) && !xxx.is_frustrated(pawn))
+				return null;
 
 			if (pawn.CurJob == null || pawn.CurJob.def == JobDefOf.LayDown)
 			{
-- 
GitLab


From 215d0eac8362b0d651c39c8457a49498badabe88 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Sun, 21 Jul 2019 20:41:57 -0700
Subject: [PATCH 04/15] - Fix double stacks of "got some lovin!" by making
 JobDriver_JoinInBed pass false for isCoreLovin to ProcessSex().  Found an
 interesting quirk about the vanilla thought, though: it stacks once per pawn,
 so someone who sleeps around a lot will get many different identical
 thoughts. - find_pawn_to_fuck now ignores animals unless the pawn is a
 zoophile. - Attempts to exclude pawns whose Duty is TravelOrLeave so that
 pawns trying to leave the map don't turn around to bang.

---
 Source/Common/SexUtility.cs              |  8 ++++----
 Source/Common/xxx.cs                     |  9 ++++++---
 Source/JobDrivers/JobDriver_JoinInBed.cs |  2 +-
 Source/JobGivers/JobGiver_JoinInBed.cs   | 10 ++++++++++
 4 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/Source/Common/SexUtility.cs b/Source/Common/SexUtility.cs
index dcb663df3..3e9382718 100644
--- a/Source/Common/SexUtility.cs
+++ b/Source/Common/SexUtility.cs
@@ -360,7 +360,7 @@ namespace rjw
 			}
 
 			xxx.UpdateRecords(pawn, partner, sextype, rape, isCoreLovin);
-			Satisfy(pawn, partner, sextype, rape, isCoreLovin);
+			Satisfy(pawn, partner, sextype, rape);
 
 			CheckTraitGain(pawn);
 			CheckTraitGain(partner);
@@ -389,7 +389,7 @@ namespace rjw
 			//check_trait_gain(pawn);
 		}
 
-		private static void Satisfy(Pawn pawn, Pawn partner, xxx.rjwSextype sextype, bool rape = false, bool isCoreLovin = false)
+		private static void Satisfy(Pawn pawn, Pawn partner, xxx.rjwSextype sextype, bool rape = false)
 		{
 			//--Log.Message("xxx::satisfy( " + pawn_name + ", " + partner_name + ", " + violent + "," + isCoreLovin + " ) called");
 
@@ -698,8 +698,8 @@ namespace rjw
 			Aftersex(pawn, partner, rape, isCoreLovin, sextype);
 
 			//--Log.Message("SexUtility::processsex( " + pawn_name + ", " + partner_name + " ) - checking thoughts");
-			xxx.think_about_sex(pawn, partner, receiving == pawn, rape, sextype);
-			xxx.think_about_sex(partner, pawn, receiving == partner, rape, sextype);
+			xxx.think_about_sex(pawn, partner, receiving == pawn, rape, sextype, isCoreLovin);
+			xxx.think_about_sex(partner, pawn, receiving == partner, rape, sextype, isCoreLovin);
 
 			pawn.Drawer.Notify_MeleeAttackOn(partner);
 		}
diff --git a/Source/Common/xxx.cs b/Source/Common/xxx.cs
index c752d3434..152205778 100644
--- a/Source/Common/xxx.cs
+++ b/Source/Common/xxx.cs
@@ -1449,7 +1449,7 @@ namespace rjw
 
 		//violent - mark true when pawn rape partner
 		//Note: violent is not reliable, since either pawn could be the rapist. Check jobdrivers instead, they're still active since this is called before ending the job.
-		public static void think_about_sex(Pawn pawn, Pawn partner, bool isReceiving, bool violent = false, rjwSextype sextype = rjwSextype.None)
+		public static void think_about_sex(Pawn pawn, Pawn partner, bool isReceiving, bool violent = false, rjwSextype sextype = rjwSextype.None, bool isCoreLovin = false)
 		{
 			// Partner should never be null, but just in case something gets changed elsewhere..
 			if (partner == null)
@@ -1551,8 +1551,11 @@ namespace rjw
 			}
 			else if (is_human(partner))
 			{
-				// human partner and not part of rape or necrophilia.  add the vanilla GotSomeLovin thought
-				pawn.needs.mood.thoughts.memories.TryGainMemory(VanillaGotSomeLovin, partner);
+				if (!isCoreLovin)
+				{
+					// human partner and not part of rape or necrophilia.  add the vanilla GotSomeLovin thought
+					pawn.needs.mood.thoughts.memories.TryGainMemory(VanillaGotSomeLovin, partner);
+				}
 			}
 
 			//--Log.Message("xxx::think_about_sex( " + xxx.get_pawnname(pawn) + ", " + xxx.get_pawnname(partner) + ", " + violent + " ) - setting disease thoughts");
diff --git a/Source/JobDrivers/JobDriver_JoinInBed.cs b/Source/JobDrivers/JobDriver_JoinInBed.cs
index 11e513d23..c8a4134bf 100644
--- a/Source/JobDrivers/JobDriver_JoinInBed.cs
+++ b/Source/JobDrivers/JobDriver_JoinInBed.cs
@@ -68,7 +68,7 @@ namespace rjw
 				initAction = delegate
 				{
 					// Trying to add some interactions and social logs
-					SexUtility.ProcessSex(Top, Partner, false /*rape*/, true/*isCoreLovin*/, false /*whoring*/);
+					SexUtility.ProcessSex(Top, Partner, false /*rape*/, false/*isCoreLovin*/, false /*whoring*/);
 				},
 				defaultCompleteMode = ToilCompleteMode.Instant
 			};
diff --git a/Source/JobGivers/JobGiver_JoinInBed.cs b/Source/JobGivers/JobGiver_JoinInBed.cs
index 2b8f89293..7bdbcf482 100644
--- a/Source/JobGivers/JobGiver_JoinInBed.cs
+++ b/Source/JobGivers/JobGiver_JoinInBed.cs
@@ -35,6 +35,8 @@ namespace rjw
 			Pawn best_fuckee = null;
 			float best_distance = 1.0e6f;
 			bool pawnIsNympho = xxx.is_nympho(pawn);
+			bool pawnIsZoo = xxx.is_zoophile(pawn);
+
 			//Rand.PopState();
 			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 
@@ -48,6 +50,7 @@ namespace rjw
 				&& is_healthy(x) 
 				&& x.Map == pawn.Map 
 				&& !x.HostileTo(pawn)
+				&& (pawnIsZoo || !xxx.is_animal(x))
 				&& (xxx.is_laying_down_alone(x) || xxx.in_same_bed(x, pawn))
 				).ToList();
 
@@ -123,6 +126,13 @@ namespace rjw
 			if (!SexUtility.ReadyForLovin(pawn) && !xxx.is_frustrated(pawn))
 				return null;
 
+			// This check attempts to keep groups leaving the map, like guests or traders, from turning around to hook up
+			if (pawn.mindState?.duty?.def == DutyDefOf.TravelOrLeave)
+			{
+				Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(pawn) + " ) has TravelOrLeave, no time for lovin!");
+				return null;
+			}
+
 			if (pawn.CurJob == null || pawn.CurJob.def == JobDefOf.LayDown)
 			{
 				//--Log.Message("   checking pawn and abilities");
-- 
GitLab


From 9013c361274e94089fcbc090a6d13c224191a043 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Sun, 21 Jul 2019 23:51:33 -0700
Subject: [PATCH 05/15] - Don't add Got Some Lovin to whores while whoring.

---
 Source/Common/SexUtility.cs | 4 ++--
 Source/Common/xxx.cs        | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/Source/Common/SexUtility.cs b/Source/Common/SexUtility.cs
index 3e9382718..f1a653ed9 100644
--- a/Source/Common/SexUtility.cs
+++ b/Source/Common/SexUtility.cs
@@ -698,8 +698,8 @@ namespace rjw
 			Aftersex(pawn, partner, rape, isCoreLovin, sextype);
 
 			//--Log.Message("SexUtility::processsex( " + pawn_name + ", " + partner_name + " ) - checking thoughts");
-			xxx.think_about_sex(pawn, partner, receiving == pawn, rape, sextype, isCoreLovin);
-			xxx.think_about_sex(partner, pawn, receiving == partner, rape, sextype, isCoreLovin);
+			xxx.think_about_sex(pawn, partner, receiving == pawn, rape, sextype, isCoreLovin, whoring);
+			xxx.think_about_sex(partner, pawn, receiving == partner, rape, sextype, isCoreLovin, false);
 
 			pawn.Drawer.Notify_MeleeAttackOn(partner);
 		}
diff --git a/Source/Common/xxx.cs b/Source/Common/xxx.cs
index 152205778..59ebecb36 100644
--- a/Source/Common/xxx.cs
+++ b/Source/Common/xxx.cs
@@ -1449,7 +1449,7 @@ namespace rjw
 
 		//violent - mark true when pawn rape partner
 		//Note: violent is not reliable, since either pawn could be the rapist. Check jobdrivers instead, they're still active since this is called before ending the job.
-		public static void think_about_sex(Pawn pawn, Pawn partner, bool isReceiving, bool violent = false, rjwSextype sextype = rjwSextype.None, bool isCoreLovin = false)
+		public static void think_about_sex(Pawn pawn, Pawn partner, bool isReceiving, bool violent = false, rjwSextype sextype = rjwSextype.None, bool isCoreLovin = false, bool whoring = false)
 		{
 			// Partner should never be null, but just in case something gets changed elsewhere..
 			if (partner == null)
@@ -1551,7 +1551,7 @@ namespace rjw
 			}
 			else if (is_human(partner))
 			{
-				if (!isCoreLovin)
+				if (!isCoreLovin && !whoring)
 				{
 					// human partner and not part of rape or necrophilia.  add the vanilla GotSomeLovin thought
 					pawn.needs.mood.thoughts.memories.TryGainMemory(VanillaGotSomeLovin, partner);
-- 
GitLab


From 2ade5b8678d95e69b978772438faaf9d90524cbb Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Mon, 22 Jul 2019 08:53:30 -0700
Subject: [PATCH 06/15] Increase default vulnerability to 120% so that melee
 skill < 5 represents a penalty and melee skill 20 doesn't mean invulnerable.

---
 Defs/StatDefs/Stats.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Defs/StatDefs/Stats.xml b/Defs/StatDefs/Stats.xml
index 7e9ef21a4..21b13514f 100644
--- a/Defs/StatDefs/Stats.xml
+++ b/Defs/StatDefs/Stats.xml
@@ -55,7 +55,7 @@
 		<skillNeedFactors>
 			<li Class="rjw_CORE_EXPOSED.SkillNeed_BaseBonus">
 				<skill>Melee</skill>
-				<baseValue>1.00</baseValue>
+				<baseValue>1.25/baseValue>
 				<bonusPerLevel>-0.05</bonusPerLevel>
 			</li>
 		</skillNeedFactors>
-- 
GitLab


From 5fdba84e1af228cff14d8347ef0f4a82e61d1aa8 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Mon, 22 Jul 2019 23:46:43 -0700
Subject: [PATCH 07/15] Fix typo

---
 Defs/StatDefs/Stats.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Defs/StatDefs/Stats.xml b/Defs/StatDefs/Stats.xml
index 21b13514f..3c569d81f 100644
--- a/Defs/StatDefs/Stats.xml
+++ b/Defs/StatDefs/Stats.xml
@@ -55,7 +55,7 @@
 		<skillNeedFactors>
 			<li Class="rjw_CORE_EXPOSED.SkillNeed_BaseBonus">
 				<skill>Melee</skill>
-				<baseValue>1.25/baseValue>
+				<baseValue>1.25</baseValue>
 				<bonusPerLevel>-0.05</bonusPerLevel>
 			</li>
 		</skillNeedFactors>
-- 
GitLab


From 983fe2aeee506f76f6057fa830116ea8b77e3442 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Tue, 23 Jul 2019 08:58:07 -0700
Subject: [PATCH 08/15] Revert nonfunctional vulnerability fix and change it to
 3.5% per melee level instead of 5%.

---
 Defs/StatDefs/Stats.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Defs/StatDefs/Stats.xml b/Defs/StatDefs/Stats.xml
index 3c569d81f..ffbfeefbd 100644
--- a/Defs/StatDefs/Stats.xml
+++ b/Defs/StatDefs/Stats.xml
@@ -55,8 +55,8 @@
 		<skillNeedFactors>
 			<li Class="rjw_CORE_EXPOSED.SkillNeed_BaseBonus">
 				<skill>Melee</skill>
-				<baseValue>1.25</baseValue>
-				<bonusPerLevel>-0.05</bonusPerLevel>
+				<baseValue>1.00</baseValue>
+				<bonusPerLevel>-0.035</bonusPerLevel>
 			</li>
 		</skillNeedFactors>
 		<capacityFactors>
-- 
GitLab


From 97769a5f830338cd651013b45b0a99a5ec748e77 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Tue, 23 Jul 2019 08:59:23 -0700
Subject: [PATCH 09/15] -Give find_pawn_to_fuck hookups a chance to turn down
 offers using their own would_fuck() check. - Remove debug logs

---
 Source/JobGivers/JobGiver_JoinInBed.cs | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/Source/JobGivers/JobGiver_JoinInBed.cs b/Source/JobGivers/JobGiver_JoinInBed.cs
index 7bdbcf482..5862dde83 100644
--- a/Source/JobGivers/JobGiver_JoinInBed.cs
+++ b/Source/JobGivers/JobGiver_JoinInBed.cs
@@ -23,6 +23,10 @@ namespace rjw
 			float fuckability = xxx.would_fuck(pawn, target); // 0.0 to 1.0
 			if (fuckability < 0.1f) return false;
 
+			float reciprocity = xxx.would_fuck(target, pawn);
+			//Log.Message($"[RJW] roll_to_skip({xxx.get_pawnname(pawn)},{xxx.get_pawnname(target)}) fuckability ({fuckability},{reciprocity})");
+			if (reciprocity < 0.1f) return false;
+
 			float chance_to_skip = 0.9f - 0.7f * fuckability;
 			//Rand.PopState();
 			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
@@ -71,7 +75,7 @@ namespace rjw
 						&& pawn.CanReserveAndReach(target, PathEndMode.OnCell, Danger.Some, 1, 0)
 						&& target.CanReserve(pawn, 1, 0))
 					{
-						Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(pawn) + " ) found lover (" + xxx.get_pawnname(target) + ")");
+						//Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(pawn) + " ) found lover (" + xxx.get_pawnname(target) + ")");
 						return target;
 					}
 					//Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(target) + " ) partner cant be fucked right now");
@@ -111,7 +115,7 @@ namespace rjw
 
 			if (best_fuckee != null)
 			{
-				Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(pawn) + " ) found rando (" + xxx.get_pawnname(best_fuckee) + ")");
+				//Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(pawn) + " ) found rando (" + xxx.get_pawnname(best_fuckee) + ")");
 			}
 			return best_fuckee;
 		}
@@ -129,7 +133,7 @@ namespace rjw
 			// This check attempts to keep groups leaving the map, like guests or traders, from turning around to hook up
 			if (pawn.mindState?.duty?.def == DutyDefOf.TravelOrLeave)
 			{
-				Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(pawn) + " ) has TravelOrLeave, no time for lovin!");
+				//Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(pawn) + " ) has TravelOrLeave, no time for lovin!");
 				return null;
 			}
 
-- 
GitLab


From c3fdaf23831b9b116f11cbb99706089b2f85fa2f Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Wed, 24 Jul 2019 09:07:54 -0700
Subject: [PATCH 10/15] Change ArtificialPrivatePartBase to use HediffClass
 Hediff_AddedPart instead of Hediff_Implant.  This causes the Transhumanist
 and Body Purist traits to activate and love/hate their shiny new toys. 
 Tested adding/removing them via surgery and via dev tools.

---
 Defs/HediffDefs/Hediffs_PrivateParts/Hediffs_PrivateParts.xml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Defs/HediffDefs/Hediffs_PrivateParts/Hediffs_PrivateParts.xml b/Defs/HediffDefs/Hediffs_PrivateParts/Hediffs_PrivateParts.xml
index e423ac1c7..6c6953bdc 100644
--- a/Defs/HediffDefs/Hediffs_PrivateParts/Hediffs_PrivateParts.xml
+++ b/Defs/HediffDefs/Hediffs_PrivateParts/Hediffs_PrivateParts.xml
@@ -7,7 +7,7 @@
 	</HediffDef>
 
 	<HediffDef Name="ArtificialPrivatePartBase" Abstract="True">
-		<hediffClass>Hediff_Implant</hediffClass>
+		<hediffClass>Hediff_AddedPart</hediffClass>
 		<defaultLabelColor>(0.5, 0.5, 0.9)</defaultLabelColor>
 		<isBad>false</isBad>
 		<addedPartProps>
-- 
GitLab


From 56f85d98e82f6402608b37faf149bfbde69352b6 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Sat, 27 Jul 2019 00:12:22 -0700
Subject: [PATCH 11/15] Bug fixes: - xxx.reduce_rest() would crash if a pawn
 didn't have a rest need if you passed in a negative value for rest amount. -
 xxx.HasNonPolyPartner() returned true even when the partner was dead which
 caused an NRE when someone considered them for hookup because their Map was
 null.  I also made HasNonPolyPartner ignore Suspended pawns too.  They're in
 stasis, they'll never know... - xxx.would_fuck() now skips the orientation
 check when initiated by an animal. - CompRJW.CheckPreference() now always
 returns true instead of false when evaluating the orientation preference of
 futas. - Hookup improvement: now ignores animal partner's opinion. - Hookup
 improvement: now never considered an animal hookup to be homewrecking,
 avoiding a bunch of irrelevant checks. - Hookup improvement: now cares more
 about fuckability than distance.  the new formula is simply
 "fuckability/distance", with an automatic pass for a partner 2 squares away
 since they're likely in our current bed.

---
 Source/Common/xxx.cs                   | 18 +++++---
 Source/Comps/CompRJW.cs                |  6 +++
 Source/JobGivers/JobGiver_JoinInBed.cs | 59 +++++++++++++++++++++-----
 3 files changed, 66 insertions(+), 17 deletions(-)

diff --git a/Source/Common/xxx.cs b/Source/Common/xxx.cs
index 59ebecb36..d15cb82e0 100644
--- a/Source/Common/xxx.cs
+++ b/Source/Common/xxx.cs
@@ -524,10 +524,10 @@ namespace rjw
 			if (has_quirk(pawn, "Vigorous")) x -= 1;
 
 			Need_Rest need_rest = pawn.needs.TryGetNeed<Need_Rest>();
-			if (need_rest != null || x <= 0)
-			{
-				need_rest.CurLevel -= need_rest.RestFallPerTick * x;
-			}
+			if (need_rest == null)
+				return;
+
+			need_rest.CurLevel -= need_rest.RestFallPerTick * x;
 		}
 
 		public static float need_some_sex(Pawn pawn)
@@ -574,6 +574,9 @@ namespace rjw
 					continue;
 				}
 
+				if (relation.otherPawn.Dead || relation.otherPawn.Suspended)
+					continue;
+
 				if ((RomanceDiversifiedIsActive && relation.otherPawn.story.traits.HasTrait(polyamorous)) ||
 					(PsychologyIsActive && relation.otherPawn.story.traits.HasTrait(polygamous)))
 				{
@@ -1037,8 +1040,8 @@ namespace rjw
 				}
 			}
 			// Age not acceptable, automatic fail.
+			//Log.Message("would_fuck() - age_ok = " + age_ok.ToString());
 			if (!age_ok) return 0.0f;
-			//--Log.Message("would_fuck() - age_ok = " + age_ok.ToString());
 
 			float age_factor;
 
@@ -1068,13 +1071,16 @@ namespace rjw
 			{
     			orientation_factor = 1.0f;
 
-				if (!ignore_gender)
+				if (!ignore_gender && !is_animal(fucker))
 				{
 					if (is_asexual(fucker))
 						return 0.0f;
 
 					if (!CompRJW.CheckPreference(fucker, fucked))
+					{
+						//Log.Message("would_fuck() - preference fail");
 						return 0.0f;
+					}
 				}
 			}
 			//Log.Message("would_fuck() - orientation_factor = " + orientation_factor.ToString());
diff --git a/Source/Comps/CompRJW.cs b/Source/Comps/CompRJW.cs
index c8a3b444e..9e3bcaf19 100644
--- a/Source/Comps/CompRJW.cs
+++ b/Source/Comps/CompRJW.cs
@@ -347,6 +347,12 @@ namespace rjw
 			bool isHomo = (Genital_Helper.has_vagina(pawn) && Genital_Helper.has_vagina(partner)) ||
 						   ((Genital_Helper.has_penis(partner) || Genital_Helper.has_penis_infertile(partner)) && (Genital_Helper.has_penis(pawn) || Genital_Helper.has_penis_infertile(pawn)));
 
+			if (isHetero && isHomo)
+			{
+				// Oh you crazy futas.  We could probably do a check against the pawn's gender, but eh.  They've got so many parts available, they'll find something to do.
+				return true;
+			}
+
 			//Rand.PopState();
 			//Rand.PushState(RJW_Multiplayer.PredictableSeed());
 			switch (ori)
diff --git a/Source/JobGivers/JobGiver_JoinInBed.cs b/Source/JobGivers/JobGiver_JoinInBed.cs
index 5862dde83..f2a4500b9 100644
--- a/Source/JobGivers/JobGiver_JoinInBed.cs
+++ b/Source/JobGivers/JobGiver_JoinInBed.cs
@@ -18,14 +18,15 @@ namespace rjw
 		}
 
 		[SyncMethod]
-		private static bool roll_to_skip(Pawn pawn, Pawn target)
+		private static bool roll_to_skip(Pawn pawn, Pawn target, out float fuckability)
 		{
-			float fuckability = xxx.would_fuck(pawn, target); // 0.0 to 1.0
+			fuckability = xxx.would_fuck(pawn, target); // 0.0 to 1.0
 			if (fuckability < 0.1f) return false;
 
-			float reciprocity = xxx.would_fuck(target, pawn);
+			float reciprocity = xxx.is_animal(target) ? 1.0f : xxx.would_fuck(target, pawn);
 			//Log.Message($"[RJW] roll_to_skip({xxx.get_pawnname(pawn)},{xxx.get_pawnname(target)}) fuckability ({fuckability},{reciprocity})");
-			if (reciprocity < 0.1f) return false;
+			if (fuckability < 0.1f || reciprocity < 0.1f)
+				return false;
 
 			float chance_to_skip = 0.9f - 0.7f * fuckability;
 			//Rand.PopState();
@@ -36,8 +37,10 @@ namespace rjw
 		[SyncMethod]
 		public static Pawn find_pawn_to_fuck(Pawn pawn, Map map)
 		{
+			//Log.Message($"[RJW] find_pawn_to_fuck: starting for pawn {xxx.get_pawnname(pawn)}");
+
 			Pawn best_fuckee = null;
-			float best_distance = 1.0e6f;
+			float best_fuckability_score = 0;
 			bool pawnIsNympho = xxx.is_nympho(pawn);
 			bool pawnIsZoo = xxx.is_zoophile(pawn);
 
@@ -58,6 +61,8 @@ namespace rjw
 				&& (xxx.is_laying_down_alone(x) || xxx.in_same_bed(x, pawn))
 				).ToList();
 
+			//Log.Message($"[RJW] find_pawn_to_fuck: considering {targets.Count} targets");
+
 			// find lover/partner on same map
 			List<Pawn> partners = targets.Where(x 
 				=> pawn.relations.DirectRelationExists(PawnRelationDefOf.Lover, x) 
@@ -65,12 +70,15 @@ namespace rjw
 				|| pawn.relations.DirectRelationExists(PawnRelationDefOf.Spouse, x)
 				).ToList();
 
+			//Log.Message($"[RJW] find_pawn_to_fuck: considering {partners.Count} partners");
+
 			if (partners.Any())
 			{
 				partners.Shuffle(); //Randomize order.
 				foreach (Pawn target in partners)
 				{
-					//Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(target) + " ) partner found");
+					//Log.Message($"[RJW] find_pawn_to_fuck: checking partner {xxx.get_pawnname(target)}");
+
 					if (pawn.Position.DistanceToSquared(target.Position) < MaxDistanceSquaredToFuck
 						&& pawn.CanReserveAndReach(target, PathEndMode.OnCell, Danger.Some, 1, 0)
 						&& target.CanReserve(pawn, 1, 0))
@@ -82,11 +90,13 @@ namespace rjw
 				}
 			}
 
+			//Log.Message($"[RJW] find_pawn_to_fuck: checking canHookup");
 			// No lovers around... see if the pawn fancies a hookup.  Nymphos and frustrated pawns always do!
 			bool canHookup = pawnIsNympho || xxx.is_frustrated(pawn) || (xxx.is_horny(pawn) && Rand.Value > NonNymphoHookupChance);
 			if (!canHookup)
 				return null;
 
+			//Log.Message($"[RJW] find_pawn_to_fuck: checking canHookupWithoutCheating");
 			// No cheating from casual hookups... would probably make colony relationship management too annoying
 			Pawn pawnBestLover = LovePartnerRelationUtility.ExistingMostLikedLovePartner(pawn, false /*allowDead*/);
 			bool canHookupWithoutCheating = !xxx.HasNonPolyPartner(pawn) || pawnBestLover == null || pawnBestLover.Map != pawn.Map;
@@ -95,27 +105,54 @@ namespace rjw
 
 			foreach (Pawn targetPawn in targets)
 			{
+				//Log.Message($"[RJW] find_pawn_to_fuck: checking hookup {xxx.get_pawnname(targetPawn)}");
 				// No homewrecking either!
-				if (xxx.HasNonPolyPartner(targetPawn) && 
+				if (!xxx.is_animal(targetPawn) &&
+					xxx.HasNonPolyPartner(targetPawn) &&
 					LovePartnerRelationUtility.ExistingMostLikedLovePartner(targetPawn, false /*allowDead*/).Map == pawn.Map)
+				{
 					continue;
+				}
 
+				//Log.Message($"[RJW] find_pawn_to_fuck: hookup {xxx.get_pawnname(targetPawn)} is sufficiently single");
+				float fuckability = 0f;
 				if (pawn.CanReserveAndReach(targetPawn, PathEndMode.OnCell, Danger.Some, 1, 0) &&
 					targetPawn.CanReserve(pawn, 1, 0) &&
-					roll_to_skip(pawn, targetPawn))
+					roll_to_skip(pawn, targetPawn, out fuckability))
 				{
 					int dis = pawn.Position.DistanceToSquared(targetPawn.Position);
-					if (dis < best_distance && dis < MaxDistanceSquaredToFuck)
+
+					if (dis <= 4)
 					{
+						// Right next to me (in my bed)?  You'll do.
+						//Log.Message($"[RJW] find_pawn_to_fuck: hookup {xxx.get_pawnname(targetPawn)} is right next to me.  we'll bang, ok?");
+						best_fuckability_score = 1.0e6f;
 						best_fuckee = targetPawn;
-						best_distance = dis;
+					}
+					else if (dis > MaxDistanceSquaredToFuck)
+					{
+						// too far
+						//Log.Message($"[RJW] find_pawn_to_fuck: hookup {xxx.get_pawnname(targetPawn)} is too far... distance:{dis} max:{MaxDistanceSquaredToFuck}");
+						continue;
+					}
+					else
+					{
+						// scaling fuckability by distance may give us more varied results and give the less attractive folks a chance
+						float fuckability_score = fuckability / GenMath.Sqrt(dis);
+						//Log.Message($"[RJW] find_pawn_to_fuck: hookup {xxx.get_pawnname(targetPawn)} is totally bangable.  score:{fuckability_score}");
+
+						if (fuckability_score > best_fuckability_score)
+						{
+							best_fuckee = targetPawn;
+							best_fuckability_score = fuckability_score;
+						}
 					}
 				}
 			}
 
 			if (best_fuckee != null)
 			{
-				//Log.Message("[RJW] find_pawn_to_fuck( " + xxx.get_pawnname(pawn) + " ) found rando (" + xxx.get_pawnname(best_fuckee) + ")");
+				//Log.Message($"[RJW] find_pawn_to_fuck({xxx.get_pawnname(pawn)}) found rando {xxx.get_pawnname(best_fuckee)} with score {best_fuckability_score}");
 			}
 			return best_fuckee;
 		}
-- 
GitLab


From 9f87eceee7da038a5dbd917ba3de9b6ee3337a05 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Sat, 27 Jul 2019 09:57:27 -0700
Subject: [PATCH 12/15] Humpshroom value 80->10, drug 500->50, growdays up to
 15, removed untested wildClusterRadius, set back to hydroponic only

---
 Defs/ThingDefs/Drugs.xml | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/Defs/ThingDefs/Drugs.xml b/Defs/ThingDefs/Drugs.xml
index 2452f0099..cc788802b 100644
--- a/Defs/ThingDefs/Drugs.xml
+++ b/Defs/ThingDefs/Drugs.xml
@@ -19,13 +19,12 @@
 		<plant>
 			<fertilityMin>0.01</fertilityMin>
 			<fertilitySensitivity>0.25</fertilitySensitivity>
-			<growDays>10.00</growDays>
+			<growDays>15.00</growDays>
 			<harvestTag>Standard</harvestTag>
 			<harvestedThingDef>HumpShroom</harvestedThingDef>
 			<harvestYield>2</harvestYield>
 			<sowMinSkill>4</sowMinSkill>
 			<sowTags>
-				<li>Ground</li>
 				<li>Hydroponic</li>
 			</sowTags>
 			<topWindExposure>0.1</topWindExposure>
@@ -33,8 +32,6 @@
 				<min>0.3</min>
 				<max>1.0</max>
 			</visualSizeRange>
-			<wildClusterRadius>3</wildClusterRadius>
-			<wildClusterWeight>5</wildClusterWeight>
 		</plant>
 		<comps>
 			<li Class="CompProperties_Glower">
@@ -56,7 +53,7 @@
 		<graphicClass>Graphic_Single</graphicClass>
 		</graphicData>
 		<statBases>
-			<MarketValue>80</MarketValue>
+			<MarketValue>10</MarketValue>
 			<Mass>0.1</Mass>
 			<DeteriorationRate>4</DeteriorationRate>
 			<Nutrition>0.40</Nutrition>
@@ -289,7 +286,7 @@
 		<rotatable>false</rotatable>
 		<statBases>
 			<WorkToMake>450</WorkToMake>
-			<MarketValue>500</MarketValue>
+			<MarketValue>50</MarketValue>
 			<Mass>0.05</Mass>
 			<DeteriorationRate>6</DeteriorationRate>
 			<Flammability>1.3</Flammability>
-- 
GitLab


From b0f513036b143f63544085a4bb03208919ad9621 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Sun, 28 Jul 2019 17:56:34 -0700
Subject: [PATCH 13/15] Whores were trying to serve downed pawns, though they
 aborted when they got all the way there and saw they were down.  I fixed it
 by consolidating a couple of IsDowned and IsSuspended checks into
 xxx.is_healthy() and is_healthy_enough().

---
 Source/Common/xxx.cs                                     | 8 +++++++-
 Source/JobGivers/JobGiver_JoinInBed.cs                   | 9 ++++-----
 .../JobDrivers/JobDriver_WhoreInvitingVisitors.cs        | 2 +-
 .../Whoring/JobGivers/JobGiver_WhoreInvitingVisitors.cs  | 3 +--
 Source/Modules/Whoring/Whoring_Helper.cs                 | 5 -----
 5 files changed, 13 insertions(+), 14 deletions(-)

diff --git a/Source/Common/xxx.cs b/Source/Common/xxx.cs
index d15cb82e0..42925eb6a 100644
--- a/Source/Common/xxx.cs
+++ b/Source/Common/xxx.cs
@@ -427,6 +427,8 @@ namespace rjw
 		public static bool is_healthy(Pawn pawn)
 		{
 			return !pawn.Dead &&
+				!pawn.Suspended &&
+				!pawn.Downed &&
 				pawn.health.capacities.CanBeAwake &&
 				pawn.health.hediffSet.BleedRateTotal <= 0.0f &&
 				pawn.health.hediffSet.PainTotal < config.significant_pain_threshold;
@@ -434,7 +436,11 @@ namespace rjw
 
 		public static bool is_healthy_enough(Pawn pawn)
 		{
-			return !pawn.Dead && pawn.health.capacities.CanBeAwake && pawn.health.hediffSet.BleedRateTotal <= 0.1f;
+			return !pawn.Dead &&
+				!pawn.Suspended &&
+				!pawn.Downed &&
+				pawn.health.capacities.CanBeAwake &&
+				pawn.health.hediffSet.BleedRateTotal <= 0.1f;
 		}
 
 		public static bool is_not_dying(Pawn pawn)
diff --git a/Source/JobGivers/JobGiver_JoinInBed.cs b/Source/JobGivers/JobGiver_JoinInBed.cs
index f2a4500b9..cf03aa796 100644
--- a/Source/JobGivers/JobGiver_JoinInBed.cs
+++ b/Source/JobGivers/JobGiver_JoinInBed.cs
@@ -12,9 +12,9 @@ namespace rjw
 		private const float NonNymphoHookupChance = 0.3f;
 		private const int MaxDistanceSquaredToFuck = 10000;
 
-		private static bool is_healthy(Pawn target)
+		private static bool CanFuck(Pawn target)
 		{
-			return xxx.is_healthy(target) && (xxx.can_fuck(target) || xxx.can_be_fucked(target));
+			return xxx.can_fuck(target) || xxx.can_be_fucked(target);
 		}
 
 		[SyncMethod]
@@ -52,9 +52,8 @@ namespace rjw
 				=> x.InBed()
 				&& x != pawn
 				&& !x.Position.IsForbidden(pawn) 
-				&& !x.Suspended 
-				&& !x.Downed 
-				&& is_healthy(x) 
+				&& xxx.is_healthy(x) 
+				&& CanFuck(x)
 				&& x.Map == pawn.Map 
 				&& !x.HostileTo(pawn)
 				&& (pawnIsZoo || !xxx.is_animal(x))
diff --git a/Source/Modules/Whoring/JobDrivers/JobDriver_WhoreInvitingVisitors.cs b/Source/Modules/Whoring/JobDrivers/JobDriver_WhoreInvitingVisitors.cs
index 375238dca..b75ccbb61 100644
--- a/Source/Modules/Whoring/JobDrivers/JobDriver_WhoreInvitingVisitors.cs
+++ b/Source/Modules/Whoring/JobDrivers/JobDriver_WhoreInvitingVisitors.cs
@@ -61,7 +61,7 @@ namespace rjw
 			yield return Toils_Goto.GotoThing(TargetPawnIndex, PathEndMode.Touch);
 
 			Toil TryItOn = new Toil();
-			TryItOn.AddFailCondition(() => !WhoringHelper.IsTargetPawnOkay(TargetPawn));
+			TryItOn.AddFailCondition(() => !xxx.is_healthy(TargetPawn));
 			TryItOn.defaultCompleteMode = ToilCompleteMode.Delay;
 			TryItOn.initAction = delegate
 			{
diff --git a/Source/Modules/Whoring/JobGivers/JobGiver_WhoreInvitingVisitors.cs b/Source/Modules/Whoring/JobGivers/JobGiver_WhoreInvitingVisitors.cs
index 03e549a81..90540c35c 100644
--- a/Source/Modules/Whoring/JobGivers/JobGiver_WhoreInvitingVisitors.cs
+++ b/Source/Modules/Whoring/JobGivers/JobGiver_WhoreInvitingVisitors.cs
@@ -122,12 +122,11 @@ namespace rjw
 			potentialClients = potentialClients.Where(x
 				=> x != whore
 				&& !x.IsForbidden(whore)
-				&& !x.Suspended
 				&& !x.HostileTo(whore)
 				&& !x.IsPrisoner
 				&& x.Position.DistanceToSquared(pos) < max_distance_whore 
 				&& whore.CanReserveAndReach(x, PathEndMode.ClosestTouch, Danger.Some, 1) 
-				&& xxx.is_healthy_enough(x));
+				&& xxx.is_healthy(x));
 
 			potentialClients = potentialClients.Except(potentialClients.Where(client.TraitCheckFail));
 
diff --git a/Source/Modules/Whoring/Whoring_Helper.cs b/Source/Modules/Whoring/Whoring_Helper.cs
index 1e294e567..d3699998d 100644
--- a/Source/Modules/Whoring/Whoring_Helper.cs
+++ b/Source/Modules/Whoring/Whoring_Helper.cs
@@ -267,11 +267,6 @@ namespace rjw
 			return AmountLeft;
 		}
 
-		public static bool IsTargetPawnOkay(Pawn pawn)
-		{
-			return xxx.is_healthy(pawn) && !pawn.Downed && !pawn.Suspended;
-		}
-
 		[SyncMethod]
 		public static bool IsHookupAppealing(Pawn target, Pawn whore)
 		{
-- 
GitLab


From 4d3e8202321bc1268c38f354377258ac4f008ab9 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Sun, 28 Jul 2019 18:11:15 -0700
Subject: [PATCH 14/15] Casual sex: Don't consider targets who've recently
 banged and who aren't horny.

---
 Source/JobGivers/JobGiver_JoinInBed.cs | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/Source/JobGivers/JobGiver_JoinInBed.cs b/Source/JobGivers/JobGiver_JoinInBed.cs
index cf03aa796..c93c291d1 100644
--- a/Source/JobGivers/JobGiver_JoinInBed.cs
+++ b/Source/JobGivers/JobGiver_JoinInBed.cs
@@ -110,6 +110,13 @@ namespace rjw
 					xxx.HasNonPolyPartner(targetPawn) &&
 					LovePartnerRelationUtility.ExistingMostLikedLovePartner(targetPawn, false /*allowDead*/).Map == pawn.Map)
 				{
+					//Log.Message($"[RJW] find_pawn_to_fuck: not hooking up with {xxx.get_pawnname(targetPawn)} to avoid homewrecking");
+					continue;
+				}
+
+				if (!SexUtility.ReadyForLovin(targetPawn) && !xxx.is_horny(targetPawn))
+				{
+					//Log.Message($"[RJW] find_pawn_to_fuck: hookup {xxx.get_pawnname(targetPawn)} isn't ready for lovin'");
 					continue;
 				}
 
-- 
GitLab


From 45d99d144a3f4a915806b0cfdad7eff8eabcc805 Mon Sep 17 00:00:00 2001
From: randomtyping <abuse@localhost.com>
Date: Mon, 29 Jul 2019 17:40:23 -0700
Subject: [PATCH 15/15] Add comments better explaining the logic of
 xxx.HasNonPolyPartner().

---
 Source/Common/xxx.cs | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/Source/Common/xxx.cs b/Source/Common/xxx.cs
index 42925eb6a..1eaf83f50 100644
--- a/Source/Common/xxx.cs
+++ b/Source/Common/xxx.cs
@@ -563,14 +563,19 @@ namespace rjw
 			return need_sex.CurLevel < need_sex.thresh_horny();
 		}
 
+		/// <summary> Checks to see if the pawn has any partners who don't have a Polyamorous/Polygamous trait; aka someone who'd get mad about sleeping around. </summary>
+		/// <returns> True if the pawn has at least one romantic partner who does not have a poly trait. False if no partners or all partners are poly. </returns>
 		public static bool HasNonPolyPartner(Pawn pawn)
 		{
+			// If they don't have a partner at all we can bail right away.
 			if (!LovePartnerRelationUtility.HasAnyLovePartner(pawn))
 				return false;
 
+			// They have a partner, and if they don't have one of the mods that adds a poly trait then their partner must be non-poly.
 			if (!RomanceDiversifiedIsActive && !PsychologyIsActive)
 				return true;
 
+			// They have a partner and a mod that adds a poly trait, so check each partner to see if they're poly.
 			foreach (DirectPawnRelation relation in pawn.relations.DirectRelations)
 			{
 				if (relation.def != PawnRelationDefOf.Lover &&
@@ -580,17 +585,22 @@ namespace rjw
 					continue;
 				}
 
+				// Dead partners don't count.  And stasis'd partners will never find out!
 				if (relation.otherPawn.Dead || relation.otherPawn.Suspended)
 					continue;
 
 				if ((RomanceDiversifiedIsActive && relation.otherPawn.story.traits.HasTrait(polyamorous)) ||
 					(PsychologyIsActive && relation.otherPawn.story.traits.HasTrait(polygamous)))
 				{
+					// We have a partner who has the poly trait!  But they could have multiple partners so keep checking.
 					continue;
 				}
 
+				// We found a partner who doesn't have a poly trait.
 				return true;
 			}
+
+			// If we got here then we checked every partner and all of them had a poly trait, so they don't have a non-poly partner.
 			return false;
 		}
 
-- 
GitLab