code:
<?xml version="1.0" encoding="utf-8"?>
<Defs>
	<InteractionDef>
		<defName>BreedingWorkInviteFemale</defName>
		presenting to be bred
		<interactionMote>Mote_Thought</interactionMote>
		<symbol>Things/Mote/SpeechSymbols/NuzzleSexInitiate</symbol>
		<logRulesInitiator>
			<rulesStrings>

r_logentry -> [INITIATOR_nameDef] knelt before [RECIPIENT_nameDef], spreading her slit with one hand while stroking his growing shaft with the other.

r_logentry -> She rubbed [RECIPIENT_nameDef]'s cock until it stood ready, then bent over and pulled her cheeks apart for him.

r_logentry -> Spreading herself on all fours, [INITIATOR_nameDef] parted her slit and let his tongue explore her, waiting for him to mount.

r_logentry -> Obediently, [INITIATOR_nameDef] knelt and presented her slit to [RECIPIENT_nameDef], waiting.

			</rulesStrings>
		</logRulesInitiator>
	</InteractionDef>

	<InteractionDef>
		<defName>BreedingWorkInviteForcedFemale</defName>
		presenting to be bred
		<interactionMote>Mote_Thought</interactionMote>
		<symbol>Things/Mote/SpeechSymbols/BreedWorkRequest</symbol>
		<logRulesInitiator>
			<rulesStrings>

r_logentry -> [INITIATOR_nameDef] knelt, shaking, before [RECIPIENT_nameDef], forcing her slit open with trembling fingers.

r_logentry -> With a quiet sob, [INITIATOR_nameDef] bent over, spreading her cheeks stiffly as [RECIPIENT_nameDef]'s cock pressed closer.

r_logentry -> [INITIATOR_nameDef] got on all fours with tears in her eyes and shakily spread her legs, exposing her slit to [RECIPIENT_nameDef].

r_logentry -> [INITIATOR_nameDef] lay back, legs shaking as she spread her slit for [RECIPIENT_nameDef], her face turned away.

			</rulesStrings>
		</logRulesInitiator>
	</InteractionDef>

	<InteractionDef>
		<defName>BreedingWorkInviteMale</defName>
		preparing to breed an animal
		<interactionMote>Mote_Thought</interactionMote>
		<symbol>Things/Mote/SpeechSymbols/NuzzleSexInitiate</symbol>
		<logRulesInitiator>
			<rulesStrings>

r_logentry -> [INITIATOR_nameDef] stood behind [RECIPIENT_nameDef], stroking his cock with one hand while spreading her slit with the other.

r_logentry -> [INITIATOR_nameDef] grabbed [RECIPIENT_nameDef]'s hindquarters, parting her slit with his fingers while jerking his shaft.

r_logentry -> [INITIATOR_nameDef] positioned [RECIPIENT_nameDef], teasing her vaginal slit with his cock’s tip while stroking himself to hardness.

r_logentry -> [INITIATOR_nameDef] steadied [RECIPIENT_nameDef]'s flanks, pressing his cock against her slit and rubbing it to prepare her.

			</rulesStrings>
		</logRulesInitiator>
	</InteractionDef>

	<InteractionDef>
		<defName>BreedingWorkInviteForcedMale</defName>
		preparing to breed an animal
		<interactionMote>Mote_Thought</interactionMote>
		<symbol>Things/Mote/SpeechSymbols/BreedWorkRequest</symbol>
		<logRulesInitiator>
			<rulesStrings>

r_logentry -> [INITIATOR_nameDef] moved behind [RECIPIENT_nameDef]’s hindquarters, trembling as he rubbed his limp cock and pressed its tip shakily against her slit, sighing in relief when it hardened.

r_logentry -> With a scowl, [INITIATOR_nameDef] stood behind [RECIPIENT_nameDef], muttering curses while parting her slit with trembling fingers.

r_logentry -> [INITIATOR_nameDef] positioned [RECIPIENT_nameDef], grimacing as he forced his cock’s tip against her slit.

r_logentry -> [INITIATOR_nameDef] steadied [RECIPIENT_nameDef]’s flanks, clenching his fists in resentment as he rubbed his cock against her slit, muttering under his breath.

r_logentry -> [INITIATOR_nameDef] moved behind [RECIPIENT_nameDef], shuddering as he stroked his soft cock and fingered her slit, sighing when his shaft finally hardened.

r_logentry -> With a grimace, [INITIATOR_nameDef] grabbed [RECIPIENT_nameDef]’s hindquarters, resentfully teasing her slit with his cock’s tip, cursing his task.

r_logentry -> [INITIATOR_nameDef] stood behind [RECIPIENT_nameDef], trembling as he pressed his hard cock against her slit, grateful it was ready.

r_logentry -> [INITIATOR_nameDef] positioned [RECIPIENT_nameDef]’s flanks, scowling as he rubbed his cock and parted her slit with a shaking hand, turning his head away.

			</rulesStrings>
		</logRulesInitiator>
	</InteractionDef>

	<InteractionDef>
		<defName>BreedingWorkAnimalResponseFemale</defName>
		bred by marked animal
		<interactionMote>Mote_Thought</interactionMote>
		<symbol>Things/Mote/SpeechSymbols/NuzzleSex</symbol>
		<logRulesInitiator>
			<rulesStrings>

r_logentry -> [INITIATOR_nameDef] lowered his head and sniffed [RECIPIENT_nameDef] eagerly, his cock already stiffening beneath him.

r_logentry -> [INITIATOR_nameDef] gave a low growl and nudged her hips with his snout, ready to mount.

r_logentry -> [INITIATOR_nameDef] licked along [RECIPIENT_nameDef]'s exposed slit, then climbed onto her back.

r_logentry -> [INITIATOR_nameDef] pawed at the ground, then placed his front legs over [RECIPIENT_nameDef]'s back, accepting her invitation.

			</rulesStrings>
		</logRulesInitiator>
	</InteractionDef>

	<InteractionDef>
		<defName>BreedingWorkAnimalResponseMale</defName>
		breeding marked animal
		<interactionMote>Mote_Thought</interactionMote>
		<symbol>Things/Mote/SpeechSymbols/NuzzleSex</symbol>
		<logRulesInitiator>
			<rulesStrings>

r_logentry -> Already behind [INITIATOR_nameDef], [RECIPIENT_nameDef] pressed his erect cock against her hindquarters to begin breeding.

r_logentry -> Standing behind [INITIATOR_nameDef]’s hindquarters, [RECIPIENT_nameDef] guided his cock toward her rear to start the act.

r_logentry -> Positioned behind [INITIATOR_nameDef], [RECIPIENT_nameDef] aligned his erect shaft with her hindquarters for breeding.

r_logentry -> Behind [INITIATOR_nameDef]’s flanks, [RECIPIENT_nameDef] steadied her and pressed his cock against her rear to initiate mating.

			</rulesStrings>
		</logRulesInitiator>
	</InteractionDef>

	<InteractionDef>
		<defName>BreedingWorkAnimalResponseFemaleForced</defName>
		bred by marked animal
		<interactionMote>Mote_Thought</interactionMote>
		<symbol>Things/Mote/SpeechSymbols/NuzzleSexForced</symbol>
		<logRulesInitiator>
			<rulesStrings>

r_logentry -> [INITIATOR_nameDef] lowered his head and sniffed [RECIPIENT_nameDef] eagerly, his cock already stiffening beneath him.

r_logentry -> [INITIATOR_nameDef] gave a low growl and nudged her hips with his snout, ready to mount.

r_logentry -> [INITIATOR_nameDef] licked along [RECIPIENT_nameDef]'s exposed slit, then climbed onto her back.

r_logentry -> [INITIATOR_nameDef] pawed at the ground, then placed his front legs over [RECIPIENT_nameDef]'s back, accepting her invitation.

			</rulesStrings>
		</logRulesInitiator>
	</InteractionDef>

	<InteractionDef>
		<defName>BreedingWorkAnimalResponseMaleForced</defName>
		breeding marked animal
		<interactionMote>Mote_Thought</interactionMote>
		<symbol>Things/Mote/SpeechSymbols/NuzzleSexForced</symbol>
		<logRulesInitiator>
			<rulesStrings>

r_logentry -> Already behind [INITIATOR_nameDef], [RECIPIENT_nameDef] pressed his erect cock against her hindquarters to begin breeding.

r_logentry -> Standing behind [INITIATOR_nameDef]’s hindquarters, [RECIPIENT_nameDef] guided his cock toward her rear to start the act.

r_logentry -> Positioned behind [INITIATOR_nameDef], [RECIPIENT_nameDef] aligned his erect shaft with her hindquarters for breeding.

r_logentry -> Behind [INITIATOR_nameDef]’s flanks, [RECIPIENT_nameDef] steadied her and pressed his cock against her rear to initiate mating.

			</rulesStrings>
		</logRulesInitiator>
	</InteractionDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>
	<JobDef>
		<defName>TrainInterspeciesSex</defName>
		<driverClass>ElToro_BAddon.JobDriver_TrainInterspeciesSex</driverClass>
		<reportString>animal sex training</reportString>
		<casualInterruptible>false</casualInterruptible>
	</JobDef>
</Defs>

repo_name: ElToro/rjw

license: unknown


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>
	<JobDef>
		<defName>AnimalBreedInvite</defName>
		<driverClass>ElToro_BAddon.JobDriver_AnimalBreedInvite</driverClass>
		<reportString>presenting to be bred</reportString>
		<casualInterruptible>true</casualInterruptible>
	</JobDef>
	<JobDef>
		<defName>AnimalBreedInviteForced</defName>
		<driverClass>ElToro_BAddon.JobDriver_AnimalBreedInviteForced</driverClass>
		<reportString>presenting to be bred</reportString>
		<casualInterruptible>true</casualInterruptible>
	</JobDef>
	<JobDef>
		<defName>BreedingWorkSexAnimalInitiate</defName>
		<driverClass>ElToro_BAddon.JobDriver_BreedingWorkSexAnimalInitiate</driverClass>
		<reportString>mating</reportString>
		<casualInterruptible>false</casualInterruptible>
	</JobDef>
	<JobDef>
		<defName>BreedingWorkRapeAnimalInitiate</defName>
		<driverClass>ElToro_BAddon.JobDriver_BreedingWorkRapeAnimalInitiate</driverClass>
		<reportString>breeding</reportString>
		<casualInterruptible>false</casualInterruptible>
	</JobDef>
	<JobDef>
		<defName>AnimalBreedInviteForcedMale</defName>
		<driverClass>ElToro_BAddon.JobDriver_AnimalBreedInviteForced</driverClass>
		<reportString>preparing to breed animal</reportString>
		<casualInterruptible>true</casualInterruptible>
	</JobDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>
	<JobDef>
		<defName>SexInitiatorAnimalSex</defName>
		<driverClass>ElToro_BAddon.JobDriver_SexInitiatorAnimalSex</driverClass>
		<reportString>mating a human</reportString>
		<casualInterruptible>false</casualInterruptible>
	</JobDef>
	<JobDef>
		<defName>SexRecieverAnimalSex</defName>
		<driverClass>ElToro_BAddon.JobDriver_SexRecieverAnimalSex</driverClass>
		<reportString>mating</reportString>
		<casualInterruptible>false</casualInterruptible>
		<isCrawlingIfDowned>false</isCrawlingIfDowned>
	</JobDef>
	<JobDef>
		<defName>ForcedBreeding</defName>
		<driverClass>ElToro_BAddon.JobDriver_ForcedBreeding</driverClass>
		<reportString>breeding a human</reportString>
		<casualInterruptible>false</casualInterruptible>
	</JobDef>
	<JobDef>
		<defName>ForcedBreedingWaiting</defName>
		<driverClass>ElToro_BAddon.JobDriver_ForcedBreedingWaiting</driverClass>
		<reportString>breeding a human</reportString>
		<casualInterruptible>false</casualInterruptible>
	</JobDef>
	<JobDef>
		<defName>ForcedBreedingReciever</defName>
		<driverClass>rjw.JobDriver_SexBaseRecieverRaped</driverClass>
		<reportString>breeding</reportString>
		<checkOverrideOnDamage>Never</checkOverrideOnDamage>
		<casualInterruptible>false</casualInterruptible>
		<isCrawlingIfDowned>false</isCrawlingIfDowned>
	</JobDef>
	<JobDef>
		<defName>ForcedBreedingRecieverMale</defName>
		<driverClass>rjw.JobDriver_SexBaseRecieverRaped</driverClass>
		<reportString>forced to breed an animal</reportString>
		<checkOverrideOnDamage>Never</checkOverrideOnDamage>
		<casualInterruptible>false</casualInterruptible>
		<isCrawlingIfDowned>false</isCrawlingIfDowned>
	</JobDef>
	<JobDef>
		<defName>InterspiciesMatingInteraction</defName>
		<driverClass>ElToro_BAddon.JobDriver_InterspiciesMatingInteraction</driverClass>
		<reportString>approaching human for mating</reportString>
		<casualInterruptible>false</casualInterruptible>
	</JobDef>
	<JobDef>
		<defName>InterspiciesMatingInteractionAccept</defName>
		<driverClass>ElToro_BAddon.JobDriver_InterspiciesMatingInteractionAccept</driverClass>
		<reportString>moving to mate with human</reportString>
		<casualInterruptible>false</casualInterruptible>
	</JobDef>
	<JobDef>
		<defName>InterspiciesMatingInteractionDecline</defName>
		<driverClass>ElToro_BAddon.JobDriver_InterspiciesMatingInteractionDecline</driverClass>
		<reportString>walking away from mating attempt</reportString>
		<casualInterruptible>false</casualInterruptible>
	</JobDef>
</Defs>


code:
<Defs>
  <PawnColumnDef>
    <defName>RJW_ForcedBreedingAnimal</defName>
	<headerTip>Mark this animal for forced breeding.

If the Breeding animals precept is selected, manual designation will be disabled.
    </headerTip>
    <headerIcon>UI/Issues/BreedWork</headerIcon>
    <workerClass>ElToro_BAddon.PawnColumnWorker_ForcedBreedingAnimal</workerClass>
    <sortable>true</sortable>
    <width>80</width>
  </PawnColumnDef>
  <PawnColumnDef>
    <defName>RJW_ForcedBreedingHuman</defName>
	<headerTip>Mark this pawn for forced breeding.

If the any of Breeding duty precept are selected, manual designation will be disabled.
    </headerTip>
    <headerIcon>UI/Issues/BreedWork</headerIcon>
    <workerClass>ElToro_BAddon.PawnColumnWorker_ForcedBreedingHuman</workerClass>
    <sortable>true</sortable>
    <width>80</width>
  </PawnColumnDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8"?>
<Defs>
<PawnRelationDef>
	<defName>PreferredMatingPartner</defName>
	preferred mating partner
	<importance>20</importance>
	<implied>false</implied>
	<reflexive>false</reflexive>
	<removeOnDeath>true</removeOnDeath>
</PawnRelationDef>
<PawnRelationDef>
	<defName>FixatedMatingPartner</defName>
	fixated mating partner
	<importance>30</importance>
	<implied>false</implied>
	<reflexive>false</reflexive>
	<removeOnDeath>true</removeOnDeath>
</PawnRelationDef>
<PawnRelationDef>
	<defName>ExMatingPartner</defName>
	ex mating partner
	<importance>10</importance>
	<implied>false</implied>
	<reflexive>false</reflexive>
	<removeOnDeath>true</removeOnDeath>
</PawnRelationDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8"?>
<Defs>

  <IssueDef>
    <defName>BreedingWorkMaleColonists</defName>
    breeding duty (Male)
    <iconPath>UI/Issues/BreedWorkMale</iconPath>
  </IssueDef>

  <IssueDef>
    <defName>BreedingWorkMaleSlaves</defName>
    breeding duty (Male)
    <iconPath>UI/Issues/BreedWorkMaleSlaves</iconPath>
  </IssueDef>

  <IssueDef>
    <defName>BreedingWorkFemaleColonists</defName>
    breeding duty (Female)
    <iconPath>UI/Issues/BreedWorkFemale</iconPath>
  </IssueDef>

  <IssueDef>
    <defName>BreedingWorkFemaleSlaves</defName>
    breeding duty (Female)
    <iconPath>UI/Issues/BreedWorkFemaleSlaves</iconPath>
  </IssueDef>

  <PreceptDef>
    <defName>BreedingWorkMaleColonists</defName>
    <issue>BreedingWorkMaleColonists</issue>
    voluntary
    <description>Male colonists are expected to voluntarily breed designated animals as part of their duty.</description>
    <impact>Low</impact>
    <displayOrderInIssue>20</displayOrderInIssue>
  </PreceptDef>

  <PreceptDef>
    <defName>BreedingWorkMaleSlaves</defName>
    <issue>BreedingWorkMaleSlaves</issue>
    voluntary
    <description>Male slaves are expected to voluntarily breed designated animals as part of their duty.</description>
    <impact>Low</impact>
    <displayOrderInIssue>10</displayOrderInIssue>
  </PreceptDef>

  <PreceptDef>
    <defName>BreedingWorkMaleForcedColonists</defName>
    <issue>BreedingWorkMaleColonists</issue>
    forced
    <description>Male colonists are forced to breed designated animals, regardless of their willingness or arousal.</description>
    <impact>Medium</impact>
    <displayOrderInIssue>40</displayOrderInIssue>
  </PreceptDef>

  <PreceptDef>
    <defName>BreedingWorkMaleForcedSlaves</defName>
    <issue>BreedingWorkMaleSlaves</issue>
    forced
    <description>Male slaves are forced to breed designated animals, regardless of their willingness or arousal.</description>
    <impact>Medium</impact>
    <displayOrderInIssue>20</displayOrderInIssue>
  </PreceptDef>

  <PreceptDef>
    <defName>BreedingWorkFemaleColonists</defName>
    <issue>BreedingWorkFemaleColonists</issue>
    voluntary
    <description>Female colonists are expected to be voluntarily bred by designated animals as part of their duty.</description>
    <impact>Low</impact>
    <displayOrderInIssue>20</displayOrderInIssue>
  </PreceptDef>

  <PreceptDef>
    <defName>BreedingWorkFemaleSlaves</defName>
    <issue>BreedingWorkFemaleSlaves</issue>
    voluntary
    <description>Female slaves are expected to be voluntarily bred by designated animals as part of their duty.</description>
    <impact>Low</impact>
    <displayOrderInIssue>10</displayOrderInIssue>
  </PreceptDef>

  <PreceptDef>
    <defName>BreedingWorkFemaleForcedColonists</defName>
    <issue>BreedingWorkFemaleColonists</issue>
    forced
    <description>Female colonists are forced to be bred by designated animals, regardless of their willingness or arousal.</description>
    <impact>Medium</impact>
    <displayOrderInIssue>40</displayOrderInIssue>
  </PreceptDef>

  <PreceptDef>
    <defName>BreedingWorkFemaleForcedSlaves</defName>
    <issue>BreedingWorkFemaleSlaves</issue>
    forced
    <description>Female slaves are forced to be bred by designated animals, regardless of their willingness or arousal.</description>
    <impact>Medium</impact>
    <displayOrderInIssue>20</displayOrderInIssue>
  </PreceptDef>

</Defs>


code:
<?xml version="1.0" encoding="utf-8"?>
<Defs>

  <IssueDef>
    <defName>ForcedBreedingAnimals</defName>
    breeding animals
    <iconPath>UI/Issues/BreedWorkSelect</iconPath>
  </IssueDef>

  <PreceptDef>
    <defName>ForcedBreed_AllAnimals</defName>
    <issue>ForcedBreedingAnimals</issue>
    all colony animals
    <description>All colony animals are expected to be targeted for breeding.</description>
    <impact>High</impact>
    <displayOrderInIssue>10</displayOrderInIssue>
  </PreceptDef>

  <PreceptDef>
    <defName>ForcedBreed_PenAnimals</defName>
    <issue>ForcedBreedingAnimals</issue>
    pen animals only
    <description>Only pen animals are expected to be targeted for breeding.</description>
    <impact>Low</impact>
    <displayOrderInIssue>20</displayOrderInIssue>
  </PreceptDef>

  <PreceptDef>
    <defName>ForcedBreed_NonPenAnimals</defName>
    <issue>ForcedBreedingAnimals</issue>
    non-pen animals only
    <description>Only free roaming animals are expected to be targeted for breeding.</description>
    <impact>Low</impact>
    <displayOrderInIssue>30</displayOrderInIssue>
  </PreceptDef>

  <PreceptDef>
    <defName>ForcedBreed_Venerated</defName>
    <issue>ForcedBreedingAnimals</issue>
    venerated animals only
    <description>Only venerated animals are expected to be targeted for breeding.</description>
    <impact>Medium</impact>
    <displayOrderInIssue>40</displayOrderInIssue>
  </PreceptDef>

  <PreceptDef>
    <defName>ForcedBreed_NonVenerated</defName>
    <issue>ForcedBreedingAnimals</issue>
    non-venerated animals only
    <description>Only non venerated animals are expected to be targeted for breeding.</description>
    <impact>Medium</impact>
    <displayOrderInIssue>50</displayOrderInIssue>
  </PreceptDef>

  <PreceptDef>
    <defName>ForcedBreed_NonBonded</defName>
    <issue>ForcedBreedingAnimals</issue>
    non-bonded animals only
    <description>Only non bonded animals are expected to be targeted for breeding.</description>
    <impact>Medium</impact>
    <displayOrderInIssue>60</displayOrderInIssue>
  </PreceptDef>

</Defs>


code:
<?xml version='1.0' encoding='UTF-8'?>
<Defs>
<RulePackDef>
  <defName>BestialityNuzzle_Initiator_Male</defName>
  <rulePack>
    <rulesStrings>

r_logentry->[INITIATOR_nameDef] stood with his erect cock exposed, a drop of slick fluid trailing from its tip as he watched [RECIPIENT_nameDef] intently.

r_logentry->[INITIATOR_nameDef] nuzzled along [RECIPIENT_nameDef]'s side, his arousal evident in both scent and posture.

r_logentry->[INITIATOR_nameDef] pressed close from behind, his firm cock brushing against [RECIPIENT_nameDef]'s thigh.

r_logentry->[INITIATOR_nameDef] reared slightly, exposing his full, twitching cock as he sniffed at [RECIPIENT_nameDef].

r_logentry->[INITIATOR_nameDef] gave a few slow, shallow thrusts in the air near [RECIPIENT_nameDef], accidentally bumping against their thigh, leaving a slick smear behind.

    </rulesStrings>
  </rulePack>
</RulePackDef>
<RulePackDef>
  <defName>BestialityNuzzle_Initiator_Female</defName>
  <rulePack>
    <rulesStrings>

r_logentry->[INITIATOR_nameDef] stepped in close, her slick, swollen vulva briefly visible beneath her tail.

r_logentry->[INITIATOR_nameDef] nudged her body against [RECIPIENT_nameDef], her scent unmistakably heavy with heat.

r_logentry->[INITIATOR_nameDef] shifted her weight, tail lifting in a slow, suggestive motion as she brushed past [RECIPIENT_nameDef].

r_logentry->[INITIATOR_nameDef] pressed her rear firmly against [RECIPIENT_nameDef], her swollen genitals leaving a hot, slick trail on their leg.

r_logentry->[INITIATOR_nameDef] lifted her tail higher, exposing her slick, swollen vulva in full view.

    </rulesStrings>
  </rulePack>
</RulePackDef>
</Defs>


code:
<?xml version='1.0' encoding='UTF-8'?>
<Defs>
  <RulePackDef>
    <defName>BestialityNuzzle_Response_Accept_Female</defName>
		<rulePack>
		  <rulesStrings>

r_logentry->[RECIPIENT_nameDef] noticed [INITIATOR_nameDef]'s arousal and shifted into a receptive posture.

r_logentry->[RECIPIENT_nameDef] let out a quiet breath and lifted a leg slightly, inviting the approach.

r_logentry->[RECIPIENT_nameDef] reached back and gently touched [INITIATOR_nameDef]'s genitals.

		</rulesStrings>
    </rulePack>
  </RulePackDef>
  <RulePackDef>
    <defName>BestialityNuzzle_Response_Reject_Female</defName>
		<rulePack>
		  <rulesStrings>

r_logentry->[RECIPIENT_nameDef] shifted away from [INITIATOR_nameDef], pretending not to notice.

r_logentry->[RECIPIENT_nameDef] took a step back, ignoring [INITIATOR_nameDef]'s approach.

r_logentry->[RECIPIENT_nameDef] turned slightly, choosing not to respond to the animal's intent.

		</rulesStrings>
    </rulePack>
  </RulePackDef>
  <RulePackDef>
    <defName>BestialityNuzzle_Response_Forced_Female</defName>
		<rulePack>
		  <rulesStrings>

r_logentry->[RECIPIENT_nameDef] hesitated and stepped away, yet [INITIATOR_nameDef] quickly tackled her, pinning her beneath him

r_logentry->[RECIPIENT_nameDef] shook her head firmly, yet [INITIATOR_nameDef] ignored her refusal, mounting her from behind.

r_logentry->[RECIPIENT_nameDef] tried to push him away, but [INITIATOR_nameDef] shoved himself forcefully against her thighs, holding her in place until she stopped resisting.

		</rulesStrings>
    </rulePack>
  </RulePackDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8"?>
<Defs>
  <RulePackDef>
    <defName>BestialityNuzzle_Response_Accept_Male</defName>
    <rulePack>
      <rulesStrings>

r_logentry->[RECIPIENT_nameDef] stepped back slightly, allowing [INITIATOR_nameDef] to move closer.

r_logentry->[RECIPIENT_nameDef] ran his hand along [INITIATOR_nameDef]'s genitals, nodding faintly in approval.

r_logentry->[RECIPIENT_nameDef] guided [INITIATOR_nameDef] into position.

      </rulesStrings>
    </rulePack>
  </RulePackDef>
  <RulePackDef>
    <defName>BestialityNuzzle_Response_Reject_Male</defName>
    <rulePack>
      <rulesStrings>

r_logentry->[RECIPIENT_nameDef] shifted away from [INITIATOR_nameDef], pretending not to notice.

r_logentry->[RECIPIENT_nameDef] took a step back, ignoring [INITIATOR_nameDef]'s approach.

r_logentry->[RECIPIENT_nameDef] turned slightly, choosing not to respond to the animal's intent.

      </rulesStrings>
    </rulePack>
  </RulePackDef>
  <RulePackDef>
    <defName>BestialityNuzzle_Response_Forced_Male</defName>
    <rulePack>
      <rulesStrings>

r_logentry->[RECIPIENT_nameDef] attempted to back away, but [INITIATOR_nameDef] mounted him, locking him in place beneath her body.

r_logentry->[RECIPIENT_nameDef] shook his head in refusal, but [INITIATOR_nameDef] shoved her body into his hips, knocking him down and straddling him.

r_logentry->[RECIPIENT_nameDef] tried to push [INITIATOR_nameDef] away, but she lunged forward, firmly pinning him beneath her weight.

r_logentry->[RECIPIENT_nameDef] shook off her first attempt, but [INITIATOR_nameDef] reared up, pulling him to the ground.

r_logentry->[RECIPIENT_nameDef] waved her away, but [INITIATOR_nameDef] surged forward, pressing him firmly onto his back and holding him there.

r_logentry->[RECIPIENT_nameDef] firmly refused [INITIATOR_nameDef]'s approach, yet she lunged, overpowering him and pinning his shoulders down.

      </rulesStrings>
    </rulePack>
  </RulePackDef>
</Defs>


code:
<Defs>
    <RulePackDef>
        <defName>BestialityTalk_Recipient_Positive</defName>
        <rulePack>
            <rulesStrings>

r_logentry(p=5)->[RECIPIENT_nameDef] now sees bestiality in a more accepting way.

r_logentry(p=5)->[RECIPIENT_nameDef] has become more open to bestiality.

r_logentry(p=5)->[RECIPIENT_nameDef]'s opinion on bestiality has improved.

r_logentry(p=5)->[RECIPIENT_nameDef] is intrigued by the idea of bestiality.

r_logentry(p=5)->[RECIPIENT_nameDef] appears more receptive to the idea of bestiality.

            </rulesStrings>
        </rulePack>
    </RulePackDef>

    <RulePackDef>
        <defName>BestialityTalk_Recipient_Negative</defName>
        <rulePack>
            <rulesStrings>

r_logentry(p=5)->[RECIPIENT_nameDef] feels even less comfortable about bestiality.

r_logentry(p=5)->[RECIPIENT_nameDef] has become even more resistant to bestiality.

r_logentry(p=5)->[RECIPIENT_nameDef] has reinforced their distaste for bestiality.

r_logentry(p=5)->[RECIPIENT_nameDef] is now even less accepting of bestiality.

r_logentry(p=5)->[RECIPIENT_nameDef] feels more opposed to bestiality than before.

            </rulesStrings>
        </rulePack>
    </RulePackDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>
	<ThinkTreeDef>
		<defName>AnimalSexualEmergencyTree</defName>
		<insertTag>Animal_PreWander</insertTag>
		<insertPriority>14</insertPriority>
		<thinkRoot Class="ThinkNode_Priority">
			<subNodes>

					<subNodes>

							<subNodes>

									<subNodes>

									</subNodes>

							</subNodes>

					</subNodes>

			</subNodes>
		</thinkRoot>
	</ThinkTreeDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8"?>
<Defs>
	<ThoughtDef>
		<defName>BestialityTalkSocial</defName>
		<thoughtClass>Thought_MemorySocialCumulative</thoughtClass>
		<stackLimit>300</stackLimit>
		<maxCumulatedOpinionOffset>10</maxCumulatedOpinionOffset>
		<developmentalStageFilter>Baby, Child, Adult</developmentalStageFilter>
		<socialTargetDevelopmentalStageFilter>Baby, Child, Adult</socialTargetDevelopmentalStageFilter>
		<nullifyingTraits>

Psychopath

		</nullifyingTraits>
		<stages>

				bestiality discussion
				<description>I had a conversation about bestiality with someone.</description>
				<baseOpinionOffset>0.66</baseOpinionOffset>

		</stages>
	</ThoughtDef>

	<ThoughtDef>
		<defName>BestialityTalkSocial_Negative</defName>
		<thoughtClass>Thought_MemorySocialCumulative</thoughtClass>
		<stackLimit>300</stackLimit>
		<maxCumulatedOpinionOffset>-10</maxCumulatedOpinionOffset>
		<developmentalStageFilter>Baby, Child, Adult</developmentalStageFilter>
		<socialTargetDevelopmentalStageFilter>Baby, Child, Adult</socialTargetDevelopmentalStageFilter>
		<nullifyingTraits>

Psychopath

		</nullifyingTraits>
		<stages>

				unpleasant bestiality discussion
				<description>I had an uncomfortable conversation about bestiality with someone.</description>
				<baseOpinionOffset>-0.66</baseOpinionOffset>

		</stages>
	</ThoughtDef>

	<ThoughtDef>
		<defName>BestialityTalkMood</defName>
		<durationDays>2</durationDays>
		<stackLimit>5</stackLimit>
		<developmentalStageFilter>Baby, Child, Adult</developmentalStageFilter>
		<stages>

				bestiality discussion
				<description>I had an enjoyable talk about bestiality.</description>
				<baseMoodEffect>1</baseMoodEffect>

		</stages>
	</ThoughtDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8"?>
<Defs>

  <ThoughtDef>
    <defName>BreedingWork_BreedingConditionedMale</defName>
    <durationDays>30</durationDays>
    <stackLimit>1</stackLimit>
    <developmentalStageFilter>Adult</developmentalStageFilter>
	<replaceThoughts>

BreedingWork_MaleColonist_Neg

BreedingWork_MaleSlave_Neg

	</replaceThoughts>
    <stages>

        conditioned to breeding
        <description>I’ve bred animals so many times, it doesn’t bother me anymore.</description>
        <baseMoodEffect>-1</baseMoodEffect>

        conditioned to breeding
        <description>I’ve stopped pretending I’m above it. Mounting them, filling them… it just feels right.</description>
        <baseMoodEffect>1</baseMoodEffect>

    </stages>
    <nullifyingTraits>

Masochist

    </nullifyingTraits>
  </ThoughtDef>

  <ThoughtDef>
    <defName>BreedingWork_BreedingConditionedFemale</defName>
	<thoughtClass>Thought_Memory</thoughtClass>
    <durationDays>30</durationDays>
    <stackLimit>1</stackLimit>
    <developmentalStageFilter>Adult</developmentalStageFilter>
	<replaceThoughts>

BreedingWork_FemaleColonist_Neg

BreedingWork_FemaleSlave_Neg

	</replaceThoughts>
    <stages>

        conditioned to being bred
        <description>I’ve been bred so many times, it doesn’t bother me anymore.</description>
        <baseMoodEffect>-1</baseMoodEffect>

        conditioned to being bred
        <description>I never thought I’d enjoy this... but there’s something comforting about being treated like livestock.</description>
        <baseMoodEffect>1</baseMoodEffect>

    </stages>
    <nullifyingTraits>

Masochist

    </nullifyingTraits>
  </ThoughtDef>

  <ThoughtDef>
    <defName>BreedingWork_MaleColonist_Pos</defName>
    <durationDays>5</durationDays>
    <stackLimit>100</stackLimit>
    <stackedEffectMultiplier>0.4</stackedEffectMultiplier>
    <developmentalStageFilter>Adult</developmentalStageFilter>
    <stages>

        bred an animal
        <description>I bred an animal. I wasn’t sure how I’d feel — but I ended up liking it.</description>
        <baseMoodEffect>1</baseMoodEffect>

    </stages>
  </ThoughtDef>

  <ThoughtDef>
    <defName>BreedingWork_MaleColonist_Neg</defName>
    <durationDays>5</durationDays>
    <stackLimit>100</stackLimit>
    <stackedEffectMultiplier>0.4</stackedEffectMultiplier>
    <developmentalStageFilter>Adult</developmentalStageFilter>
    <stages>

        forced to breed an animal
        <description>I was made to breed an animal. I felt like livestock — not a man..</description>
        <baseMoodEffect>-4</baseMoodEffect>

        forced to breed an animal
        <description>I was used again. It doesn’t even feel like anything now.</description>
        <baseMoodEffect>-1</baseMoodEffect>

        forced to breed an animal
        <description>I bred again. No resistance. Just warmth… maybe I’d make a good stud after all.</description>
        <baseMoodEffect>1</baseMoodEffect>

    </stages>
    <nullifyingTraits>

Masochist

    </nullifyingTraits>
  </ThoughtDef>

  <ThoughtDef>
    <defName>BreedingWork_FemaleColonist_Pos</defName>
    <durationDays>5</durationDays>
    <stackLimit>100</stackLimit>
    <stackedEffectMultiplier>0.4</stackedEffectMultiplier>
    <developmentalStageFilter>Adult</developmentalStageFilter>
    <stages>

        was bred by an animal
        <description>I let an animal breed me. It wasn't unpleasant... in fact, it felt surprisingly good.</description>
        <baseMoodEffect>1</baseMoodEffect>

    </stages>
  </ThoughtDef>

  <ThoughtDef>
    <defName>BreedingWork_FemaleColonist_Neg</defName>
    <durationDays>5</durationDays>
    <stackLimit>100</stackLimit>
    <stackedEffectMultiplier>0.4</stackedEffectMultiplier>
    <developmentalStageFilter>Adult</developmentalStageFilter>
    <stages>

        forced to be bred by an animal
        <description>I forced myself to be bred by an animal. I didn’t want to — and yet, I presented myself. I felt like livestock pretending to volunteer.</description>
        <baseMoodEffect>-4</baseMoodEffect>

        forced to be bred by an animal
        <description>I was bred again. I don’t feel anything anymore.</description>
        <baseMoodEffect>-1</baseMoodEffect>

        forced to be bred by an animal
        <description>I was bred again. I lowered myself without protest. Just livestock in heat — maybe that’s what I am now.</description>
        <baseMoodEffect>1</baseMoodEffect>

    </stages>
    <nullifyingTraits>

Masochist

    </nullifyingTraits>
  </ThoughtDef>

  <ThoughtDef>
    <defName>BreedingWork_MaleSlave_Pos</defName>
    <durationDays>5</durationDays>
    <stackLimit>100</stackLimit>
    <stackedEffectMultiplier>0.4</stackedEffectMultiplier>
    <developmentalStageFilter>Adult</developmentalStageFilter>
    <stages>

        bred an animal
        <description>I had to breed an animal — didn’t bother me much; at least it’s easy work.</description>
        <baseMoodEffect>1</baseMoodEffect>

    </stages>
  </ThoughtDef>

  <ThoughtDef>
    <defName>BreedingWork_MaleSlave_Neg</defName>
    <durationDays>5</durationDays>
    <stackLimit>100</stackLimit>
    <stackedEffectMultiplier>0.4</stackedEffectMultiplier>
    <developmentalStageFilter>Adult</developmentalStageFilter>
    <stages>

        forced to breed an animal
        <description>They forced me to breed an animal. I’m not even seen as a person anymore — just livestock doing its job.</description>
        <baseMoodEffect>-2</baseMoodEffect>

        forced to breed an animal
        <description>They made me do it again. It doesn’t matter anymore.</description>
        <baseMoodEffect>-1</baseMoodEffect>

        forced to breed an animal
        <description>I was at it again, thrusting without thought. No one forced me. There was warmth… and it felt good, more than I wanted to admit.</description>
        <baseMoodEffect>1</baseMoodEffect>

    </stages>
    <nullifyingTraits>

Masochist

    </nullifyingTraits>
  </ThoughtDef>

  <ThoughtDef>
    <defName>BreedingWork_FemaleSlave_Pos</defName>
    <durationDays>5</durationDays>
    <stackLimit>100</stackLimit>
    <stackedEffectMultiplier>0.4</stackedEffectMultiplier>
    <developmentalStageFilter>Adult</developmentalStageFilter>
    <stages>

        was bred by an animal
        <description>I was bred by an animal — had to invite it myself. Didn't bother me much — at least it's simple work.</description>
        <baseMoodEffect>1</baseMoodEffect>

    </stages>
  </ThoughtDef>

  <ThoughtDef>
    <defName>BreedingWork_FemaleSlave_Neg</defName>
    <durationDays>5</durationDays>
    <stackLimit>100</stackLimit>
    <stackedEffectMultiplier>0.4</stackedEffectMultiplier>
    <developmentalStageFilter>Adult</developmentalStageFilter>
    <stages>

        forced to be bred by an animal
        <description>They had me present myself and wait to be bred. I was used like livestock — that’s all I am to them.</description>
        <baseMoodEffect>-2</baseMoodEffect>

        forced to be bred by an animal
        <description>I was bred again. I didn’t feel anything. Just routine.</description>
        <baseMoodEffect>-1</baseMoodEffect>

        forced to be bred by an animal
        <description>I was mounted again. No fear, no shame — just warmth. Being used like this doesn’t feel so bad anymore.</description>
        <baseMoodEffect>1</baseMoodEffect>

    </stages>
  </ThoughtDef>

</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>

  <TrainableDef>
	<defName>InterspeciesHumanSex</defName>
	interspecies human sex
	<description>Trains the animal to initiate sex with humans. Does not affect same-species or non-human-animal sex.</description>
    <icon>UI/Icons/Trainables/Obedience</icon>
    <listPriority>399</listPriority>
    <difficulty>100</difficulty>
    <requiredTrainability>Intermediate</requiredTrainability>
    <steps>3</steps>
    <prerequisites>

Tameness

    </prerequisites>
    <defaultTrainable>true</defaultTrainable>
    <tags>

Basic

    </tags>
  </TrainableDef>

  <TrainableDef>
	<defName>InterspeciesAnimalSex</defName>
	interspecies animal sex
	<description>Trains the animal to initiate sex with animals of other species. Does not affect same-species mating.</description>
    <icon>UI/Icons/Trainables/Obedience</icon>
    <listPriority>398</listPriority>
    <difficulty>100</difficulty>
    <requiredTrainability>Intermediate</requiredTrainability>
    <steps>3</steps>
    <prerequisites>

Tameness

    </prerequisites>
    <defaultTrainable>true</defaultTrainable>
    <tags>

Basic

    </tags>
  </TrainableDef>

</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>

<Defs>
  <TraitDef>
    <defName>AnimalHybrid</defName>
    <commonality>0</commonality>
    <degreeDatas>

        Animal Hybrid
        <description>Born from mating between a human and an animal, {PAWN_nameDef} possesses a natural affinity for animals and understands them instinctively.</description>
        <statOffsets>
          <TameAnimalChance>1.2</TameAnimalChance>
          <TrainAnimalChance>1.2</TrainAnimalChance>
        </statOffsets>

    </degreeDatas>
  </TraitDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>

  <WorkGiverDef>
    <defName>BreedAnimals</defName>
    breed animals
    <giverClass>ElToro_BAddon.WorkGiver_BreedAnimals</giverClass>
    <workType>BasicWorker</workType>
    <priorityInType>900</priorityInType>

    <verb>breeding</verb>
    <gerund>breeding</gerund>

    <scanThings>true</scanThings>
    <scanCells>false</scanCells>

    <requiredCapacities>

Manipulation

    </requiredCapacities>
  </WorkGiverDef>

  <WorkGiverDef>
    <defName>BreedAnimalsForced</defName>
    forced animal breeding
    <giverClass>ElToro_BAddon.WorkGiver_BreedAnimalsForced</giverClass>
    <workType>BasicWorker</workType>
    <priorityInType>950</priorityInType>

    <verb>breeding</verb>
    <gerund>breeding</gerund>

    <scanThings>true</scanThings>
    <scanCells>false</scanCells>

    <requiredCapacities>

Manipulation

    </requiredCapacities>
  </WorkGiverDef>

</Defs>


code:
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>
  <BestialityTalk_TurnedViolent_Label>Bestiality Talk Turned Violent</BestialityTalk_TurnedViolent_Label>
  <BestialityTalk_TurnedViolent_Text1>{0} snapped and attacked!</BestialityTalk_TurnedViolent_Text1>
  <BestialityTalk_TurnedViolent_Text2>{0} couldn’t take it and fought back!</BestialityTalk_TurnedViolent_Text2>
</LanguageData>


code:
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>
  <ForcedBreeding_Tip_NotAnimal>Cannot designate: Not an animal.</ForcedBreeding_Tip_NotAnimal>
  <ForcedBreeding_Tip_NotHuman>Cannot designate: Not a human.</ForcedBreeding_Tip_NotHuman>
  <ForcedBreeding_Tip_Ideo>Cannot designate: Disabled by ideology precept.</ForcedBreeding_Tip_Ideo>
  <ForcedBreeding_Tip_MissingComp>Cannot designate: Unsupported modded animal.</ForcedBreeding_Tip_MissingComp>
  <ForcedBreeding_Tip_Dead>Cannot designate: This animal is dead.</ForcedBreeding_Tip_Dead>
  <ForcedBreeding_Tip_NotColony>Cannot designate: Not a colony-owned animal.</ForcedBreeding_Tip_NotColony>
  <ForcedBreeding_Tip_NotMature>Cannot designate: This animal is not sexually mature or is incapable of sex.</ForcedBreeding_Tip_NotMature>
  <ForcedBreeding_Tip_NoGenitals>Cannot designate: This animal has no functional genitals.</ForcedBreeding_Tip_NoGenitals>
  <ForcedBreeding_Tip_CanDesignate>Designate this animal for forced breeding.</ForcedBreeding_Tip_CanDesignate>
</LanguageData>


code:
<Patch>
  <Operation Class="PatchOperationInsert">
    <xpath>/Defs/PawnTableDef[defName="RJW_PawnTable_Animals"]/columns/li[text()='RJW_BreedAnimal']</xpath>
    <value>

RJW_ForcedBreedingAnimal

    </value>
  </Operation>
</Patch>


code:
<Patch>
  <Operation Class="PatchOperationInsert">
    <xpath>/Defs/PawnTableDef[defName="RJW_PawnTable_Colonists"]/columns/li[text()='RJW_BreedHumanlike']</xpath>
    <value>

RJW_ForcedBreedingHuman

    </value>
  </Operation>
  <Operation Class="PatchOperationInsert">
    <xpath>/Defs/PawnTableDef[defName="RJW_PawnTable_Property"]/columns/li[text()='RJW_BreedHumanlike']</xpath>
    <value>

RJW_ForcedBreedingHuman

    </value>
  </Operation>
</Patch>


code:
Mod that adds the following features for RJW human-animal (and vice versa) interactions:

• Human-Animal Hybrid System
• Nuzzle Escalation System
• Bestiality Acceptance

More details are available in About.xml.


code:
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35931.197 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElToro_BAddon", "ElToro_BAddon\ElToro_BAddon.csproj", "{22D89E69-8BD8-460F-BC36-C01610CB5A75}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{22D89E69-8BD8-460F-BC36-C01610CB5A75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{22D89E69-8BD8-460F-BC36-C01610CB5A75}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{22D89E69-8BD8-460F-BC36-C01610CB5A75}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{22D89E69-8BD8-460F-BC36-C01610CB5A75}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {A60FFA52-DB8D-4DD4-BFFF-74F62B0C2943}
	EndGlobalSection
EndGlobal


code:
using System.Collections.Generic;
using System.Linq;
using Verse;
using RimWorld;
using UnityEngine;
using rjw;
using Verse.Noise;

namespace ElToro_BAddon
{
    public class CompAnimalSexMemory : ThingComp
    {
        private Dictionary<Pawn, float> affinityMap = new Dictionary<Pawn, float>();
        private Dictionary<Pawn, int> sexCountMap = new Dictionary<Pawn, int>();
        private Dictionary<Pawn, int> lastSexTickMap = new Dictionary<Pawn, int>();
        private SexTrait traits = SexTrait.None;
        private PawnFloatMap affinityWrapper = new PawnFloatMap();
        private PawnIntMap sexCountWrapper = new PawnIntMap();
        private PawnIntMap lastSexTickWrapper = new PawnIntMap();
        private int lastDecayCheckTick = 0;

        public SexTrait GetTraits() => traits;
        public void SetTraits(SexTrait t) => traits = t;
        public void AddTrait(SexTrait t) => traits |= t;
        public bool HasTrait(SexTrait t) => (traits & t) != 0;

        public override void CompTickRare()
        {
            base.CompTickRare();

            if (!ShouldUseMemory)
            {
                return;
            }

            if (Find.TickManager.TicksGame - lastDecayCheckTick >= 60000)
            {
                lastDecayCheckTick = Find.TickManager.TicksGame;
                RunDecayLogic();
            }
        }

        public bool ShouldUseMemory =>
            parent is Pawn pawn &&
            xxx.is_animal(pawn) &&
            (AfterSexUtility.AnimalParaphilia(pawn) ||
             (pawn.training != null &&
              (pawn.training.HasLearned(defs.InterspeciesAnimalSex) || pawn.training.HasLearned(defs.InterspeciesHumanSex))));

        public void IncreaseAffinity(Pawn partner)
        {
            if (partner == null || partner.Dead || !ShouldUseMemory)
                return;

            var pawn = (Pawn)parent;
            int now = Find.TickManager.TicksGame;

            if (!affinityMap.ContainsKey(partner))
                affinityMap[partner] = 0f;
            if (!sexCountMap.ContainsKey(partner))
                sexCountMap[partner] = 0;
            if (!lastSexTickMap.ContainsKey(partner))
                lastSexTickMap[partner] = now;

            float gain = 0.05f * Rand.Range(0.8f, 1.2f);

            if (pawn.relations.DirectRelationExists(PawnRelationDefOf.Bond, partner))
                gain *= 1.5f;

            if (pawn.relations.DirectRelationExists(defs.ExMatingPartner, partner))
                gain *= 1.2f;

            if (HasTrait(SexTrait.Affectionate))
                gain *= 1.5f;

            affinityMap[partner] = Mathf.Clamp01(affinityMap[partner] + gain);
            sexCountMap[partner] += 1;
            lastSexTickMap[partner] = now;

            TryAssignRelation(partner);
            GiveSexTrait();
        }

        public void TryAssignRelation(Pawn partner)
        {
            float affinity = affinityMap.TryGetValue(partner, out var val) ? val : 0f;
            Pawn pawn = parent as Pawn;

            bool hasFixated = pawn.relations.DirectRelationExists(defs.FixatedMatingPartner, partner);
            bool hasPreferred = pawn.relations.DirectRelationExists(defs.PreferredMatingPartner, partner);
            bool hasExFixated = pawn.relations.DirectRelationExists(defs.ExMatingPartner, partner);

            if (affinity >= 1.0f)
            {
                if (!hasFixated)
                {
                    if (hasPreferred)
                    {
                        pawn.relations.RemoveDirectRelation(defs.PreferredMatingPartner, partner);
                    }
                    if (hasExFixated)
                    {
                        pawn.relations.RemoveDirectRelation(defs.ExMatingPartner, partner);
                    }

                    pawn.relations.AddDirectRelation(defs.FixatedMatingPartner, partner);
                }
                return;
            }

            if (!hasFixated && !hasPreferred && affinity >= 0.5f)
            {
                if (hasExFixated)
                {
                    pawn.relations.RemoveDirectRelation(defs.ExMatingPartner, partner);
                }

                pawn.relations.AddDirectRelation(defs.PreferredMatingPartner, partner);
            }
        }

        public void RunDecayLogic()
        {
            int now = Find.TickManager.TicksGame;
            Pawn pawn = parent as Pawn;

            foreach (var partner in lastSexTickMap.Keys.ToList())
            {
                int lastTick = lastSexTickMap[partner];
                bool hasFixated = pawn.relations.DirectRelationExists(defs.FixatedMatingPartner, partner);
                bool hasPreferred = pawn.relations.DirectRelationExists(defs.PreferredMatingPartner, partner);
                bool hasExFixated = pawn.relations.DirectRelationExists(defs.ExMatingPartner, partner);

                if (hasFixated)
                {
                    if (now - lastTick >= 600000)
                    {
                        pawn.relations.RemoveDirectRelation(defs.FixatedMatingPartner, partner);
                        pawn.relations.AddDirectRelation(defs.ExMatingPartner, partner);
                        affinityMap[partner] = 0f;
                    }
                }
                else if (hasPreferred)
                {
                    if (now - lastTick >= 300000)
                    {
                        pawn.relations.RemoveDirectRelation(defs.PreferredMatingPartner, partner);
                        affinityMap.Remove(partner);
                        sexCountMap.Remove(partner);
                        lastSexTickMap.Remove(partner);
                    }
                }
                else if (hasExFixated)
                {
                    if (now - lastTick >= 300000)
                    {
                        pawn.relations.RemoveDirectRelation(defs.ExMatingPartner, partner);
                        affinityMap.Remove(partner);
                        sexCountMap.Remove(partner);
                        lastSexTickMap.Remove(partner);
                    }
                }
                else
                {
                    if (now - lastTick >= 300000)
                    {
                        affinityMap.Remove(partner);
                        sexCountMap.Remove(partner);
                        lastSexTickMap.Remove(partner);
                    }
                }
            }
        }

        public override void PostExposeData()
        {
            base.PostExposeData();

            if (affinityWrapper == null) affinityWrapper = new PawnFloatMap();
            if (sexCountWrapper == null) sexCountWrapper = new PawnIntMap();
            if (lastSexTickWrapper == null) lastSexTickWrapper = new PawnIntMap();

            if (Scribe.mode == LoadSaveMode.Saving)
            {
                affinityWrapper.FromDictionary(affinityMap);
                sexCountWrapper.FromDictionary(sexCountMap);
                lastSexTickWrapper.FromDictionary(lastSexTickMap);
            }

            Scribe_Deep.Look(ref affinityWrapper, "affinityWrapper");
            Scribe_Deep.Look(ref sexCountWrapper, "sexCountWrapper");
            Scribe_Deep.Look(ref lastSexTickWrapper, "lastSexTickWrapper");
            Scribe_Values.Look(ref traits, "sexTraits", SexTrait.None);

            if (Scribe.mode == LoadSaveMode.PostLoadInit)
            {
                if (affinityWrapper != null)
                    affinityMap = affinityWrapper.ToDictionary();
                if (sexCountWrapper != null)
                    sexCountMap = sexCountWrapper.ToDictionary();
                if (lastSexTickWrapper != null)
                    lastSexTickMap = lastSexTickWrapper.ToDictionary();
            }

            Scribe_Values.Look(ref lastDecayCheckTick, "lastDecayCheckTick", 0);
        }

        // For debugging tools
        public Dictionary<Pawn, float> GetAffinityMap() => affinityMap;
        public Dictionary<Pawn, int> GetSexCount() => sexCountMap;
        public bool GiveSexTrait()
        {
            if (traits != SexTrait.None)
                return false;

            if (!ShouldUseMemory)
                return false;

            SexTrait chosen;
            switch (Rand.RangeInclusive(0, 4))
            {
                case 0: chosen = SexTrait.Aggressive; break;
                case 1: chosen = SexTrait.LikesHumans; break;
                case 2: chosen = SexTrait.LikesAnimals; break;
                case 3: chosen = SexTrait.Vigorous; break;
                default: chosen = SexTrait.Affectionate; break;
            }

            AddTrait(chosen);
            return true;
        }
    }
}


code:
using Verse;
using System.Linq;

namespace ElToro_BAddon
{
    [StaticConstructorOnStartup]
    public static class CompInjector_AnimalSexMemory
    {
        static CompInjector_AnimalSexMemory()
        {
            foreach (ThingDef def in DefDatabase<ThingDef>.AllDefsListForReading)
            {
                if (def.race?.Animal == true)
                {
                    if (def.comps == null)
                        def.comps = new System.Collections.Generic.List<CompProperties>();

                    bool alreadyHas = def.comps.Any(c => c.compClass == typeof(CompAnimalSexMemory));
                    if (!alreadyHas)
                    {
                        def.comps.Add(new CompProperties_AnimalSexMemory());
                    }
                }
            }
        }
    }
}


code:
using Verse;

namespace ElToro_BAddon
{
    public class CompProperties_AnimalSexMemory : CompProperties
    {
        public CompProperties_AnimalSexMemory()
        {
            this.compClass = typeof(CompAnimalSexMemory);
        }
    }
}


code:
using System.Collections.Generic;
using Verse;

namespace ElToro_BAddon
{
    public class PawnFloatMap : IExposable
    {
        public List<Pawn> keys = new List<Pawn>();
        public List<float> values = new List<float>();

        public Dictionary<Pawn, float> ToDictionary()
        {
            var result = new Dictionary<Pawn, float>();
            for (int i = 0; i < keys.Count; i++)
            {
                if (keys[i] != null)
                    result[keys[i]] = values[i];
            }
            return result;
        }

        public void FromDictionary(Dictionary<Pawn, float> dict)
        {
            keys.Clear();
            values.Clear();
            foreach (var kv in dict)
            {
                keys.Add(kv.Key);
                values.Add(kv.Value);
            }
        }

        public void ExposeData()
        {
            Scribe_Collections.Look(ref keys, "keys", LookMode.Reference);
            Scribe_Collections.Look(ref values, "values", LookMode.Value);
        }
    }
}


code:
using System.Collections.Generic;
using Verse;

namespace ElToro_BAddon
{
    public class PawnIntMap : IExposable
    {
        public List<Pawn> keys = new List<Pawn>();
        public List<int> values = new List<int>();

        public Dictionary<Pawn, int> ToDictionary()
        {
            var result = new Dictionary<Pawn, int>();
            for (int i = 0; i < keys.Count; i++)
            {
                if (keys[i] != null)
                    result[keys[i]] = values[i];
            }
            return result;
        }

        public void FromDictionary(Dictionary<Pawn, int> dict)
        {
            keys.Clear();
            values.Clear();
            foreach (var kv in dict)
            {
                keys.Add(kv.Key);
                values.Add(kv.Value);
            }
        }

        public void ExposeData()
        {
            Scribe_Collections.Look(ref keys, "keys", LookMode.Reference);
            Scribe_Collections.Look(ref values, "values", LookMode.Value);
        }
    }
}


code:
using System.Collections.Generic;
using Verse;

namespace ElToro_BAddon
{
    [System.Flags]
    public enum SexTrait : byte
    {
        None = 0,
        Aggressive = 1 << 0,
        LikesHumans = 1 << 1,
        LikesAnimals = 1 << 2,
        Vigorous = 1 << 3,
        Affectionate = 1 << 4
    }
}


code:
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LudeonTK;
using RimWorld;
using rjw;
using Verse;
using Verse.AI;

namespace ElToro_BAddon
{
    public static class DebugAction_AnimalAffinityTools
    {
        [DebugAction("ElToro BAddon", "Show Affinity & Traits of Selected Animal", actionType = DebugActionType.Action, allowedGameStates = AllowedGameStates.PlayingOnMap)]
        public static void ShowAffinityAndTraits()
        {
            Pawn animal = Find.Selector.SingleSelectedThing as Pawn;
            if (animal == null || !animal.RaceProps.Animal)
            {
                Messages.Message("Select exactly one animal pawn.", MessageTypeDefOf.RejectInput, false);
                return;
            }

            var comp = animal.TryGetComp<CompAnimalSexMemory>();
            if (comp == null)
            {
                Messages.Message("This animal has no affinity component.", MessageTypeDefOf.RejectInput, false);
                return;
            }

            StringBuilder sb = new StringBuilder();
            sb.AppendLine($"[DEBUG] Affinity & Traits for {animal.LabelShortCap}:");

            // Display traits
            var traits = comp.GetTraits();
            sb.AppendLine($"Traits: {traits}");

            // Display affinities
            foreach (var pair in comp.GetAffinityMap())
            {
                if (pair.Key != null)
                    sb.AppendLine($"  → {pair.Key.LabelShortCap}: {pair.Value:F2}");
            }

            Log.Message(sb.ToString());
            Messages.Message("Affinity and traits printed to log.", MessageTypeDefOf.TaskCompletion, false);
        }
    }
    public static class DebugAction_AnimalAffinityTools2
    {
        [DebugAction("ElToro BAddon", "Simulate Affinity Gain", actionType = DebugActionType.Action, allowedGameStates = AllowedGameStates.PlayingOnMap)]
        public static void SimulateAffinity()
        {
            var selected = Find.Selector.SelectedObjects.OfType<Pawn>().ToList();
            if (selected.Count != 2)
            {
                Messages.Message("Select exactly two pawns: one animal and one human.", MessageTypeDefOf.RejectInput, false);
                return;
            }

            Pawn animal = selected.FirstOrDefault(p => p.RaceProps.Animal);
            Pawn human = selected.FirstOrDefault(p => p.RaceProps.Humanlike);

            if (animal == null || human == null)
            {
                Messages.Message("Must select one animal and one human.", MessageTypeDefOf.RejectInput, false);
                return;
            }

            var comp = animal.TryGetComp<CompAnimalSexMemory>();
            if (comp == null)
            {
                Messages.Message("Selected animal has no affinity comp.", MessageTypeDefOf.RejectInput, false);
                return;
            }

            comp.IncreaseAffinity(human);
            Messages.Message($"[DEBUG] Simulated sex: {animal.LabelShortCap} gained affinity for {human.LabelShortCap}.", MessageTypeDefOf.NeutralEvent, false);
        }
    }
    public static class DebugActions_TrainInterspeciesSex
    {
        [DebugAction("ElToro BAddon", "Start Interspecies Sex Training", actionType = DebugActionType.Action, allowedGameStates = AllowedGameStates.PlayingOnMap)]
        public static void StartInterspeciesSexTraining()
        {
            var selected = Find.Selector.SelectedObjects.OfType<Pawn>().ToList();
            if (selected.Count != 3)
            {
                Messages.Message("You must select exactly 3 pawns: human trainer, trainee animal, mating partner.", MessageTypeDefOf.RejectInput, false);
                return;
            }

            Pawn trainer = selected[0];
            Pawn trainee = selected[1];
            Pawn matingPartner = selected[2];

            if (!trainer.RaceProps.Humanlike)
            {
                Messages.Message("First selected pawn must be a human.", MessageTypeDefOf.RejectInput, false);
                return;
            }

            Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("TrainInterspeciesSex"), trainee, matingPartner);
            trainer.jobs.StartJob(job, JobCondition.InterruptForced);
        }
    }
    public static class DebugActions_TrainInterspecies
    {
        [DebugAction("ElToro BAddon", "Try Interspicies Mating Job", actionType = DebugActionType.ToolMapForPawns)]
        public static void TryInterspiciesMatingJob(Pawn animal)
        {
            if (!animal.Spawned || animal.Dead || animal.Downed)
            {
                Log.Warning($"[TrainInterspeciesSex] Cannot run job for unspawned or invalid pawn: {animal}");
                return;
            }

            var jobGiver = new JobGiver_InterspiciesMating();
            Job job = JobGiver_InterspiciesMating.TryGiveJobPublic(animal);
            if (job != null)
            {
                Log.Message($"[TrainInterspeciesSex] Assigning job {job.def.defName} to {animal.NameShortColored}");
                animal.jobs.StartJob(job, JobCondition.InterruptForced);
            }
            else
            {
                Log.Warning($"[TrainInterspeciesSex] No job returned for {animal.NameShortColored}");
            }
        }
    }
}


code:
using RimWorld;
using rjw;
using rjw.Modules.Attraction;
using System.Collections.Generic;
using UnityEngine;
using Verse;
using Verse.AI;

namespace ElToro_BAddon
{
    public class InteractionWorker_InterspiciesMating : InteractionWorker
    {
        public override float RandomSelectionWeight(Pawn initiator, Pawn recipient)
        {
            return 0f;
        }

        public override void Interacted(Pawn initiator, Pawn recipient, List<RulePackDef> extraSentencePacks,
           out string letterText, out string letterLabel, out LetterDef letterDef, out LookTargets lookTargets)
        {
            letterText = null;
            letterLabel = null;
            letterDef = null;
            lookTargets = null;

            if (!xxx.is_healthy(initiator) || !CasualSex_Helper.CanHaveSex(initiator) || !SexUtility.ReadyForLovin(initiator))
                return;

            var comp = initiator.TryGetComp<CompAnimalSexMemory>();
            float willingnessScore = AttractionUtility.Evaluate(recipient, initiator, AttractionPurpose.ForFucking);
            float acceptanceChance;

            if (willingnessScore < 0.1f)
            {
                acceptanceChance = 0f;
            }
            else if (willingnessScore >= 0.5f)
            {
                acceptanceChance = 0.9f;
            }
            else
            {
                acceptanceChance = Mathf.Lerp(0.3f, 0.9f, Mathf.InverseLerp(0.1f, 0.5f, willingnessScore));
            }

            if (xxx.is_satisfied(recipient))
            {
                acceptanceChance *= 0.5f;
            }

            if (comp.HasTrait(SexTrait.Affectionate))
            {
                acceptanceChance *= 1.25f;
            }

            acceptanceChance = Mathf.Clamp01(acceptanceChance * Settings.InterspiciesHumanModifier);

            bool accepted = Rand.Chance(acceptanceChance) && recipient.Awake();
            bool forced = false;

            if (!accepted)
            {
                float initiatorV = xxx.get_vulnerability(initiator);
                float recipientV = xxx.get_vulnerability(recipient);

                float forceChance = 0.05f;

                float baseBoost = (recipientV - initiatorV) / 2f;
                baseBoost = Mathf.Clamp01(baseBoost);

                float maxBoost = 0f;
                if (comp != null)
                {
                    if (comp.HasTrait(SexTrait.Affectionate))
                        maxBoost = 0f;
                    else if (comp.HasTrait(SexTrait.Aggressive))
                        maxBoost = 0.30f;
                    else
                        maxBoost = 0.15f;
                }

                // Apply boost
                forceChance += maxBoost * baseBoost;

                forceChance = Mathf.Clamp01(forceChance * Settings.InterspiciesRapeModifier);

                forced = Rand.Chance(forceChance) && xxx.can_get_raped(recipient) && xxx.can_rape(initiator);
            }

            if (accepted)
            {
                Job jobAccept = JobMaker.MakeJob(defs.InterspiciesMatingInteractionAccept, recipient);
                initiator.jobs.TryTakeOrderedJob(jobAccept);
            }
            else if (forced)
            {
                Job jobForced = JobMaker.MakeJob(defs.ForcedBreeding, recipient);
                initiator.jobs.TryTakeOrderedJob(jobForced);
            }
            else
            {
                Job jobDecline = JobMaker.MakeJob(defs.InterspiciesMatingInteractionDecline, recipient);
                initiator.jobs.TryTakeOrderedJob(jobDecline);
            }
        }
    }
}


code:
using System.Collections.Generic;
using RimWorld;
using Verse;
using Verse.AI;
using rjw;

namespace ElToro_BAddon
{
	/// <summary>
	/// This is the driver for animals mounting breeders.
	/// </summary>
	public class JobDriver_ForcedBreeding : JobDriver_Rape
	{
		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
			return true;
        }

		protected override IEnumerable<Toil> MakeNewToils()
		{
			setup_ticks();
			var PartnerJob = defs.ForcedBreedingReciever;

			this.FailOnDespawnedNullOrForbidden(iTarget);
			this.FailOn(() => !pawn.CanReach(Partner, PathEndMode.Touch, Danger.Some));
            this.FailOn(() => pawn.Drafted);
            this.FailOn(() => Partner.Drafted);

            var WaitForInteraction = new Toil();
            WaitForInteraction.defaultCompleteMode = ToilCompleteMode.Delay;
			WaitForInteraction.defaultDuration = 130; // Should be 121, but i don't like such values =)
            WaitForInteraction.socialMode = RandomSocialMode.Off;

            WaitForInteraction.tickAction = delegate
            {
                if (pawn.interactions.CanInteractNowWith(Partner))
                {
                    ReadyForNextToil();
                }

            };
            yield return WaitForInteraction;

            // Path to target
            yield return Toils_Goto.GotoThing(iTarget, PathEndMode.OnCell);

			var interactToil = new Toil();
            interactToil.FailOn(() => Partner.CurJob != null && Partner.jobs.curDriver != null && (Partner.jobs.curDriver is JobDriver_SexBaseInitiator || Partner.jobs.curDriver is JobDriver_SexBaseReciever));
            interactToil.initAction = () =>
            {
                InteractionDef interaction = (Partner.gender == Gender.Female)
                    ? defs.NuzzleSexForcedFemale
                    : defs.NuzzleSexForcedMale;

                pawn.interactions?.TryInteractWith(Partner, interaction);
                SexUtility.RapeTargetAlert(pawn, Partner);
            };
            interactToil.defaultCompleteMode = ToilCompleteMode.Instant;
            yield return interactToil;

            var StartPartnerJob = new Toil();
			StartPartnerJob.defaultCompleteMode = ToilCompleteMode.Instant;
			StartPartnerJob.socialMode = RandomSocialMode.Off;
			StartPartnerJob.initAction = delegate
			{
				if (Partner.jobs.curDriver is JobDriver_SexBaseRecieverRaped) return;

                Partner.jobs.EndCurrentJob(JobCondition.InterruptForced, false, false);
                Partner?.jobs.StopAll();
                Partner.pather?.StopDead();

                if (!pawn.Reserve(Partner, job, ignoreOtherReservations: true))
                {
                    pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
                    return;
                }

                Partner.jobs.StartJob(
					JobMaker.MakeJob(PartnerJob, pawn),
					lastJobEndCondition: JobCondition.InterruptForced
				);

			};
			yield return StartPartnerJob;

			// Use happy heart instead of bad-touch heart when accepting of breeding.
			var fleckDef = xxx.mote_noheart;

			// Breed target
			var SexToil = new Toil();
			SexToil.defaultCompleteMode = ToilCompleteMode.Never;
			SexToil.defaultDuration = duration;
			SexToil.handlingFacing = true;
			SexToil.initAction = delegate
			{
				Partner.pather.StopDead();
				Partner.jobs.curDriver.asleep = false;
				Start();
			};
			SexToil.tickAction = delegate
			{
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					ThrowMetaIconF(pawn.Position, pawn.Map, fleckDef);
				SexTick(pawn, Partner);
				if (!Partner.Dead)
					SexUtility.reduce_rest(Partner, 1);
				SexUtility.reduce_rest(pawn, 2);
				if (ticks_left <= 0)
					ReadyForNextToil();
			};
			SexToil.FailOn(() => Partner.CurJob?.def != PartnerJob);
			SexToil.AddFinishAction(End);
			yield return SexToil;

			yield return new Toil
			{
				initAction = delegate
				{
					//Log.Message("JobDriver_Breeding::MakeNewToils() - Calling aftersex");
					//// Trying to add some interactions and social logs
					Sexprops.isRape = true;
					SexUtility.ProcessSex(Sexprops);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


code:
using System.Collections.Generic;
using RimWorld;
using Verse;
using Verse.AI;
using rjw;

namespace ElToro_BAddon
{
	/// <summary>
	/// This is the driver for animals mounting breeders.
	/// </summary>
	public class JobDriver_ForcedBreedingWaiting : JobDriver_Rape
	{
		public override bool TryMakePreToilReservations(bool errorOnFailed)
		{
            return true;
        }

		protected override IEnumerable<Toil> MakeNewToils()
		{
			setup_ticks();
			var PartnerJob = defs.ForcedBreedingReciever;

			this.FailOnDespawnedNullOrForbidden(iTarget);
			this.FailOn(() => !pawn.CanReach(Partner, PathEndMode.Touch, Danger.Some));
            this.FailOn(() => pawn.Drafted);
            this.FailOn(() => Partner.Drafted);

            var WaitForInteraction = new Toil();
            WaitForInteraction.defaultCompleteMode = ToilCompleteMode.Delay;
			WaitForInteraction.defaultDuration = 130; // Should be 121, but i don't like such values =)
            WaitForInteraction.socialMode = RandomSocialMode.Off;

            WaitForInteraction.tickAction = delegate
            {
                if (pawn.interactions.CanInteractNowWith(Partner))
                {
                    ReadyForNextToil();
                }

            };
            yield return WaitForInteraction;

            // Path to target
            yield return Toils_Goto.GotoThing(iTarget, PathEndMode.OnCell);

			var interactToil = new Toil();
            interactToil.initAction = () =>
            {
                InteractionDef interaction = (Partner.gender == Gender.Female)
                    ? defs.NuzzleSexWaitingForcedFemale
                    : defs.NuzzleSexWaitingForcedMale;

                pawn.interactions?.TryInteractWith(Partner, interaction);
                SexUtility.RapeTargetAlert(pawn, Partner);
            };
            interactToil.defaultCompleteMode = ToilCompleteMode.Instant;
            yield return interactToil;

            var StartPartnerJob = new Toil();
			StartPartnerJob.defaultCompleteMode = ToilCompleteMode.Instant;
			StartPartnerJob.socialMode = RandomSocialMode.Off;
			StartPartnerJob.initAction = delegate
			{
				if (Partner.jobs.curDriver is JobDriver_SexBaseRecieverRaped) return;

                Partner.jobs.EndCurrentJob(JobCondition.InterruptForced, false, false);
                Partner?.jobs.StopAll();
                Partner.pather?.StopDead();

                if (!pawn.Reserve(Partner, job, ignoreOtherReservations: true))
                {
                    pawn.jobs.EndCurrentJob(JobCondition.Incompletable);
                    return;
                }

                Partner.jobs.StartJob(
                    JobMaker.MakeJob(PartnerJob, pawn),
                    lastJobEndCondition: JobCondition.InterruptForced
                );

			};
			yield return StartPartnerJob;

			// Use happy heart instead of bad-touch heart when accepting of breeding.
			var fleckDef = xxx.mote_noheart;

			// Breed target
			var SexToil = new Toil();
			SexToil.defaultCompleteMode = ToilCompleteMode.Never;
			SexToil.defaultDuration = duration;
			SexToil.handlingFacing = true;
			SexToil.initAction = delegate
			{
				Partner.pather.StopDead();
				Partner.jobs.curDriver.asleep = false;
				Start();
			};
			SexToil.tickAction = delegate
			{
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					ThrowMetaIconF(pawn.Position, pawn.Map, fleckDef);
				SexTick(pawn, Partner);
				if (!Partner.Dead)
					SexUtility.reduce_rest(Partner, 1);
				SexUtility.reduce_rest(pawn, 2);
				if (ticks_left <= 0)
					ReadyForNextToil();
			};
			SexToil.FailOn(() => Partner.CurJob?.def != PartnerJob);
			SexToil.AddFinishAction(End);
			yield return SexToil;

			yield return new Toil
			{
				initAction = delegate
				{
					//Log.Message("JobDriver_Breeding::MakeNewToils() - Calling aftersex");
					//// Trying to add some interactions and social logs
					Sexprops.isRape = true;
					SexUtility.ProcessSex(Sexprops);
				},
				defaultCompleteMode = ToilCompleteMode.Instant
			};
		}
	}
}


code:
using System.Collections.Generic;
using Verse;
using Verse.AI;
using RimWorld;
using rjw;

namespace ElToro_BAddon
{
    public class JobDriver_InterspiciesMatingInteraction : JobDriver
    {
        public override bool TryMakePreToilReservations(bool errorOnFailed)
        {
            return true;
        }

        protected override IEnumerable<Toil> MakeNewToils()
        {
            this.FailOnDespawnedOrNull(TargetIndex.A);
            this.FailOn(() => !pawn.CanReach(TargetA, PathEndMode.Touch, Danger.Some));
            this.FailOn(() => TargetA.Pawn.Drafted);
            Pawn partner = TargetA.Pawn;

            var WaitForInteraction = new Toil();
            WaitForInteraction.defaultCompleteMode = ToilCompleteMode.Delay;
            WaitForInteraction.defaultDuration = 130; // Should be 121, but i don't like such values =)
            WaitForInteraction.socialMode = RandomSocialMode.Off;
            WaitForInteraction.initAction = delegate
            {
                if (pawn.interactions.CanInteractNowWith(partner))
                {
                    ReadyForNextToil();
                }

            };

            WaitForInteraction.tickAction = delegate
            {
                if (pawn.interactions.CanInteractNowWith(partner))
                {
                    ReadyForNextToil();
                }

            };
            yield return WaitForInteraction;

            // Go to the human
            yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch);

            // Try to interact
            var interactToil = new Toil();
            interactToil.initAction = () =>
            {

                InteractionDef interaction = (pawn.gender == Gender.Female)
                    ? defs.InterspiciesHumanMatingFemale
                    : defs.InterspiciesHumanMatingMale;

                pawn.interactions?.TryInteractWith(partner, interaction);
            };
            interactToil.defaultCompleteMode = ToilCompleteMode.Instant;
            yield return interactToil;
        }
    }
}


code:
using System.Collections.Generic;
using Verse;
using Verse.AI;
using RimWorld;
using rjw;

namespace ElToro_BAddon
{
    public class JobDriver_InterspiciesMatingInteractionAccept : JobDriver
    {
        public override bool TryMakePreToilReservations(bool errorOnFailed)
        {
            return true;
        }

        protected override IEnumerable<Toil> MakeNewToils()
        {
            this.FailOnDespawnedOrNull(TargetIndex.A);
            this.FailOn(() => !pawn.CanReach(TargetA, PathEndMode.Touch, Danger.Some));
            this.FailOn(() => pawn.Drafted);
            this.FailOn(() => TargetA.Pawn.Drafted);
            Pawn partner = TargetA.Pawn;

            var WaitForInteraction = new Toil();
            WaitForInteraction.defaultCompleteMode = ToilCompleteMode.Delay;
            WaitForInteraction.defaultDuration = 130; // Should be 121, but i don't like such values =)
            WaitForInteraction.socialMode = RandomSocialMode.Off;

            WaitForInteraction.tickAction = delegate
            {
                if (pawn.interactions.CanInteractNowWith(partner))
                {
                    ReadyForNextToil();
                }

            };
            yield return WaitForInteraction;

            // Go to the human
            yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch);

            // Try to interact
            var interactToil = new Toil();
            interactToil.FailOn(() => partner.CurJob != null && partner.jobs.curDriver != null && (partner.jobs.curDriver is JobDriver_SexBaseInitiator || partner.jobs.curDriver is JobDriver_SexBaseReciever));
            interactToil.initAction = () =>
            {

                InteractionDef interaction = (partner.gender == Gender.Female)
                    ? defs.NuzzleSexAcceptedFemale
                    : defs.NuzzleSexAcceptedMale;

                pawn.interactions?.TryInteractWith(partner, interaction);
            };
            interactToil.defaultCompleteMode = ToilCompleteMode.Instant;
            yield return interactToil;

            var sexToil = new Toil();
            sexToil.initAction = () =>
            {
                Job SexJob = JobMaker.MakeJob(defs.SexInitiatorAnimalSex, partner);
                pawn.jobs.TryTakeOrderedJob(SexJob);
            };
            sexToil.defaultCompleteMode = ToilCompleteMode.Instant;
            yield return sexToil;
        }
    }
}


code:
using System.Collections.Generic;
using Verse;
using Verse.AI;
using RimWorld;
using rjw;

namespace ElToro_BAddon
{
    public class JobDriver_InterspiciesMatingInteractionDecline : JobDriver
    {
        public override bool TryMakePreToilReservations(bool errorOnFailed)
        {
            return true;
        }

        protected override IEnumerable<Toil> MakeNewToils()
        {
            this.FailOnDespawnedOrNull(TargetIndex.A);
            this.FailOn(() => !pawn.CanReach(TargetA, PathEndMode.Touch, Danger.Some));
            this.FailOn(() => pawn.Drafted);
            this.FailOn(() => TargetA.Pawn.Drafted);
            Pawn partner = TargetA.Pawn;

            var WaitForInteraction = new Toil();
            WaitForInteraction.defaultCompleteMode = ToilCompleteMode.Delay;
            WaitForInteraction.defaultDuration = 130; // Should be 121, but i don't like such values =)
            WaitForInteraction.socialMode = RandomSocialMode.Off;

            WaitForInteraction.tickAction = delegate
            {
                if (pawn.interactions.CanInteractNowWith(partner))
                {
                    ReadyForNextToil();
                }

            };
            yield return WaitForInteraction;

            // Go to the human
            yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch);

            // Try to interact
            var interactToil = new Toil();
            interactToil.initAction = () =>
            {

                InteractionDef interaction = (partner.gender == Gender.Female)
                    ? defs.NuzzleSexDeclinedFemale
                    : defs.NuzzleSexDeclinedMale;

                pawn.interactions?.TryInteractWith(partner, interaction);
            };
            interactToil.defaultCompleteMode = ToilCompleteMode.Instant;
            yield return interactToil;
        }
    }
}


code:
using System.Collections.Generic;
using Verse;
using Verse.AI;
using RimWorld;
using rjw;
using HarmonyLib;
using UnityEngine; // Needed for sex jobs and SexUtility

namespace ElToro_BAddon
{
    public class JobDriver_TrainInterspeciesSex : JobDriver
    {
        protected Pawn Trainee => (Pawn)this.job.targetA.Thing;
        protected Pawn MatingPartner => (Pawn)this.job.targetB.Thing;

        public override bool TryMakePreToilReservations(bool errorOnFailed)
        {
            return pawn.Reserve(Trainee, job, 1, -1, null, errorOnFailed) &&
                   pawn.Reserve(MatingPartner, job, 1, -1, null, errorOnFailed);
        }

        protected override IEnumerable<Toil> MakeNewToils()
        {
            this.FailOn(() => pawn.Drafted);
            this.FailOn(() => Trainee.Downed);
            this.FailOn(() => MatingPartner.Downed);
            this.FailOnDespawnedOrNull(TargetIndex.A);
            this.FailOnDespawnedOrNull(TargetIndex.B);
            TrainableDef trainable = DefDatabase<TrainableDef>.GetNamed("InterspeciesAnimalSex");
            bool trainResult = true;

            // 1. Trainer goes to Trainee
            yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch);

            // 2. Trainer and Trainee go to Partner
            var gotoPartner = new Toil();
            gotoPartner.defaultCompleteMode = ToilCompleteMode.PatherArrival;
            gotoPartner.initAction = delegate
            {
                Job partnerWait = JobMaker.MakeJob(JobDefOf.Wait);
                MatingPartner.jobs.StartJob(partnerWait, JobCondition.InterruptForced);
                pawn.pather.StartPath(MatingPartner.Position, PathEndMode.OnCell);
                Trainee.jobs.StopAll();
                Job job = JobMaker.MakeJob(JobDefOf.GotoMindControlled, MatingPartner.Position);
                Trainee.jobs.StartJob(job, JobCondition.InterruptForced);
            };
            yield return gotoPartner;

            var waitTrainee = new Toil();
            waitTrainee.defaultCompleteMode = ToilCompleteMode.Delay;
            waitTrainee.initAction = delegate
            {
                ticksLeftThisToil = 5000;
            };
            waitTrainee.tickAction = delegate
            {
                if (Trainee.Position.AdjacentToCardinal(pawn.Position) || pawn.PositionHeld == Trainee.PositionHeld)
                {
                    Trainee.jobs.EndCurrentJob(JobCondition.Succeeded, true);
                    ReadyForNextToil();
                }
            };
            yield return waitTrainee;

            // 3. Repositioning step
            IntVec3 plannedHumanPos = IntVec3.Invalid;
            IntVec3 plannedTraineePos = IntVec3.Invalid;

            var repositionStep = new Toil();
            repositionStep.defaultCompleteMode = ToilCompleteMode.Delay;
            repositionStep.initAction = delegate
            {
                ticksLeftThisToil = 5000;
                Map map = pawn.Map;
                IntVec3 partnerPos = MatingPartner.Position;

                // Preferred layout: 2 tiles below
                IntVec3 traineePos = partnerPos + new IntVec3(0, 0, -2);
                IntVec3 humanPos = partnerPos + new IntVec3(-1, 0, -2);

                bool preferredLayoutAvailable =
                    traineePos.Standable(map) &&
                    humanPos.Standable(map);

                if (!preferredLayoutAvailable)
                {
                    // Fallback layout: Diagonal
                    traineePos = partnerPos + new IntVec3(0, 0, -1);
                    humanPos = partnerPos + new IntVec3(-1, 0, -1);

                    bool fallbackLayoutAvailable =
                        traineePos.Standable(map) &&
                        humanPos.Standable(map);

                    if (!fallbackLayoutAvailable)
                    {
                        // Emergency fallback: stay where they are
                        humanPos = pawn.Position;
                        traineePos = Trainee.Position;
                    }
                }

                plannedHumanPos = humanPos;
                plannedTraineePos = traineePos;

                if (humanPos.IsValid && traineePos.IsValid)
                {
                    pawn.pather.StartPath(humanPos, PathEndMode.OnCell);
                    Job job = JobMaker.MakeJob(JobDefOf.GotoMindControlled, traineePos);
                    Trainee.jobs.StartJob(job, JobCondition.InterruptForced);
                }
            };
            repositionStep.tickAction = delegate
            {
                if (pawn.Position == plannedHumanPos && Trainee.Position == plannedTraineePos)
                {
                    Trainee.jobs.EndCurrentJob(JobCondition.Succeeded, true);
                    ReadyForNextToil();
                }
            };
            yield return repositionStep;

            // 5. Trainer (human) Waits and tries training
            var waitAndTrain = Toils_General.Wait(300);
            waitAndTrain.initAction = () =>
            {
                Job traineeWait = JobMaker.MakeJob(JobDefOf.Wait);
                traineeWait.expiryInterval = 300;
                Trainee.jobs.StartJob(traineeWait, JobCondition.InterruptForced);
                Trainee.rotationTracker.FaceTarget(MatingPartner);
            };
            waitAndTrain.AddFinishAction(() =>
            {
                float chance = pawn.GetStatValue(StatDefOf.TrainAnimalChance);
                chance *= GenMath.LerpDouble(0f, 1f, 1.5f, 0.5f, Trainee.GetStatValue(StatDefOf.Wildness));

                if (pawn.relations?.DirectRelationExists(PawnRelationDefOf.Bond, Trainee) ?? false)
                    chance *= 5f;

                chance = Mathf.Clamp01(chance);

                string resultText;
                pawn.interactions.TryInteractWith(Trainee, InteractionDefOf.TrainAttempt);
                if (Rand.Value < chance)
                {
                    Trainee.training.Train(trainable, pawn);
                    resultText = "TextMote_TrainSuccess".Translate(trainable.LabelCap, chance.ToStringPercent());
                    RelationsUtility.TryDevelopBondRelation(pawn, Trainee, 0.007f);
                    TaleRecorder.RecordTale(TaleDefOf.TrainedAnimal, pawn, Trainee, trainable);
                    trainResult = true;
                }
                else
                {
                    resultText = "TextMote_TrainFail".Translate(trainable.LabelCap, chance.ToStringPercent());
                    RelationsUtility.TryDevelopBondRelation(pawn, Trainee, 0.0015f);
                    trainResult = false;
                }

                int learnedSteps = 0;
                var stepsField = Traverse.Create(Trainee.training).Field("steps").GetValue<DefMap<TrainableDef, int>>();
                if (stepsField != null)
                    learnedSteps = stepsField[trainable];

                resultText += "\n" + learnedSteps + " / " + trainable.steps;

                MoteMaker.ThrowText((pawn.DrawPos + Trainee.DrawPos) / 2f, pawn.Map, resultText, 5f);
            });
            waitAndTrain.activeSkill = () => SkillDefOf.Animals;
            yield return waitAndTrain;

            // 6. Decide path based on training result
            var decideNextStep = new Toil
            {
                initAction = () =>
                {
                    if (trainResult)
                    {
                        MatingPartner.jobs.StopAll();
                        Trainee.jobs.StopAll();

                        if (pawn.Map.reservationManager.ReservedBy(Trainee, pawn))
                            pawn.Map.reservationManager.Release(Trainee, pawn, job);

                        if (pawn.Map.reservationManager.ReservedBy(MatingPartner, pawn))
                            pawn.Map.reservationManager.Release(MatingPartner, pawn, job);

                        var sexJob = JobMaker.MakeJob(xxx.animalMate, MatingPartner);
                        Trainee.jobs.StartJob(sexJob, JobCondition.InterruptForced);

                        var receivingJob = JobMaker.MakeJob(xxx.gettin_loved, Trainee);
                        MatingPartner.jobs.StartJob(receivingJob, JobCondition.InterruptForced);
                    }
                    else
                    {
                        MatingPartner.jobs.StopAll();
                        Trainee.jobs.StopAll();

                        if (pawn.Map.reservationManager.ReservedBy(Trainee, pawn))
                            pawn.Map.reservationManager.Release(Trainee, pawn, job);

                        if (pawn.Map.reservationManager.ReservedBy(MatingPartner, pawn))
                            pawn.Map.reservationManager.Release(MatingPartner, pawn, job);

                        pawn.jobs.EndCurrentJob(JobCondition.Succeeded);
                    }
                },
                defaultCompleteMode = ToilCompleteMode.Instant
            };
            yield return decideNextStep;

            // 7. (Optional) Human watches mating for a few ticks
            var waitMating = Toils_General.Wait(200);
            waitMating.initAction = () =>
            {
                pawn.rotationTracker.FaceCell(MatingPartner.Position);
            };
            waitMating.tickAction = () =>
            {
                pawn.rotationTracker.FaceCell(MatingPartner.Position);
            };
            waitMating.handlingFacing = true;
            yield return waitMating;
        }
    }
}


code:
using Verse;
using Verse.AI;
using rjw;
using UnityEngine;
using RimWorld;
using static HarmonyLib.Code;

namespace ElToro_BAddon
{
    public class JobGiver_InterspiciesMating : ThinkNode_JobGiver
    {
        protected override Job TryGiveJob(Pawn animal)
        {
            if (!Settings.AnimalInitiatedMating)
                return null;

            if (!SexUtility.ReadyForLovin(animal))
                return null;

            if (!xxx.is_healthy(animal) || !CasualSex_Helper.CanHaveSex(animal))
                return null;

            bool trainedAnimal = animal.training?.HasLearned(defs.InterspeciesAnimalSex) ?? false;
            bool trainedHuman = animal.training?.HasLearned(defs.InterspeciesHumanSex) ?? false;

            float scoreHuman;
            float scoreAnimal;

            var comp = animal.TryGetComp<CompAnimalSexMemory>();

            if (trainedAnimal && !trainedHuman)
                return TryAnimalSex(animal);
            if (trainedHuman && !trainedAnimal)
                return TryHumanSex(animal);

            scoreHuman = 0.5f;
            scoreAnimal = 0.5f;

            var paraphilia = animal.health?.hediffSet?.GetFirstHediffOfDef(xxx.humanlikeParaphilia);
            if (paraphilia != null)
            {
                float severity = paraphilia.Severity;
                scoreHuman += 0.25f * severity;
                scoreAnimal -= 0.25f * severity;
            }

            int fixatedHuman = 0;
            int fixatedAnimal = 0;
            int preferredHuman = 0;
            int preferredAnimal = 0;

            foreach (var rel in animal.relations.DirectRelations)
            {
                if (rel.otherPawn == null) continue;

                bool otherHuman = xxx.is_human(rel.otherPawn);
                bool otherAnimal = xxx.is_animal(rel.otherPawn);

                if (rel.def == defs.FixatedMatingPartner)
                {
                    if (otherHuman) fixatedHuman++;
                    if (otherAnimal) fixatedAnimal++;
                }
                else if (rel.def == defs.PreferredMatingPartner)
                {
                    if (otherHuman) preferredHuman++;
                    if (otherAnimal) preferredAnimal++;
                }
            }

            scoreHuman += 0.15f * fixatedHuman + 0.05f * preferredHuman;
            scoreAnimal += 0.15f * fixatedAnimal + 0.05f * preferredAnimal;

            if (comp.HasTrait(SexTrait.LikesHumans))
            {
                scoreHuman *= 1.25f;
                scoreAnimal *= 0.75f;
            }

            if (comp.HasTrait(SexTrait.LikesAnimals))
            {
                scoreHuman *= 0.75f;
                scoreAnimal *= 1.25f;
            }

            bool rollHuman = Rand.Chance(Mathf.Min(scoreHuman, 0.9f));
            bool rollAnimal = Rand.Chance(Mathf.Min(scoreAnimal, 0.9f));

            if (rollHuman && !rollAnimal)
                return TryHumanSex(animal);
            if (rollAnimal && !rollHuman)
                return TryAnimalSex(animal);

            if (scoreHuman > scoreAnimal)
                return TryHumanSex(animal) ?? TryAnimalSex(animal);
            if (scoreAnimal > scoreHuman)
                return TryAnimalSex(animal) ?? TryHumanSex(animal);

            return TryHumanSex(animal) ?? TryAnimalSex(animal);
        }

        private Job TryHumanSex(Pawn animal)
        {
            bool trainedHuman = animal.training?.HasLearned(defs.InterspeciesHumanSex) ?? false;
            if (!trainedHuman)
            {
                return null;
            }

            Pawn partner = MatingHelper.FindBestSexPartner(animal);
            if (partner != null)
            {
                return JobMaker.MakeJob(defs.InterspiciesMatingInteraction, partner);
            }
            return null;
        }

        private Job TryAnimalSex(Pawn animal)
        {
            bool trainedAnimal = animal.training?.HasLearned(defs.InterspeciesAnimalSex) ?? false;
            if (!trainedAnimal)
            {
                return null;
            }

            Pawn partner = MatingHelper.FindBestSexPartner(animal, false);
            if (partner != null && animal.CanReserve(partner))
                return JobMaker.MakeJob(xxx.animalMate, partner);

            return null;
        }
        public static Job TryGiveJobPublic(Pawn animal)
        {
            var giver = new JobGiver_InterspiciesMating();
            return giver.TryGiveJob(animal);
        }
    }
}


code:
using HarmonyLib;
using Verse;
using rjw;

namespace ElToro_BAddon.Patches
{
    [HarmonyPatch(typeof(ThinkNode_ConditionalCanMateBonded), "Satisfied")]
    public static class Patch_ConditionalCanMateBonded
    {
        [HarmonyPrefix]
        public static bool Prefix(Pawn p, ref bool __result)
        {
            if (!xxx.is_animal(p))
            {
                __result = false;
                return false;
            }

            __result = true;
            return false; // Skip original method
        }
    }
}


code:
using HarmonyLib;
using RimWorld;
using Verse;
using Verse.AI;
using rjw;

namespace ElToro_BAddon
{
    [HarmonyPatch(typeof(JobGiver_MateBonded), "TryGiveJob")]
    public static class Patch_JobGiver_MateBonded_Replacer
    {
        [HarmonyPrefix]
        public static bool Prefix(Pawn animal, ref Job __result)
        {
            if (!SexUtility.ReadyForLovin(animal) || !xxx.IsTargetPawnOkay(animal) || !CasualSex_Helper.CanHaveSex(animal))
            {
                __result = null;
                return false; // skip original
            }

            Pawn partner = MatingHelper.FindBestSexPartner(animal);
            if (partner != null)
            {
                bool success = animal.interactions.TryInteractWith(partner, defs.NuzzleSex);
                ModLog.Message($"[MateBonded Patch] Triggered NuzzleSex: {animal.LabelShortCap} → {partner.LabelShortCap}, Success: {success}");
            }

            __result = null;
            return false;
        }
    }
}


code:
using ElToro_BAddon;
using HarmonyLib;
using Verse;
using rjw;

[HarmonyPatch(typeof(AfterSexUtility), "UpdateRecords")]
public static class Patch_AnimalInitiatedMating_AfterSexUtilityUpdateRecords
{
    public static void Postfix(SexProps props)
    {
        if (props.pawn == null || props.partner == null)
            return;

        bool pawnIsAnimal = xxx.is_animal(props.pawn);
        bool partnerIsAnimal = xxx.is_animal(props.partner);
        bool pawnIsHuman = xxx.is_human(props.pawn);
        bool partnerIsHuman = xxx.is_human(props.partner);
        bool isRape = props.isRape;

        if (pawnIsAnimal)
        {
            var comp = props.pawn.TryGetComp<CompAnimalSexMemory>();
            comp?.GiveSexTrait();
        }

        if (partnerIsAnimal)
        {
            var comp = props.partner.TryGetComp<CompAnimalSexMemory>();
            comp?.GiveSexTrait();
        }

        bool allowAffinity =
            (!isRape && (pawnIsAnimal && partnerIsHuman || partnerIsAnimal && pawnIsHuman)) ||
            (pawnIsAnimal && partnerIsAnimal);

        if (allowAffinity)
        {
            if (pawnIsAnimal)
            {
                var comp = props.pawn.TryGetComp<CompAnimalSexMemory>();
                comp?.IncreaseAffinity(props.partner);
            }

            if (partnerIsAnimal)
            {
                var comp = props.partner.TryGetComp<CompAnimalSexMemory>();
                comp?.IncreaseAffinity(props.pawn);
            }
        }
    }
}


code:
using HarmonyLib;
using RimWorld;
using rjw;
using Verse;
using Verse.AI;

namespace ElToro_BAddon
{
    [HarmonyPatch(typeof(WorkGiver_Train), nameof(WorkGiver_Train.JobOnThing))]
    public static class Patch_AnimalInitiatedMating_WorkGiver_Train_JobOnThing
    {
        static void Postfix(Pawn pawn, Thing t, bool forced, ref Job __result)
        {
            if (__result == null || __result.def != JobDefOf.Train)
                return;
            Pawn trainee = t as Pawn;

            TrainableDef nextTrainable = trainee.training.NextTrainableToTrain();
            if (nextTrainable == null)
                return;

            if (nextTrainable.defName == "InterspeciesAnimalSex")
            {
                Pawn matingPartner = MatingHelper.FindHighestScoringAnimalPartner(trainee);

                if (!CasualSex_Helper.CanHaveSex(pawn))
                    return;

                if (matingPartner == null)
                {
                    __result = null;
                    return;
                }

                __result = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("TrainInterspeciesSex"), trainee, matingPartner);
            }
        }
    }
}


code:
using rjw;
using Verse;
using Verse.AI;

namespace ElToro_BAddon
{
	/// <summary>
	/// Called to determine if the animal can mate(vanilla reproductory sex) with animals.
	/// </summary>
	public class ThinkNode_ConditionalAnimalInitiatedMating : ThinkNode_Conditional
	{
		protected override bool Satisfied(Pawn p)
        {
            if (!xxx.is_animal(p))
            {
                return false;
            }

            bool trainedAnimal = p.training?.HasLearned(defs.InterspeciesAnimalSex) ?? false;
            bool trainedHuman = p.training?.HasLearned(defs.InterspeciesHumanSex) ?? false;

            if (trainedAnimal || trainedHuman)
            {
                return true;
            }

            return false;
        }
    }
}