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

	<Operation Class="PatchOperationFindMod">
   		<mods>

Core SK

   		</mods>
		<match Class="PatchOperationSequence">
			<success>Always</success>
            <operations>

                    <xpath>/Defs/Verse.ThingDef/comps</xpath>
                    <success>Always</success>
                    <nomatch Class="PatchOperationAdd">
                    <xpath>/Defs/Verse.ThingDef</xpath>
                    <value>
                        <comps />
                    </value>
                    </nomatch>

                    <xpath>/Defs/Verse.ThingDef/comps</xpath>
                    <value>

                    </value>

            </operations>
		</match>
    </Operation>

</Patch>


code:
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Rimworld-Animations")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Rimworld-Animations")]
[assembly: AssemblyCopyright("Copyright ©  2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("71b05d71-67b2-4014-82cd-18c20ac0882f")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

license: unknown


code:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{71B05D71-67B2-4014-82CD-18C20AC0882F}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Rimworld_Animations</RootNamespace>
    <AssemblyName>Rimworld-Animations</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>false</DebugSymbols>
    <DebugType>none</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>1.2\Assemblies\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>none</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="0Harmony">
      <HintPath>..\..\..\..\workshop\content\294100\2009463077\Current\Assemblies\0Harmony.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="AlienRace">
      <HintPath>..\..\..\..\workshop\content\294100\839005762\1.2\Assemblies\AlienRace.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Assembly-CSharp, Version=1.1.7397.36407, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="RJW">
      <HintPath>..\rjw-master\1.2\Assemblies\RJW.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
    <Reference Include="UnityEngine, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\RimWorldWin64_Data\Managed\UnityEngine.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\..\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
      <Private>False</Private>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Source\Actors\Actor.cs" />
    <Compile Include="Source\Actors\AlienRaceOffset.cs" />
    <Compile Include="Source\Actors\BodyTypeOffset.cs" />
    <Compile Include="Source\Animations\AnimationStage.cs" />
    <Compile Include="Source\Animations\Clips\BaseAnimationClip.cs" />
    <Compile Include="Source\Animations\Clips\PawnAnimationClip.cs" />
    <Compile Include="Source\Animations\Clips\ThingAnimationClip.cs" />
    <Compile Include="Source\Animations\Keyframes\Keyframe.cs" />
    <Compile Include="Source\Animations\Keyframes\PawnKeyframe.cs" />
    <Compile Include="Source\Animations\Keyframes\ThingKeyframe.cs" />
    <Compile Include="Source\AnimationUtility.cs" />
    <Compile Include="Source\Comps\CompBodyAnimator.cs" />
    <Compile Include="Source\Comps\CompProperties_BodyAnimator.cs" />
    <Compile Include="Source\Comps\CompProperties_ThingAnimator.cs" />
    <Compile Include="Source\Comps\CompThingAnimator.cs" />
    <Compile Include="Source\Defs\AnimationDef.cs" />
    <Compile Include="Source\JobDrivers\JobDriver_SexBaseRecieverLovedForAnimation.cs" />
    <Compile Include="Source\JobDrivers\JobDriver_SexCasualForAnimation.cs" />
    <Compile Include="Source\MainTabWindows\WorldComponent_UpdateMainTab.cs" />
    <Compile Include="Source\MainTabWindows\MainTabWindow_OffsetConfigure.cs" />
    <Compile Include="Source\MainTabWindows\OffsetMainButtonDefOf.cs" />
    <Compile Include="Source\Patches\HarmonyPatch_AlienRace.cs" />
    <Compile Include="Source\Patches\HarmonyPatch_CSL.cs" />
    <Compile Include="Source\Patches\HarmonyPatch_DontShaveYourHead.cs" />
    <Compile Include="Source\Patches\HarmonyPatch_FacialAnimation.cs" />
    <Compile Include="Source\Patches\HarmonyPatch_HatsDisplaySelection.cs" />
    <Compile Include="Source\Patches\HarmonyPatch_PawnRenderer.cs" />
    <Compile Include="Source\Patches\HarmonyPatch_PawnRotation.cs" />
    <Compile Include="Source\Patches\HarmonyPatch_Pawn_DrawTracker.cs" />
    <Compile Include="Source\Patches\HarmonyPatch_ShowHairWithHats.cs" />
    <Compile Include="Source\Patches\Harmony_PatchAll.cs" />
    <Compile Include="Source\Patches\rjwPatches\HarmonyPatch_DoLovinAnimationPatch.cs" />
    <Compile Include="Source\Patches\rjwPatches\HarmonyPatch_DrawSemen.cs" />
    <Compile Include="Source\Patches\rjwPatches\HarmonyPatch_JobDriver_SexBaseInitiator.cs" />
    <Compile Include="Source\Patches\rjwPatches\HarmonyPatch_JoinInBedGiveJob.cs" />
    <Compile Include="Source\Patches\rjwPatches\HarmonyPatch_PlayAnimJoinInBedRMB.cs" />
    <Compile Include="Source\Patches\rjwPatches\HarmonyPatch_PlaySexSounds.cs" />
    <Compile Include="Source\Patches\rjwPatches\HarmonyPatch_SexTick.cs" />
    <Compile Include="Source\Patches\rjwPatches\HarmonyPatch_WorkGiverSex.cs" />
    <Compile Include="Source\Settings\AnimationSettings.cs" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="About\About.xml" />
    <Content Include="About\Manifest.xml" />
    <Content Include="Defs\AnimationDefs\Animations_Beast.xml" />
    <Content Include="Defs\AnimationDefs\Animations_Lesbian.xml" />
    <Content Include="Defs\AnimationDefs\Animations_Masturbate.xml" />
    <Content Include="Defs\AnimationDefs\Animations_Multi.xml" />
    <Content Include="Defs\AnimationDefs\Animations_vanilla.xml" />
    <Content Include="Defs\AnimationDefs\Animations_Vanilla2.xml" />
    <Content Include="Defs\JobDefs\Jobs_SexForAnim.xml" />
    <Content Include="Defs\MainTabDefs\MainButtonDef.xml" />
    <Content Include="Defs\SoundDefs\Sounds_Sex.xml" />
    <Content Include="LoadFolders.xml" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>


code:
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29905.134
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rimworld-Animations", "Rimworld-Animations.csproj", "{71B05D71-67B2-4014-82CD-18C20AC0882F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Patch_HatsDisplaySelection", "Patch_HatsDisplaySelection\Patch_HatsDisplaySelection.csproj", "{BA766964-1716-422D-A09E-29426F8EB9D5}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{71B05D71-67B2-4014-82CD-18C20AC0882F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{71B05D71-67B2-4014-82CD-18C20AC0882F}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{71B05D71-67B2-4014-82CD-18C20AC0882F}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{71B05D71-67B2-4014-82CD-18C20AC0882F}.Release|Any CPU.Build.0 = Release|Any CPU
		{BA766964-1716-422D-A09E-29426F8EB9D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{BA766964-1716-422D-A09E-29426F8EB9D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{BA766964-1716-422D-A09E-29426F8EB9D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{BA766964-1716-422D-A09E-29426F8EB9D5}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {7E01292A-D76F-4CA3-AA64-085B84ECD905}
	EndGlobalSection
EndGlobal


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;

namespace Rimworld_Animations {
    public class Actor {
        public List<string> defNames;
        public List<string> requiredGenitals;
        public List<AlienRaceOffset> raceOffsets;
        public List<string> blacklistedRaces;
        public bool initiator = false;
        public string gender;
        public bool isFucking = false;
        public bool isFucked = false;
        public bool controlGenitalAngle = false;
        public List<BodyDef> bodyDefTypes = new List<BodyDef>();
        public BodyTypeOffset bodyTypeOffset = new BodyTypeOffset();
        public Vector3 offset = new Vector2(0, 0);

    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace Rimworld_Animations {
    public class AlienRaceOffset {

        public string defName;
        public Vector2 offset;

    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace Rimworld_Animations {
    public class BodyTypeOffset {

        public Vector2? Male;
        public Vector2? Female;
        public Vector2? Thin;
        public Vector2? Hulk;
        public Vector2? Fat;

    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.AI;

namespace Rimworld_Animations {
    public static class AnimationUtility {
        /*
         Note: always make the list in this order:
             Female pawns, animal female pawns, male pawns, animal male pawns
        */
        public static AnimationDef tryFindAnimation(ref List<Pawn> participants, rjw.xxx.rjwSextype sexType = 0, rjw.SexProps sexProps = null) {

            participants =
                participants.OrderBy(p => p.jobs.curDriver is rjw.JobDriver_SexBaseInitiator)
                .OrderBy(p => p == sexProps.Giver)
                .OrderByDescending(p => rjw.GenderHelper.GetSex(p) == rjw.GenderHelper.Sex.futa)
                .OrderBy(p => rjw.xxx.can_fuck(p))
                .ToList();

            List<Pawn> localParticipants = new List<Pawn>(participants);

            IEnumerable<AnimationDef> options = DefDatabase<AnimationDef>.AllDefs.Where((AnimationDef x) => {

                if (x.actors.Count != localParticipants.Count) {
                    return false;
                }
                for (int i = 0; i < x.actors.Count; i++) {

                    if (rjw.RJWPreferenceSettings.Malesex == rjw.RJWPreferenceSettings.AllowedSex.Nohomo) {
                        if (rjw.xxx.is_male(localParticipants[i]) && x.actors[i].isFucked) {
                            return false;
                        }
                }

                    if ((x.actors[i].blacklistedRaces != null) && x.actors[i].blacklistedRaces.Contains(localParticipants[i].def.defName)) {
                        if(AnimationSettings.debugMode)
                            Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is blacklisted");
                        return false;
                    }

                    if(x.actors[i].defNames.Contains("Human")) {
                        if (!rjw.xxx.is_human(localParticipants[i])) {
                            if (AnimationSettings.debugMode)
                                Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is not human");

                            return false;
                        }

                    }
                    else if (!x.actors[i].bodyDefTypes.Contains(localParticipants[i].RaceProps.body)) {

                        if (!x.actors[i].defNames.Contains(localParticipants[i].def.defName)) {

                            if (rjw.RJWSettings.DevMode) {
                                string animInfo = x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is not ";
                                foreach(String defname in x.actors[i].defNames) {
                                    animInfo += defname + ", ";
                                }
                                if (AnimationSettings.debugMode)
                                    Log.Message(animInfo);
                            }

                            return false;
                        }
                    }
                    //genitals checking

                    if(!GenitalCheckForPawn(x.actors[i].requiredGenitals, localParticipants[i], out string failReason)) {
                        Debug.Log("Didn't select " + x.defName + ", " + localParticipants[i].Name + " " + failReason);
                        return false;
                    }

                    //TESTING ANIMATIONS ONLY REMEMBER TO COMMENT OUT BEFORE PUSH
                    /*
                    if (x.defName != "Cunnilingus")
                        return false;
                    */

                    if (x.actors[i].isFucking && !rjw.xxx.can_fuck(localParticipants[i])) {
                        if (AnimationSettings.debugMode)
                            Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " can't fuck");
                        return false;
                    }

                    if (x.actors[i].isFucked && !rjw.xxx.can_be_fucked(localParticipants[i])) {
                        if (AnimationSettings.debugMode)
                            Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " can't be fucked");
                        return false;
                    }
                }
                return true;
            });
            List<AnimationDef> optionsWithInteractionType = options.ToList().FindAll(x => x.interactionDefTypes != null && x.interactionDefTypes.Contains(sexProps.DictionaryKey));
            if (optionsWithInteractionType.Any()) {
                if (AnimationSettings.debugMode)
                    Log.Message("Selecting animation for interaction type " + sexProps.DictionaryKey.defName + "...");
                return optionsWithInteractionType.RandomElement();
            }
            List<AnimationDef> optionsWithSexType = options.ToList().FindAll(x => x.sexTypes != null && x.sexTypes.Contains(sexType));
            if (optionsWithSexType.Any()) {
                if (AnimationSettings.debugMode)
                    Log.Message("Selecting animation for rjwSexType " + sexType.ToStringSafe() + "...");
                return optionsWithSexType.RandomElement();
            }

            /*
            if(optionsWithInitiator.Any()) {
                if (AnimationSettings.debugMode)
                    Log.Message("Selecting animation for initiators...");
            }
            */

            if (options != null && options.Any()) {
                if (AnimationSettings.debugMode)
                    Log.Message("Randomly selecting animation...");
                return options.RandomElement();
            }
            else
                return null;
        }

        public static void RenderPawnHeadMeshInAnimation(Mesh mesh, Vector3 loc, Quaternion quaternion, Material material, bool portrait, Pawn pawn) {

            if(pawn == null || pawn.Map != Find.CurrentMap) {
                GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
                return;
            }

            CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();

            if (pawnAnimator == null || !pawnAnimator.isAnimating || portrait) {
                GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
            } else {
                Vector3 pawnHeadPosition = pawnAnimator.getPawnHeadPosition();
                pawnHeadPosition.y = loc.y;
                GenDraw.DrawMeshNowOrLater(mesh, pawnHeadPosition, Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up), material, portrait);
            }
        }

        public static void RenderPawnHeadMeshInAnimation(Mesh mesh, Vector3 loc, Quaternion quaternion, Material material, bool portrait, Pawn pawn, float bodySizeFactor = 1) {

            if (pawn == null) {
                GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
                return;
            }

            CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();

            if (pawnAnimator == null || !pawnAnimator.isAnimating || portrait) {
                GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
            }
            else {
                Vector3 pawnHeadPosition = pawnAnimator.getPawnHeadPosition();
                pawnHeadPosition.x *= bodySizeFactor;
                pawnHeadPosition.x *= bodySizeFactor;
                pawnHeadPosition.y = loc.y;
                GenDraw.DrawMeshNowOrLater(mesh, pawnHeadPosition, Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up), material, portrait);
            }
        }

        public static bool GenitalCheckForPawn(List<string> requiredGenitals, Pawn pawn, out string failReason) {

            failReason = null;
            if (requiredGenitals != null) {
                if (requiredGenitals.Contains("Vagina")) {

                    if (!rjw.Genital_Helper.has_vagina(pawn)) {
                        failReason = "missing vagina";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("Penis")) {

                    if (!(rjw.Genital_Helper.has_multipenis(pawn) || rjw.Genital_Helper.has_penis_infertile(pawn) || rjw.Genital_Helper.has_penis_fertile(pawn) || rjw.Genital_Helper.has_ovipositorM(pawn) || rjw.Genital_Helper.has_ovipositorF(pawn))) {
                        failReason = "missing penis";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("Mouth")) {

                    if (!rjw.Genital_Helper.has_mouth(pawn)) {
                        failReason = "missing mouth";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("Anus")) {

                    if (!rjw.Genital_Helper.has_anus(pawn)) {
                        failReason = "missing anus";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("Breasts")) {
                    if (!rjw.Genital_Helper.can_do_breastjob(pawn)) {
                        failReason = "missing breasts";
                        return false;
                    }
                }

                if (requiredGenitals.Contains("NoVagina")) {

                    if (rjw.Genital_Helper.has_vagina(pawn)) {
                        failReason = "has vagina";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("NoPenis")) {

                    if ((rjw.Genital_Helper.has_multipenis(pawn) || rjw.Genital_Helper.has_penis_infertile(pawn) || rjw.Genital_Helper.has_penis_fertile(pawn))) {
                        failReason = "has penis";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("NoMouth")) {

                    if (rjw.Genital_Helper.has_mouth(pawn)) {
                        failReason = "has mouth";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("NoAnus")) {

                    if (rjw.Genital_Helper.has_anus(pawn)) {
                        failReason = "has anus";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("NoBreasts")) {
                    if (rjw.Genital_Helper.can_do_breastjob(pawn)) {
                        failReason = "has breasts";
                        return false;
                    }
                }
            }

            return true;

        }
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Rimworld_Animations {
    public class AnimationStage
    {
        public string stageName;
        public int stageIndex;
        public int playTimeTicks = 0;
        public bool isLooping;
        public List<BaseAnimationClip> animationClips;

        public void initialize() {
            foreach (BaseAnimationClip clip in animationClips) {
                clip.buildSimpleCurves();
                //select playTimeTicks as longest playtime of all the animations
                if(clip.duration > playTimeTicks) {
                    playTimeTicks = clip.duration;
                }
            }
        }
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;

namespace Rimworld_Animations {
    public abstract class BaseAnimationClip
    {
        public List<ThingDef> types; //types of participants
        public int duration;
        public abstract void buildSimpleCurves();
        public string soundDef = null; //for playing sounds
        public int actor;

    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;

namespace Rimworld_Animations {
    public class PawnAnimationClip : BaseAnimationClip {

        public List<PawnKeyframe> keyframes;
        public AltitudeLayer layer = AltitudeLayer.Pawn;

        public Dictionary<int, string> SoundEffects = new Dictionary<int, string>();
        public Dictionary<int, bool> quiver = new Dictionary<int, bool>();
        public SimpleCurve GenitalAngle = new SimpleCurve();
        public SimpleCurve BodyAngle = new SimpleCurve();
        public SimpleCurve HeadAngle = new SimpleCurve();
        public SimpleCurve HeadBob = new SimpleCurve();
        public SimpleCurve BodyOffsetX = new SimpleCurve();
        public SimpleCurve BodyOffsetZ = new SimpleCurve();
        public SimpleCurve HeadFacing = new SimpleCurve();
        public SimpleCurve BodyFacing = new SimpleCurve();

        public override void buildSimpleCurves() {

            int duration = 0;
            //getting the length of the whole clip
            foreach(PawnKeyframe frame in keyframes) {
                duration += frame.tickDuration;
            }

            //guarantees loops don't get cut off mid-anim
            this.duration = duration;

            int keyframePosition = 0;
            foreach (PawnKeyframe frame in keyframes) {

                if (frame.atTick.HasValue) {
                    if (frame.bodyAngle.HasValue)
                        BodyAngle.Add((float)frame.atTick / (float)duration, frame.bodyAngle.Value, true);

                    if (frame.headAngle.HasValue)
                        HeadAngle.Add((float)frame.atTick / (float)duration, frame.headAngle.Value, true);

                    if (frame.bodyOffsetX.HasValue)
                        BodyOffsetX.Add((float)frame.atTick / (float)duration, frame.bodyOffsetX.Value, true);

                    if (frame.bodyOffsetZ.HasValue)
                        BodyOffsetZ.Add((float)frame.atTick / (float)duration, frame.bodyOffsetZ.Value, true);

                    if (frame.headFacing.HasValue)
                        HeadFacing.Add((float)frame.atTick / (float)duration, frame.headFacing.Value, true);

                    if (frame.bodyFacing.HasValue)
                        BodyFacing.Add((float)frame.atTick / (float)duration, frame.bodyFacing.Value, true);

                    if (frame.headBob.HasValue)
                        HeadBob.Add((float)frame.atTick / (float)duration, frame.headBob.Value, true);

                    if (frame.genitalAngle.HasValue)
                        GenitalAngle.Add((float)frame.atTick / (float)duration, frame.genitalAngle.Value, true);

                    if (frame.soundEffect != null) {
                        SoundEffects.Add((int)frame.atTick, frame.soundEffect);
                    }

                }
                else {
                    if (frame.bodyAngle.HasValue)
                        BodyAngle.Add((float)keyframePosition / (float)duration, frame.bodyAngle.Value, true);

                    if (frame.headAngle.HasValue)
                        HeadAngle.Add((float)keyframePosition / (float)duration, frame.headAngle.Value, true);

                    if (frame.bodyOffsetX.HasValue)
                        BodyOffsetX.Add((float)keyframePosition / (float)duration, frame.bodyOffsetX.Value, true);

                    if (frame.bodyOffsetZ.HasValue)
                        BodyOffsetZ.Add((float)keyframePosition / (float)duration, frame.bodyOffsetZ.Value, true);

                    if (frame.headFacing.HasValue)
                        HeadFacing.Add((float)keyframePosition / (float)duration, frame.headFacing.Value, true);

                    if (frame.bodyFacing.HasValue)
                        BodyFacing.Add((float)keyframePosition / (float)duration, frame.bodyFacing.Value, true);

                    if (frame.headBob.HasValue)
                        HeadBob.Add((float)keyframePosition / (float)duration, frame.headBob.Value, true);

                    if (frame.genitalAngle.HasValue)
                        GenitalAngle.Add((float)keyframePosition / (float)duration, frame.genitalAngle.Value, true);

                    if (frame.soundEffect != null) {
                        SoundEffects.Add(keyframePosition, frame.soundEffect);
                    }

                    if(frame.tickDuration != 1 && frame.quiver.HasValue) {

                        quiver.Add(keyframePosition, true);
                        quiver.Add(keyframePosition + frame.tickDuration - 1, false);
                    }
                    keyframePosition += frame.tickDuration;

                }

            }

        }

    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Rimworld_Animations {
    public class ThingAnimationClip : BaseAnimationClip
    {
        public List<ThingKeyframe> keyframes;

        public override void buildSimpleCurves() {

        }
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Rimworld_Animations {
    public abstract class Keyframe
    {
        public int tickDuration = 1;

    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace Rimworld_Animations {
    public class PawnKeyframe : Keyframe
    {
        public float? bodyAngle;
        public float? headAngle;

        public float? genitalAngle;

        public float? bodyOffsetZ;
        public float? bodyOffsetX;

        public float? headBob;
        //todo: add headOffsets l/r?

        public int? bodyFacing;
        public int? headFacing;

        public string soundEffect;
        public bool? quiver;

        public float? atTick;
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Rimworld_Animations {
    public class ThingKeyframe : Keyframe
    {
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using rjw;
using UnityEngine;
using Verse;
using Verse.Sound;

namespace Rimworld_Animations {
    public class CompBodyAnimator : ThingComp
    {
        public Pawn pawn => base.parent as Pawn;
        public PawnGraphicSet Graphics;

        //public CompProperties_BodyAnimator Props => (CompProperties_BodyAnimator)(object)base.props;

        public bool isAnimating {
            get {
                return Animating;
            }
            set {
                Animating = value;

                if(value == true) {
                    SexUtility.DrawNude(pawn);
                } else {
                    pawn.Drawer.renderer.graphics.ResolveAllGraphics();
                    actorsInCurrentAnimation = null;
                }

                PortraitsCache.SetDirty(pawn);
            }
        }
        private bool Animating = false;
        private bool mirror = false, quiver = false, shiver = false;
        private int actor;

        private int lastDrawFrame = -1;

        private int animTicks = 0, stageTicks = 0, clipTicks = 0;
        private int curStage = 0;
        private float clipPercent = 0;

        public Vector3 anchor = Vector3.zero, deltaPos = Vector3.zero, headBob = Vector3.zero;
        public float bodyAngle = 0, headAngle = 0, genitalAngle = 0;
        public Rot4 headFacing = Rot4.North, bodyFacing = Rot4.North;

        public List<Pawn> actorsInCurrentAnimation;

        public bool controlGenitalAngle = false;

        private AnimationDef anim;
        private AnimationStage stage => anim.animationStages[curStage];
        private PawnAnimationClip clip => (PawnAnimationClip)stage.animationClips[actor];

        public bool Mirror {
            get {
                return mirror;
            }
        }

        public void setAnchor(IntVec3 pos)
        {
            anchor = pos.ToVector3Shifted();
        }
        public void setAnchor(Thing thing) {

            //center on bed
            if(thing is Building_Bed) {
                anchor = thing.Position.ToVector3();
                if (((Building_Bed)thing).SleepingSlotsCount == 2) {
                    if (thing.Rotation.AsInt == 0) {
                        anchor.x += 1;
                        anchor.z += 1;
                    }
                    else if (thing.Rotation.AsInt == 1) {
                        anchor.x += 1;
                    }
                    else if(thing.Rotation.AsInt == 3) {
                        anchor.z += 1;
                    }

                }
                else {
                    if(thing.Rotation.AsInt == 0) {
                        anchor.x += 0.5f;
                        anchor.z += 1f;
                    }
                    else if(thing.Rotation.AsInt == 1) {
                        anchor.x += 1f;
                        anchor.z += 0.5f;
                    }
                    else if(thing.Rotation.AsInt == 2) {
                        anchor.x += 0.5f;
                    } else {
                        anchor.z += 0.5f;
                    }
                }
            }
            else {
                anchor = thing.Position.ToVector3Shifted();
            }
        }
        public void StartAnimation(AnimationDef anim, List<Pawn> actors, int actor, bool mirror = false, bool shiver = false, bool fastAnimForQuickie = false) {

            actorsInCurrentAnimation = actors;
            AlienRaceOffset raceOffset = anim?.actors[actor]?.raceOffsets?.Find(x => x.defName == pawn.def.defName);

            if (raceOffset != null) {
                anchor.x += mirror ? raceOffset.offset.x * -1f : raceOffset.offset.x;
                anchor.z += raceOffset.offset.y;
            }

            //change the offset based on pawn body type
            if(pawn?.story?.bodyType != null) {
                if (pawn.story.bodyType == BodyTypeDefOf.Fat && anim?.actors[actor]?.bodyTypeOffset?.Fat != null) {
                    anchor.x += anim.actors[actor].bodyTypeOffset.Fat.Value.x * (mirror ? -1f : 1f);
                    anchor.z += anim.actors[actor].bodyTypeOffset.Fat.Value.y;
                }
                else if (pawn.story.bodyType == BodyTypeDefOf.Female && anim?.actors[actor]?.bodyTypeOffset?.Female != null) {
                    anchor.x += anim.actors[actor].bodyTypeOffset.Female.Value.x * (mirror ? -1f : 1f);
                    anchor.z += anim.actors[actor].bodyTypeOffset.Female.Value.y;
                }
                else if (pawn.story.bodyType == BodyTypeDefOf.Male && anim?.actors[actor]?.bodyTypeOffset?.Male != null) {
                    anchor.x += anim.actors[actor].bodyTypeOffset.Male.Value.x * (mirror ? -1f : 1f);
                    anchor.z += anim.actors[actor].bodyTypeOffset.Male.Value.y;
                }
                else if (pawn.story.bodyType == BodyTypeDefOf.Thin && anim?.actors[actor]?.bodyTypeOffset?.Thin != null) {
                    anchor.x += anim.actors[actor].bodyTypeOffset.Thin.Value.x * (mirror ? -1f : 1f);
                    anchor.z += anim.actors[actor].bodyTypeOffset.Thin.Value.y;
                }
                else if (pawn.story.bodyType == BodyTypeDefOf.Hulk && anim?.actors[actor]?.bodyTypeOffset?.Hulk != null) {
                    anchor.x += anim.actors[actor].bodyTypeOffset.Hulk.Value.x * (mirror ? -1f : 1f);
                    anchor.z += anim.actors[actor].bodyTypeOffset.Hulk.Value.y;
                }
            }

            pawn.jobs.posture = PawnPosture.Standing;

            this.actor = actor;
            this.anim = anim;
            this.mirror = mirror;

            curStage = fastAnimForQuickie ? 1 : 0;
            animTicks = 0;
            stageTicks = 0;
            clipTicks = 0;

            quiver = false;
            this.shiver = shiver && AnimationSettings.rapeShiver;

            controlGenitalAngle = anim.actors[actor].controlGenitalAngle;

            isAnimating = true;
            //tick once for initialization
            tickAnim();

        }

        public override void CompTick() {

            base.CompTick();

            if(isAnimating) {
                if (pawn.Dead || pawn?.jobs?.curDriver == null || (pawn?.jobs?.curDriver != null && !(pawn?.jobs?.curDriver is rjw.JobDriver_Sex))) {
                    isAnimating = false;
                }
                else {
                    tickAnim();
                }
            }
        }
        public void animatePawn(ref Vector3 rootLoc, ref float angle, ref Rot4 bodyFacing, ref Rot4 headFacing) {

            if(!isAnimating) {
                return;
            }
            rootLoc = anchor + deltaPos;
            angle = bodyAngle;
            bodyFacing = this.bodyFacing;
            headFacing = this.headFacing;

        }

        public void tickGraphics(PawnGraphicSet graphics) {
            this.Graphics = graphics;
        }

        public void tickAnim() {

            if (!isAnimating) return;

            if (anim == null) {
                isAnimating = false;
                return;
            }

            animTicks++;

            if (animTicks < anim.animationTimeTicks) {
                tickStage();
            } else {

                if(LoopNeverending())
                {
                    ResetOnLoop();
                } else
                {
                    isAnimating = false;
                }

            }
        }

        public void tickStage()
        {
            stageTicks++;

            if(stageTicks >= stage.playTimeTicks) {

                curStage++;

                stageTicks = 0;
                clipTicks = 0;
                clipPercent = 0;
            }

            if(curStage >= anim.animationStages.Count) {
                if (LoopNeverending())
                {
                    ResetOnLoop();
                }
                else
                {
                    isAnimating = false;
                    pawn.jobs.curDriver.ReadyForNextToil();
                }

            } else {
                tickClip();
            }

        }

        public void tickClip() {

            clipTicks++;

            //play sound effect
            if(rjw.RJWSettings.sounds_enabled && clip.SoundEffects.ContainsKey(clipTicks) && AnimationSettings.soundOverride) {

                SoundInfo sound = new TargetInfo(pawn.Position, pawn.Map);
                string soundEffectName = clip.SoundEffects[clipTicks];

                if ((pawn.jobs.curDriver as JobDriver_Sex).isAnimalOnAnimal)
                {
                    sound.volumeFactor *= RJWSettings.sounds_animal_on_animal_volume;
                }

                if(soundEffectName.StartsWith("Voiceline_"))
                {
                    sound.volumeFactor *= RJWSettings.sounds_voice_volume;
                }

                if (clip.SoundEffects[clipTicks] == "Cum") {

                    sound.volumeFactor *= RJWSettings.sounds_cum_volume;
                    considerApplyingSemen();

                } else
                {
                    sound.volumeFactor *= RJWSettings.sounds_sex_volume;
                }

                SoundDef.Named(soundEffectName).PlayOneShot(sound);

            }
            if(AnimationSettings.orgasmQuiver && clip.quiver.ContainsKey(clipTicks)) {
                quiver = clip.quiver[clipTicks];
            }

            //loop animation if possible
            if (clipPercent >= 1 && stage.isLooping) {
                clipTicks = 1;//warning: don't set to zero or else calculations go wrong
            }
            clipPercent = (float)clipTicks / (float)clip.duration;

            calculateDrawValues();
        }

        public void considerApplyingSemen()
        {
            if(AnimationSettings.applySemenOnAnimationOrgasm && (pawn?.jobs?.curDriver is JobDriver_Sex))
            {
                Pawn partner = (pawn.jobs.curDriver as JobDriver_Sex)?.Partner;

                if (anim.sexTypes.Contains((pawn.jobs.curDriver as JobDriver_Sex).sexType))
                {
                    SemenHelper.calculateAndApplySemen(pawn, partner, (pawn.jobs.curDriver as JobDriver_Sex).sexType);
                }
            }
        }

        public void calculateDrawValues() {

            /*if(Find.TickManager.TickRateMultiplier > 1 && (lastDrawFrame + 1 >= RealTime.frameCount || RealTime.deltaTime < 0.05f)) {
                return;
            }*/

            deltaPos = new Vector3(clip.BodyOffsetX.Evaluate(clipPercent) * (mirror ? -1 : 1), clip.layer.AltitudeFor(), clip.BodyOffsetZ.Evaluate(clipPercent));

            if (AnimationSettings.offsets != null && AnimationSettings.offsets.ContainsKey(CurrentAnimation.defName + pawn.def.defName + ActorIndex)) {
                deltaPos.x += AnimationSettings.offsets[CurrentAnimation.defName + pawn.def.defName + ActorIndex].x * (mirror ? -1 : 1);
                deltaPos.z += AnimationSettings.offsets[CurrentAnimation.defName + pawn.def.defName + ActorIndex].y;
            }

            bodyAngle = (clip.BodyAngle.Evaluate(clipPercent) + (quiver || shiver ? ((Rand.Value * AnimationSettings.shiverIntensity) - (AnimationSettings.shiverIntensity / 2f)) : 0f)) * (mirror ? -1 : 1);
            headAngle = clip.HeadAngle.Evaluate(clipPercent) * (mirror ? -1 : 1);

            if (controlGenitalAngle) {
                genitalAngle = clip.GenitalAngle.Evaluate(clipPercent) * (mirror ? -1 : 1);
            }

            if (AnimationSettings.rotation != null && AnimationSettings.rotation.ContainsKey(CurrentAnimation.defName + pawn.def.defName + ActorIndex)) {
                float offsetRotation = AnimationSettings.rotation[CurrentAnimation.defName + pawn.def.defName + ActorIndex] * (Mirror ? -1 : 1);
                genitalAngle += offsetRotation;
                bodyAngle += offsetRotation;
                headAngle += offsetRotation;
            }

            //don't go past 360 or less than 0
            if (bodyAngle < 0) bodyAngle = 360 - ((-1f*bodyAngle) % 360);
            if (bodyAngle > 360) bodyAngle %= 360;

            if (headAngle < 0) headAngle = 360 - ((-1f * headAngle) % 360);
            if (headAngle > 360) headAngle %= 360;

            if (genitalAngle < 0) genitalAngle = 360 - ((-1f * genitalAngle) % 360);
            if (genitalAngle > 360) genitalAngle %= 360;

            bodyFacing = mirror ? new Rot4((int)clip.BodyFacing.Evaluate(clipPercent)).Opposite : new Rot4((int)clip.BodyFacing.Evaluate(clipPercent));

            bodyFacing = new Rot4((int)clip.BodyFacing.Evaluate(clipPercent));
            if(bodyFacing.IsHorizontal && mirror) {
                bodyFacing = bodyFacing.Opposite;
            }

            headFacing = new Rot4((int)clip.HeadFacing.Evaluate(clipPercent));
            if(headFacing.IsHorizontal && mirror) {
                headFacing = headFacing.Opposite;
            }
            headBob = new Vector3(0, 0, clip.HeadBob.Evaluate(clipPercent));

            lastDrawFrame = RealTime.frameCount;

        }

        public Vector3 getPawnHeadPosition() {

            Vector3 headPos = anchor + deltaPos + Quaternion.AngleAxis(bodyAngle, Vector3.up) * (pawn.Drawer.renderer.BaseHeadOffsetAt(headFacing) + headBob);

            return headPos;

        }

        public AnimationDef CurrentAnimation {
            get {
                return anim;
            }
        }

        public int ActorIndex {
            get {
                return actor;
            }
        }

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

            Scribe_Defs.Look(ref anim, "RJWAnimations-Anim");

            Scribe_Values.Look(ref animTicks, "RJWAnimations-animTicks", 1);
            Scribe_Values.Look(ref stageTicks, "RJWAnimations-stageTicks", 1);
            Scribe_Values.Look(ref clipTicks, "RJWAnimations-clipTicks", 1);
            Scribe_Values.Look(ref clipPercent, "RJWAnimations-clipPercent", 1);

            Scribe_Values.Look(ref mirror, "RJWAnimations-mirror");

            Scribe_Values.Look(ref curStage, "RJWAnimations-curStage", 0);
            Scribe_Values.Look(ref actor, "RJWAnimations-actor");

            Scribe_Values.Look(ref Animating, "RJWAnimations-Animating");
            Scribe_Values.Look(ref anchor, "RJWAnimations-anchor");
            Scribe_Values.Look(ref deltaPos, "RJWAnimations-deltaPos");
            Scribe_Values.Look(ref headBob, "RJWAnimations-headBob");
            Scribe_Values.Look(ref bodyAngle, "RJWAnimations-bodyAngle");
            Scribe_Values.Look(ref headAngle, "RJWAnimations-headAngle");

            Scribe_Values.Look(ref genitalAngle, "RJWAnimations-GenitalAngle");
            Scribe_Values.Look(ref controlGenitalAngle, "RJWAnimations-controlGenitalAngle");

            Scribe_Values.Look(ref headFacing, "RJWAnimations-headFacing");
            Scribe_Values.Look(ref headFacing, "RJWAnimations-bodyFacing");

            Scribe_Values.Look(ref quiver, "RJWAnimations-orgasmQuiver");
        }

        public void shiftActorPositionAndRestartAnimation() {
            actor = (actor == anim.actors.Count - 1 ? 0 : actor + 1);

            if (pawn?.story?.bodyType != null) {
                if (pawn.story.bodyType == BodyTypeDefOf.Fat && anim?.actors[actor]?.bodyTypeOffset?.Fat != null) {
                    anchor.x += anim.actors[actor].bodyTypeOffset.Fat.Value.x * (mirror ? -1f : 1f);
                    anchor.z += anim.actors[actor].bodyTypeOffset.Fat.Value.y;
                }
                else if (pawn.story.bodyType == BodyTypeDefOf.Female && anim?.actors[actor]?.bodyTypeOffset?.Female != null) {
                    anchor.x += anim.actors[actor].bodyTypeOffset.Female.Value.x * (mirror ? -1f : 1f);
                    anchor.z += anim.actors[actor].bodyTypeOffset.Female.Value.y;
                }
                else if (pawn.story.bodyType == BodyTypeDefOf.Male && anim?.actors[actor]?.bodyTypeOffset?.Male != null) {
                    anchor.x += anim.actors[actor].bodyTypeOffset.Male.Value.x * (mirror ? -1f : 1f);
                    anchor.z += anim.actors[actor].bodyTypeOffset.Male.Value.y;
                }
                else if (pawn.story.bodyType == BodyTypeDefOf.Thin && anim?.actors[actor]?.bodyTypeOffset?.Thin != null) {
                    anchor.x += anim.actors[actor].bodyTypeOffset.Thin.Value.x * (mirror ? -1f : 1f);
                    anchor.z += anim.actors[actor].bodyTypeOffset.Thin.Value.y;
                }
                else if (pawn.story.bodyType == BodyTypeDefOf.Hulk && anim?.actors[actor]?.bodyTypeOffset?.Hulk != null) {
                    anchor.x += anim.actors[actor].bodyTypeOffset.Hulk.Value.x * (mirror ? -1f : 1f);
                    anchor.z += anim.actors[actor].bodyTypeOffset.Hulk.Value.y;
                }
            }

            curStage = 0;
            animTicks = 0;
            stageTicks = 0;
            clipTicks = 0;

            controlGenitalAngle = anim.actors[actor].controlGenitalAngle;

            tickAnim();
        }

        public bool LoopNeverending()
        {
            if(pawn?.jobs?.curDriver != null &&
                (pawn.jobs.curDriver is JobDriver_Sex) && (pawn.jobs.curDriver as JobDriver_Sex).neverendingsex ||
                (pawn.jobs.curDriver is JobDriver_SexBaseReciever) && (pawn.jobs.curDriver as JobDriver_Sex).Partner?.jobs?.curDriver != null && ((pawn.jobs.curDriver as JobDriver_Sex).Partner.jobs.curDriver as JobDriver_Sex).neverendingsex)
            {
                return true;
            }

            return false;
        }

        public void ResetOnLoop()
        {
            curStage = 1;
            animTicks = 0;
            stageTicks = 0;
            clipTicks = 0;

            tickAnim();
        }
    }
}


code:
 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;

namespace Rimworld_Animations {
    class CompProperties_BodyAnimator : CompProperties
    {
        public CompProperties_BodyAnimator() {

            base.compClass = typeof(CompBodyAnimator);
        }
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Rimworld_Animations {
    class CompProperties_ThingAnimator
    {
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Rimworld_Animations {
    class CompThingAnimator
    {
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;

namespace Rimworld_Animations {
    public class AnimationDef : Def
    {
        public List<AnimationStage> animationStages;
        public List<Actor> actors;
        public int animationTimeTicks = 0; //do not set manually
        public bool sounds = false;
        public List<rjw.xxx.rjwSextype> sexTypes = null;
        public List<InteractionDef> interactionDefTypes = null;

        public override void PostLoad() {
            base.PostLoad();
            foreach(AnimationStage stage in animationStages) {
                stage.initialize();
                animationTimeTicks += stage.playTimeTicks;
            }
        }
    }
}


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

namespace Rimworld_Animations {
	public class JobDriver_SexBaseRecieverLovedForAnimation : JobDriver_SexBaseReciever {

		public readonly TargetIndex ipartner = TargetIndex.A;
		public readonly TargetIndex ibed = TargetIndex.B;

		public override bool TryMakePreToilReservations(bool errorOnFailed) {

			return base.TryMakePreToilReservations(errorOnFailed);
		}

		protected override IEnumerable<Toil> MakeNewToils() {

			setup_ticks();

			float partner_ability = xxx.get_sex_ability(Partner);

			// More/less hearts based on partner ability.
			if (partner_ability < 0.8f)
				ticks_between_thrusts += 100;
			else if (partner_ability > 2.0f)
				ticks_between_thrusts -= 25;

			// More/less hearts based on opinion.
			if (pawn.relations.OpinionOf(Partner) < 0)
				ticks_between_hearts += 50;
			else if (pawn.relations.OpinionOf(Partner) > 60)
				ticks_between_hearts -= 25;

			parteners.Add(Partner);// add job starter, so this wont fail, before Initiator starts his job
								   //--Log.Message("[RJW]JobDriver_GettinLoved::MakeNewToils is called");

			this.FailOnDespawnedOrNull(ipartner);
			this.FailOn(() => !Partner.health.capacities.CanBeAwake);
			this.FailOn(() => pawn.Drafted);
			this.KeepLyingDown(ibed);
			yield return Toils_Reserve.Reserve(ipartner, 1, 0);
			yield return Toils_Reserve.Reserve(ibed, Bed.SleepingSlotsCount, 0);

			Toil get_loved = new Toil();
			get_loved.FailOn(() => {

				for (int i = 0; i < parteners.Count; i++)
                {
					if (parteners[i].CurJobDef != DefDatabase<JobDef>.GetNamed("JoinInBedAnimation", true))
                    {
						return true;
                    }
				}

				return false;

			});
			get_loved.defaultCompleteMode = ToilCompleteMode.Never;
			get_loved.socialMode = RandomSocialMode.Off;
			get_loved.handlingFacing = true;
			get_loved.AddPreTickAction(delegate {
				if (pawn.IsHashIntervalTick(ticks_between_hearts))
					MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
			});
			get_loved.AddEndCondition(() =>
			{
				if (parteners.Count <= 0)
				{
					return JobCondition.Succeeded;
				}
				return JobCondition.Ongoing;

			});
			get_loved.AddFinishAction(delegate {
				if (xxx.is_human(pawn))
					pawn.Drawer.renderer.graphics.ResolveApparelGraphics();
			});
			yield return get_loved;

		}
	}
}


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

namespace Rimworld_Animations {
    class JobDriver_SexCasualForAnimation : JobDriver_SexBaseInitiator {

        public readonly TargetIndex ipartner = TargetIndex.A;
        public readonly TargetIndex ibed = TargetIndex.B;

        public override bool TryMakePreToilReservations(bool errorOnFailed) {
            return pawn.Reserve(Target, job, xxx.max_rapists_per_prisoner, 0, null, errorOnFailed);
        }

        protected override IEnumerable<Toil> MakeNewToils() {
            setup_ticks();
            this.FailOnDespawnedOrNull(ipartner);
            this.FailOnDespawnedOrNull(ibed);
            this.FailOn(() => !Partner.health.capacities.CanBeAwake);

            yield return Toils_Reserve.Reserve(ipartner, xxx.max_rapists_per_prisoner, 0, null);

            Toil goToPawnInBed = Toils_Goto.GotoThing(ipartner, PathEndMode.OnCell);
            goToPawnInBed.FailOn(() => !RestUtility.InBed(Partner) && Partner.CurJobDef != DefDatabase<JobDef>.GetNamed("GettinLovedAnimation") && !xxx.in_same_bed(Partner, pawn));

            yield return goToPawnInBed;

            Toil startPartnerSex = new Toil();
            startPartnerSex.initAction = delegate {

                if(Partner.CurJobDef != DefDatabase<JobDef>.GetNamed("GettinLovedAnimation")) // allows threesomes
                {
                    Job gettinLovedJob = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("GettinLovedAnimation"), pawn, Bed); // new gettin loved toil that wakes up the pawn goes here

                    Partner.jobs.jobQueue.EnqueueFirst(gettinLovedJob);
                    Partner.jobs.EndCurrentJob(JobCondition.InterruptForced);
                }

            };
            yield return startPartnerSex;

            Toil sexToil = new Toil();
            sexToil.FailOn(() => (Partner.CurJobDef == null) || Partner.CurJobDef != DefDatabase<JobDef>.GetNamed("GettinLovedAnimation", true)); //partner jobdriver is not sexbaserecieverlovedforanim
            sexToil.socialMode = RandomSocialMode.Off;
            sexToil.defaultCompleteMode = ToilCompleteMode.Never;
            sexToil.handlingFacing = true;
            sexToil.initAction = delegate {

                usedCondom = (CondomUtility.TryUseCondom(base.pawn) || CondomUtility.TryUseCondom(Partner));
                Start();
            };

            sexToil.AddPreTickAction(delegate {

                if(!Partner.TryGetComp<CompBodyAnimator>().isAnimating)
                {
                    pawn.TryGetComp<CompBodyAnimator>().isAnimating = false;
                }

                if(Gen.IsHashIntervalTick(pawn, ticks_between_hearts)) {
                    MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
                }
                SexTick(pawn, Partner);
                SexUtility.reduce_rest(Partner);
                SexUtility.reduce_rest(pawn, 2);
                if (ticks_left <= 0)
                    ReadyForNextToil();

            });
            sexToil.AddFinishAction(delegate {

                End();

            });
            yield return sexToil;

            Toil finish = new Toil();
            finish.initAction = delegate {
                SexUtility.ProcessSex(pawn, Partner, usedCondom, isRape, isCoreLovin: false, isWhoring, sexType);
            };
            finish.defaultCompleteMode = ToilCompleteMode.Instant;
            yield return finish;

        }
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
using UnityEngine;

namespace Rimworld_Animations {
    class MainTabWindow_OffsetConfigure : MainTabWindow
    {

        public override Vector2 RequestedTabSize => new Vector2(505, 380);
        public override void DoWindowContents(Rect inRect) {

            Rect position = new Rect(inRect.x, inRect.y, inRect.width, inRect.height);

            Listing_Standard listingStandard = new Listing_Standard();
            listingStandard.Begin(position);

            listingStandard.Label("Animation Manager");

            listingStandard.GapLine();

            if (Find.Selector.SingleSelectedThing is Pawn) {

                Pawn curPawn = Find.Selector.SingleSelectedThing as Pawn;

                if (curPawn.TryGetComp<CompBodyAnimator>().isAnimating) {

                    AnimationDef def = curPawn.TryGetComp<CompBodyAnimator>().CurrentAnimation;
                    int ActorIndex = curPawn.TryGetComp<CompBodyAnimator>().ActorIndex;
                    float offsetX = 0, offsetZ = 0, rotation = 0;

                    if (AnimationSettings.offsets.ContainsKey(def.defName + curPawn.def.defName + ActorIndex)) {
                        offsetX = AnimationSettings.offsets[def.defName + curPawn.def.defName + ActorIndex].x;
                        offsetZ = AnimationSettings.offsets[def.defName + curPawn.def.defName + ActorIndex].y;
                    } else {
                        AnimationSettings.offsets.Add(def.defName + curPawn.def.defName + ActorIndex, new Vector2(0, 0));
                    }

                    if (AnimationSettings.rotation.ContainsKey(def.defName + curPawn.def.defName + ActorIndex)) {
                        rotation = AnimationSettings.rotation[def.defName + curPawn.def.defName + ActorIndex];
                    }
                    else {
                        AnimationSettings.rotation.Add(def.defName + curPawn.def.defName + ActorIndex, 0);
                    }

                    listingStandard.Label("Name: " + curPawn.Name + " Race: " + curPawn.def.defName + " Actor Index: " + curPawn.TryGetComp<CompBodyAnimator>().ActorIndex + " Animation: " + def.label + (curPawn.TryGetComp<CompBodyAnimator>().Mirror ? " mirrored" : ""));

                    if(curPawn.def.defName == "Human") {
                        listingStandard.Label("Warning--You generally don't want to change human offsets, only alien offsets");
                    }

                    listingStandard.Label("X Offset: " + offsetX);
                    offsetX = listingStandard.Slider(offsetX, -10, 10);

                    listingStandard.Label("Z Offset: " + offsetZ);
                    offsetZ = listingStandard.Slider(offsetZ, -10, 10);

                    listingStandard.Label("Rotation: " + rotation);
                    rotation = listingStandard.Slider(rotation, -180, 180);

                    if(listingStandard.ButtonText("Reset All")) {
                        offsetX = 0;
                        offsetZ = 0;
                        rotation = 0;
                    }

                    listingStandard.GapLine();

                    if(listingStandard.ButtonText("Shift Actors")) {

                        if(AnimationSettings.debugMode) {
                            Log.Message("Shifting actors in animation...");
                        }

                        for(int i = 0; i < curPawn.TryGetComp<CompBodyAnimator>().actorsInCurrentAnimation.Count; i++) {

                            Pawn actor = curPawn.TryGetComp<CompBodyAnimator>().actorsInCurrentAnimation[i];

                            actor.TryGetComp<CompBodyAnimator>()?.shiftActorPositionAndRestartAnimation();

                            //reset the clock time of every pawn in animation
                            if(actor.jobs.curDriver is rjw.JobDriver_Sex) {
                                (actor.jobs.curDriver as rjw.JobDriver_Sex).ticks_left = def.animationTimeTicks;
                                (actor.jobs.curDriver as rjw.JobDriver_Sex).ticksLeftThisToil = def.animationTimeTicks;
                                (actor.jobs.curDriver as rjw.JobDriver_Sex).duration = def.animationTimeTicks;
                            }

                        }

                    }

                    if (offsetX != AnimationSettings.offsets[def.defName + curPawn.def.defName + ActorIndex].x || offsetZ != AnimationSettings.offsets[def.defName + curPawn.def.defName + ActorIndex].y) {
                        AnimationSettings.offsets[def.defName + curPawn.def.defName + ActorIndex] = new Vector2(offsetX, offsetZ);

                    }

                    if(rotation != AnimationSettings.rotation[def.defName + curPawn.def.defName + ActorIndex]) {
                        AnimationSettings.rotation[def.defName + curPawn.def.defName + ActorIndex] = rotation;
                    }

                }

            }
            else {
                listingStandard.Label("Select a pawn currently in an animation to change their offsets");
            }

            listingStandard.End();

            base.DoWindowContents(inRect);

        }

        public override void PreOpen() {
            base.PreOpen();
            if(AnimationSettings.offsets == null) {
                if (AnimationSettings.debugMode)
                    Log.Message("New offsets");
                AnimationSettings.offsets = new Dictionary<string, Vector2>();
            }

            if(AnimationSettings.rotation == null) {
                if (AnimationSettings.debugMode)
                    Log.Message("New rotation");
                AnimationSettings.rotation = new Dictionary<string, float>();
            }
        }

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

            LoadedModManager.GetMod<RJW_Animations>().WriteSettings();
        }
    }
}


code:
using RimWorld;
using Verse;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Rimworld_Animations {

    [DefOf]
    public static class OffsetMainButtonDefOf {

        public static MainButtonDef OffsetManager;

        static OffsetMainButtonDefOf() {
            DefOfHelper.EnsureInitializedInCtor(typeof(OffsetMainButtonDefOf));
        }

    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using RimWorld.Planet;
using Verse;

namespace Rimworld_Animations {
    public class WorldComponent_UpdateMainTab : WorldComponent {

        public WorldComponent_UpdateMainTab(World world) : base(world) {

        }

        public override void FinalizeInit() {
            base.FinalizeInit();
            OffsetMainButtonDefOf.OffsetManager.buttonVisible = AnimationSettings.offsetTab;
        }

    }
}


code:
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;
using AlienRace;

namespace Rimworld_Animations {
    [StaticConstructorOnStartup]
    public static class HarmonyPatch_AlienRace {
        static HarmonyPatch_AlienRace () {
			try {
				((Action)(() => {
					if (LoadedModManager.RunningModsListForReading.Any(x => x.Name == "Humanoid Alien Races 2.0")) {

						(new Harmony("rjw")).Patch(AccessTools.Method(typeof(PawnGraphicSet), "ResolveApparelGraphics"),
							prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_AlienRace), "Prefix_StopResolveAllGraphicsWhileSex")));

						(new Harmony("rjw")).Patch(AccessTools.Method(AccessTools.TypeByName("AlienRace.HarmonyPatches"), "DrawAddons"),
							prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_AlienRace), "Prefix_AnimateHeadAddons")));
					}
				}))();
			}
			catch (TypeLoadException ex) {

			}
		}

		public static bool Prefix_StopResolveAllGraphicsWhileSex(ref Pawn ___pawn) {

			if(___pawn.TryGetComp<CompBodyAnimator>() != null && ___pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
				return false;
			}
			return true;
		}

		public static bool Prefix_AnimateHeadAddons(bool portrait, Vector3 vector, Vector3 headOffset, Pawn pawn, Quaternion quat, Rot4 rotation, bool invisible) {

			if (portrait || pawn.TryGetComp<CompBodyAnimator>() == null || !pawn.TryGetComp<CompBodyAnimator>().isAnimating) return true;
			if (!(pawn.def is ThingDef_AlienRace alienProps) || invisible) return false;

			List<AlienPartGenerator.BodyAddon> addons = alienProps.alienRace.generalSettings.alienPartGenerator.bodyAddons;
			AlienPartGenerator.AlienComp alienComp = pawn.GetComp<AlienPartGenerator.AlienComp>();
			CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();

			for (int i = 0; i < addons.Count; i++) {
				AlienPartGenerator.BodyAddon ba = addons[index: i];
				if (!ba.CanDrawAddon(pawn: pawn)) continue;

				AlienPartGenerator.RotationOffset offset;
				if (ba.drawnInBed || ba.alignWithHead) {
					offset = pawnAnimator.headFacing == Rot4.South ?
														ba.offsets.south :
														pawnAnimator.headFacing == Rot4.North ?
															ba.offsets.north :
															pawnAnimator.headFacing == Rot4.East ?
															ba.offsets.east :
															ba.offsets.west;

				} else {

					offset = rotation == Rot4.South ?
										ba.offsets.south :
										rotation == Rot4.North ?
											ba.offsets.north :
											rotation == Rot4.East ?
											ba.offsets.east :
											ba.offsets.west;

				}

				Vector2 bodyOffset = (portrait ? offset?.portraitBodyTypes ?? offset?.bodyTypes : offset?.bodyTypes)?.FirstOrDefault(predicate: to => to.bodyType == pawn.story.bodyType)
                                   ?.offset ?? Vector2.zero;
                Vector2 crownOffset = (portrait ? offset?.portraitCrownTypes ?? offset?.crownTypes : offset?.crownTypes)?.FirstOrDefault(predicate: to => to.crownType == alienComp.crownType)
                                    ?.offset ?? Vector2.zero;

				//Defaults for tails
				//south 0.42f, -0.3f, -0.22f
				//north     0f,  0.3f, -0.55f
				//east -0.42f, -0.3f, -0.22f

				float moffsetX = 0.42f;
				float moffsetZ = -0.22f;

				float layerOffset = offset?.layerOffset ?? ba.layerOffset;

				float moffsetY = ba.inFrontOfBody ? 0.3f + ba.layerOffset : -0.3f - ba.layerOffset;
				float num = ba.angle;

				if (((ba.drawnInBed || ba.alignWithHead) ? pawnAnimator.headFacing : rotation) == Rot4.North) {
					moffsetX = 0f;
					if (ba.layerInvert)
						moffsetY = -moffsetY;

					moffsetZ = -0.55f;
					num = 0;
				}

				moffsetX += bodyOffset.x + crownOffset.x;
				moffsetZ += bodyOffset.y + crownOffset.y;

				if (((ba.drawnInBed || ba.alignWithHead) ? pawnAnimator.headFacing : rotation) == Rot4.East) {
					moffsetX = -moffsetX;
					num = -num; //Angle
				}

				Vector3 offsetVector = new Vector3(x: moffsetX, y: moffsetY, z: moffsetZ);

				//Angle calculation to not pick the shortest, taken from Quaternion.Angle and modified

				//assume drawnInBed means headAddon
				Graphic addonGraphic = alienComp.addonGraphics[i];
				addonGraphic.drawSize = (portrait && ba.drawSizePortrait != Vector2.zero ? ba.drawSizePortrait : ba.drawSize) * (ba.scaleWithPawnDrawsize ? alienComp.customDrawSize : Vector2.one) * 1.5f;

				if ((ba.drawnInBed || ba.alignWithHead) && pawn.TryGetComp<CompBodyAnimator>() != null && pawn.TryGetComp<CompBodyAnimator>().isAnimating) {

					Quaternion headQuatInAnimation = Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up);
					Rot4 headRotInAnimation = pawnAnimator.headFacing;
					Vector3 headPositionInAnimation = pawnAnimator.getPawnHeadPosition() - pawn.Drawer.renderer.BaseHeadOffsetAt(pawnAnimator.headFacing).RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: headQuatInAnimation)) * 2f * 57.29578f);

					GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: headRotInAnimation), loc: headPositionInAnimation + (ba.alignWithHead ? headOffset : Vector3.zero) + offsetVector.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: headQuatInAnimation)) * 2f * 57.29578f),
						quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * headQuatInAnimation, mat: addonGraphic.MatAt(rot: pawnAnimator.headFacing), drawNow: portrait);

				}

				else {

					Quaternion addonRotation = quat;
					if (AnimationSettings.controlGenitalRotation && pawnAnimator.controlGenitalAngle && ba?.hediffGraphics != null && ba.hediffGraphics.Count != 0 && ba.hediffGraphics[0]?.path != null && (ba.hediffGraphics[0].path.Contains("Penis") || ba.hediffGraphics[0].path.Contains("penis"))) {
						addonRotation = Quaternion.AngleAxis(angle: pawnAnimator.genitalAngle, axis: Vector3.up);
					}

					GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: rotation), loc: vector + offsetVector.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: quat)) * 2f * 57.29578f),
					quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * addonRotation, mat: addonGraphic.MatAt(rot: rotation), drawNow: portrait);

				}

			}

			return false;
		}
	}
}


code:
/*
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using rjw;
using Verse;
using RimWorld;
using System.Reflection.Emit;
using System.Reflection;
using UnityEngine;

namespace Rimworld_Animations {
	[StaticConstructorOnStartup]
	public static class HarmonyPatch_CSL {
		static HarmonyPatch_CSL() {
			try {
				((Action)(() => {
					if (LoadedModManager.RunningModsListForReading.Any(x => x.Name == "Children, school and learning")) {

						(new Harmony("rjw")).Patch(AccessTools.Method(AccessTools.TypeByName("Children.PawnRenderer_RenderPawnInternal_Patch"), "RenderPawnInternalScaled"),
							prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_CSL), "Prefix_CSL")),
							transpiler: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_CSL), "Transpiler_CSL")));
					}
				}))();
			}
			catch (TypeLoadException ex) {

			}
		}

		public static void Prefix_CSL(PawnRenderer __instance, Pawn pawn, ref Vector3 rootLoc, ref float angle, bool renderBody, ref Rot4 bodyFacing, ref Rot4 headFacing, RotDrawMode bodyDrawType, bool portrait, bool headStump, bool invisible) {

			PawnGraphicSet graphics = __instance.graphics;
			CompBodyAnimator bodyAnim = pawn.TryGetComp<CompBodyAnimator>();

			if (!graphics.AllResolved) {
				graphics.ResolveAllGraphics();
			}

			if (bodyAnim != null && bodyAnim.isAnimating && !portrait) {
				bodyAnim.tickGraphics(graphics);
				pawn.TryGetComp<CompBodyAnimator>().animatePawn(ref rootLoc, ref angle, ref bodyFacing, ref headFacing);

			}
		}

		public static IEnumerable<CodeInstruction> Transpiler_CSL(IEnumerable<CodeInstruction> instructions) {

			MethodInfo drawMeshNowOrLater = AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater");
			FieldInfo headGraphic = AccessTools.Field(typeof(PawnGraphicSet), "headGraphic");

			List<CodeInstruction> codes = instructions.ToList();
			bool forHead = true;
			for (int i = 0; i < codes.Count(); i++) {

				//Instead of calling drawmeshnoworlater, add pawn to the stack and call my special static method
				if (codes[i].OperandIs(drawMeshNowOrLater) && forHead) {

					yield return new CodeInstruction(OpCodes.Ldarg_0);
					yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(PawnRenderer), "pawn"));
					yield return new CodeInstruction(OpCodes.Ldarg_2);
					yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(AnimationUtility), nameof(AnimationUtility.RenderPawnHeadMeshInAnimation), new Type[] { typeof(Mesh), typeof(Vector3), typeof(Quaternion), typeof(Material), typeof(bool), typeof(Pawn), typeof(float) }));

				}
				//checking for if(graphics.headGraphic != null)
				else if (codes[i].opcode == OpCodes.Ldfld && codes[i].OperandIs(headGraphic)) {
					forHead = true;
					yield return codes[i];
				}
				//checking for if(renderbody)
				else if (codes[i].opcode == OpCodes.Ldarg_3) {
					forHead = false;
					yield return codes[i];
				}
				else {
					yield return codes[i];
				}
			}
		}

	}
}*/


code:
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace Rimworld_Animations {
    class HarmonyPatch_DontShaveYourHead {

		[StaticConstructorOnStartup]
		public static class Patch_DontShaveYourHead {

			static Patch_DontShaveYourHead() {
				try {
					((Action)(() =>
					{
						if (LoadedModManager.RunningModsListForReading.Any(x => x.Name == "Don't Shave Your Head 1.0")) {
							(new Harmony("rjw")).Patch(AccessTools.Method(AccessTools.TypeByName("DontShaveYourHead.Harmony_PawnRenderer"), "DrawHairReroute"), //typeof(ShowHair.Patch_PawnRenderer_RenderPawnInternal), nameof(ShowHair.Patch_PawnRenderer_RenderPawnInternal.Postfix)),
								transpiler: new HarmonyMethod(AccessTools.Method(typeof(Patch_ShowHairWithHats), "Transpiler")));
						}
					}))();
				}
				catch (TypeLoadException ex) { }
			}
		}
	}
}


code:
using HarmonyLib;
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;

namespace Rimworld_Animations {
	[StaticConstructorOnStartup]
	public static class Patch_FacialAnimation {

		static Patch_FacialAnimation() {
			try {
				((Action)(() => {
					if (LoadedModManager.RunningModsListForReading.Any(x => x.Name == "[NL] Facial Animation - WIP")) {
						(new Harmony("rjw")).Patch(AccessTools.Method(AccessTools.TypeByName("FacialAnimation.DrawFaceGraphicsComp"), "DrawGraphics"),
							prefix: new HarmonyMethod(AccessTools.Method(typeof(Patch_FacialAnimation), "Prefix")));
					}
				}))();
			}
			catch (TypeLoadException ex) {

			}
		}

		public static bool Prefix(ref Pawn ___pawn, ref Rot4 headFacing, ref Vector3 headOrigin, ref Quaternion quaternion, ref bool portrait) {

			CompBodyAnimator bodyAnim = ___pawn.TryGetComp<CompBodyAnimator>();

			if (bodyAnim != null && bodyAnim.isAnimating && !portrait) {

				headFacing = bodyAnim.headFacing;
				headOrigin = new Vector3(bodyAnim.getPawnHeadPosition().x, headOrigin.y, bodyAnim.getPawnHeadPosition().z);
				quaternion = Quaternion.AngleAxis(bodyAnim.headAngle, Vector3.up);
			}

			return true;
		}
		/*
		public static List<string> rjwLovinDefNames = new List<string>{
			"Lovin",
			"Quickie",
			"GettingQuickie",
			"JoinInBed",
			"JoinInBedAnimation",
			"GettinLovedAnimation",
			"GettinLoved",
			"GettinLicked",
			"GettinSucked",
			"GettinRaped",
			"ViolateCorpse",
			"RJW_Masturbate",
			"GettinBred",
			"Breed",
			"RJW_Mate",
			"Bestiality",
			"BestialityForFemale",
			"StruggleInBondageGear",
			"WhoreIsServingVisitors",
			"UseFM"
		};

		public static List<string> rjwRapeDefNames = new List<string> {
			"RapeComfortPawn",
			"RandomRape",
			"RapeEnemy"
		};

		public static bool Prefix_IsSameA(JobDef job, string ___jobDef, ref bool __result) {

			if(___jobDef != null && ___jobDef == "Lovin" && job?.defName != null && rjwLovinDefNames.Contains(job?.defName)) {
				__result = true;
				return false;
			}
			else if (___jobDef != null && ___jobDef == "Wait_Combat" && job?.defName != null && rjwRapeDefNames.Contains(job?.defName)) {
				__result = true;
				return false;
			}

			return true;
		}

		public static bool Prefix_IsSameB(string jobName, string ___jobDef, ref bool __result) {

			if (___jobDef != null && ___jobDef == "Lovin" && jobName != null && rjwLovinDefNames.Contains(jobName)) {
				__result = true;
				return false;
			}
			if (___jobDef != null && ___jobDef == "Wait_Combat" && jobName != null && rjwRapeDefNames.Contains(jobName)) {
				__result = true;
				return false;
			}

			return true;
		}
		*/
	}
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RimWorld;
using Verse;
using UnityEngine;
using System.Reflection;
using System.Reflection.Emit;

namespace Rimworld_Animations {
    public static class HarmonyPatch_HatsDisplaySelection {

        public static void PatchHatsDisplaySelectionArgs() {
            (new Harmony("rjw")).Patch(AccessTools.Method(AccessTools.TypeByName("HatDisplaySelection.Patch"), "DrawHatCEWithHair"),
                transpiler: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_HatsDisplaySelection), "ReplaceDrawMeshOrLaterWithAnimate")));

            (new Harmony("rjw")).Patch(AccessTools.Method(AccessTools.TypeByName("HatDisplaySelection.Patch"), "DrawHatWithHair"),
                transpiler: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_HatsDisplaySelection), "ReplaceDrawMeshOrLaterWithAnimate")));

            (new Harmony("rjw")).Patch(AccessTools.Method(AccessTools.TypeByName("HatDisplaySelection.Patch"), "DrawHeadApparelWithHair"),
                prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_HatsDisplaySelection), "PrefixPatchForDrawHeadApparelWithHair")));

        }

        public static void PrefixPatchForDrawHeadApparelWithHair(PawnRenderer renderer, ref Vector3 rootLoc, ref float angle, bool renderBody, ref Rot4 bodyFacing, ref Rot4 headFacing, RotDrawMode bodyDrawType, bool portrait, bool headStump, bool invisible)
        {
            PawnGraphicSet graphics = renderer.graphics;
            Pawn pawn = graphics.pawn;
            CompBodyAnimator bodyAnim = pawn.TryGetComp<CompBodyAnimator>();

            if (!graphics.AllResolved)
            {
                graphics.ResolveAllGraphics();
            }

            if (bodyAnim != null && bodyAnim.isAnimating && !portrait && pawn.Map == Find.CurrentMap)
            {
                bodyAnim.tickGraphics(graphics);
                bodyAnim.animatePawn(ref rootLoc, ref angle, ref bodyFacing, ref headFacing);

            }
        }

        public static IEnumerable<CodeInstruction> ReplaceDrawMeshOrLaterWithAnimate(IEnumerable<CodeInstruction> instructions) {

            MethodInfo drawMeshNowOrLater = AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater");
            List<CodeInstruction> codes = instructions.ToList();
            for (int i = 0; i < instructions.Count(); i++) {

                if (codes[i].OperandIs(drawMeshNowOrLater)) {

                    yield return new CodeInstruction(OpCodes.Ldarg_0);
                    yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(AccessTools.TypeByName("HatDisplaySelection.Patch"), "pawn"));
                    yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(AnimationUtility), nameof(AnimationUtility.RenderPawnHeadMeshInAnimation), new Type[] { typeof(Mesh), typeof(Vector3), typeof(Quaternion), typeof(Material), typeof(bool), typeof(Pawn) }));

                }
                else {
                    yield return codes[i];
                }

            }

        }

    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RimWorld;
using Verse;
using UnityEngine;
using System.Reflection;
using System.Reflection.Emit;

namespace Rimworld_Animations {

	[HarmonyPatch(typeof(PawnRenderer), "RenderPawnInternal", new Type[]
		{
			typeof(Vector3),
			typeof(float),
			typeof(bool),
			typeof(Rot4),
			typeof(Rot4),
			typeof(RotDrawMode),
			typeof(bool),
			typeof(bool),
			typeof(bool)
		}
	)]
	public static class HarmonyPatch_PawnRenderer {

		[HarmonyBefore(new string[] { "showhair.kv.rw", "erdelf.HumanoidAlienRaces", "Nals.FacialAnimation" })]
		public static void Prefix(PawnRenderer __instance, ref Vector3 rootLoc, ref float angle, bool renderBody, ref Rot4 bodyFacing, ref Rot4 headFacing, RotDrawMode bodyDrawType, bool portrait, bool headStump, bool invisible) {
			PawnGraphicSet graphics = __instance.graphics;
			Pawn pawn = graphics.pawn;
			CompBodyAnimator bodyAnim = pawn.TryGetComp<CompBodyAnimator>();

			if (!graphics.AllResolved) {
				graphics.ResolveAllGraphics();
			}

			if (bodyAnim != null && bodyAnim.isAnimating && !portrait && pawn.Map == Find.CurrentMap) {
				bodyAnim.tickGraphics(graphics);
				bodyAnim.animatePawn(ref rootLoc, ref angle, ref bodyFacing, ref headFacing);

			}
		}
	}

	[StaticConstructorOnStartup]
	public static class HarmonyPatch_Animate
    {

		static HarmonyPatch_Animate() {
			// hats display selection patch -- broken
			if (LoadedModManager.RunningModsListForReading.Any(x => x.Name == "Hats Display Selection")) {
				HarmonyPatch_HatsDisplaySelection.PatchHatsDisplaySelectionArgs();
			}
			else {
				PatchRimworldFunctionsNormally();
			}

			//PatchRimworldFunctionsNormally();

		}

		static void PatchRimworldFunctionsNormally() {
			(new Harmony("rjw")).Patch(AccessTools.Method(typeof(PawnRenderer), "RenderPawnInternal", parameters: new Type[]
				{
					typeof(Vector3),
					typeof(float),
					typeof(bool),
					typeof(Rot4),
					typeof(Rot4),
					typeof(RotDrawMode),
					typeof(bool),
					typeof(bool),
					typeof(bool)
				}),
				transpiler: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_Animate), "Transpiler")));
		}

		[HarmonyAfter(new string[] { "showhair.kv.rw", "erdelf.HumanoidAlienRaces", "Nals.FacialAnimation" })]
		[HarmonyReversePatch(HarmonyReversePatchType.Snapshot)]
		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {

			MethodInfo drawMeshNowOrLater = AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater");
			FieldInfo headGraphic = AccessTools.Field(typeof(PawnGraphicSet), "headGraphic");

			List<CodeInstruction> codes = instructions.ToList();
			bool forHead = true;
			for(int i = 0; i < codes.Count(); i++) {

				//Instead of calling drawmeshnoworlater, add pawn to the stack and call my special static method
				if (codes[i].OperandIs(drawMeshNowOrLater) && forHead) {

					yield return new CodeInstruction(OpCodes.Ldarg_0);
					yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(PawnRenderer), "pawn"));
					yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(AnimationUtility), nameof(AnimationUtility.RenderPawnHeadMeshInAnimation), new Type[] { typeof(Mesh), typeof(Vector3), typeof(Quaternion), typeof(Material), typeof(bool), typeof(Pawn) }));

				}
				//checking for if(graphics.headGraphic != null)
				else if (codes[i].opcode == OpCodes.Ldfld && codes[i].OperandIs(headGraphic)) {
					forHead = true;
					yield return codes[i];
				}
				//checking for if(renderbody)
				else if(codes[i].opcode == OpCodes.Ldarg_3) {
					forHead = false;
					yield return codes[i];
				}
				else {
					yield return codes[i];
				}
			}
		}
    }
}


code:
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace Rimworld_Animations {
    [HarmonyPatch(typeof(Thing), "Rotation", MethodType.Getter)]
    public static class HarmonyPatch_PawnRotation {

        public static bool Prefix(Thing __instance, ref Rot4 __result) {

            if(!(__instance is Pawn) || (__instance as Pawn)?.TryGetComp<CompBodyAnimator>() == null || !(__instance as Pawn).TryGetComp<CompBodyAnimator>().isAnimating) {
                return true;
            }

            Pawn pawn = (__instance as Pawn);
            __result = pawn.TryGetComp<CompBodyAnimator>().bodyFacing;

            return false;

        }

    }
}


code:
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;

namespace Rimworld_Animations {

    [HarmonyPatch(typeof(Pawn_DrawTracker), "DrawPos", MethodType.Getter)]
    public static class HarmonyPatch_Pawn_DrawTracker {
        public static bool Prefix(ref Pawn ___pawn, ref Vector3 __result) {

            CompBodyAnimator bodyAnim = ___pawn.TryGetComp<CompBodyAnimator>();

            if (bodyAnim != null && bodyAnim.isAnimating) {
                __result = ___pawn.TryGetComp<CompBodyAnimator>().anchor + ___pawn.TryGetComp<CompBodyAnimator>().deltaPos;

                return false;
            }
            return true;
        }
    }
}


code:
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;

namespace Rimworld_Animations {
    [StaticConstructorOnStartup]
    public static class Patch_ShowHairWithHats {

		static Patch_ShowHairWithHats() {
			try {
				((Action)(() =>
				{
					if (LoadedModManager.RunningModsListForReading.Any(x => x.Name == "[KV] Show Hair With Hats or Hide All Hats - 1.1")) {
						(new Harmony("rjw")).Patch(AccessTools.Method(AccessTools.TypeByName("ShowHair.Patch_PawnRenderer_RenderPawnInternal"), "Postfix"), //typeof(ShowHair.Patch_PawnRenderer_RenderPawnInternal), nameof(ShowHair.Patch_PawnRenderer_RenderPawnInternal.Postfix)),
							transpiler: new HarmonyMethod(AccessTools.Method(typeof(Patch_ShowHairWithHats), "Transpiler")));
					}
				}))();
			}
			catch (TypeLoadException ex) { }
		}

		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {

			MethodInfo drawMeshNowOrLater = AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater");

			List<CodeInstruction> codes = instructions.ToList();
			for (int i = 0; i < codes.Count(); i++) {

				//Instead of calling drawmeshnoworlater, add pawn to the stack and call my special static method
				if (codes[i].OperandIs(drawMeshNowOrLater)) {

					yield return new CodeInstruction(OpCodes.Ldarg_0);
					yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(PawnRenderer), "pawn"));
					yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(AnimationUtility), nameof(AnimationUtility.RenderPawnHeadMeshInAnimation), new Type[] { typeof(Mesh), typeof(Vector3), typeof(Quaternion), typeof(Material), typeof(bool), typeof(Pawn) }));

				}
				else {
					yield return codes[i];
				}
			}
		}
	}
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using HarmonyLib;
using System.Reflection;

namespace Rimworld_Animations {

    [StaticConstructorOnStartup]
    public static class Harmony_PatchAll {

        static Harmony_PatchAll() {

            Harmony val = new Harmony("rimworldanim");
            val.PatchAll(Assembly.GetExecutingAssembly());

        }
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RimWorld;
using Verse;
using rjw;
using Verse.AI;

namespace Rimworld_Animations {

    [HarmonyPatch(typeof(JobGiver_DoLovin), "TryGiveJob")]
    public static class HarmonyPatch_DoLovinAnimationPatch {

        public static void Postfix(ref Pawn pawn, ref Job __result) {

            if(__result != null) {
                Pawn partnerInMyBed = LovePartnerRelationUtility.GetPartnerInMyBed(pawn);
                RestUtility.WakeUp(pawn);
                __result = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("JoinInBedAnimation", true), partnerInMyBed, partnerInMyBed.CurrentBed());
            }
        }
    }
}


code:
/*
 * Todo: Ask to make SemenSplatch and DrawSemen public
 *
 *
 * using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using rjw;
using HarmonyLib;

namespace Rimworld_Animations {
    [HarmonyPatch("DrawSemen")]
    public static class HarmonyPatch_DrawSemen {

        public static void Prefix(ref Dictionary<string, SemenSplatch>) {

        }

    }
}*/


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RimWorld;
using Verse;
using rjw;

namespace Rimworld_Animations {

    [HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "Start")]
    static class HarmonyPatch_JobDriver_SexBaseInitiator_Start {
        public static void Postfix(ref JobDriver_SexBaseInitiator __instance) {
			/*
			 These particular jobs need special code
			 don't play anim for now
			 */
			if(__instance is JobDriver_Masturbate || __instance is JobDriver_ViolateCorpse) {
				return;
			}

			Pawn pawn = __instance.pawn;

			Building_Bed bed = __instance.Bed;

			/*
			if (__instance is JobDriver_BestialityForFemale)
				bed = (__instance as JobDriver_BestialityForFemale).Bed;
			else if (__instance is JobDriver_WhoreIsServingVisitors) {
				bed = (__instance as JobDriver_WhoreIsServingVisitors).Bed;
			}
			else if (__instance is JobDriver_SexCasualForAnimation) {
				bed = (__instance as JobDriver_SexCasualForAnimation).Bed;
			}
			else if (__instance is JobDriver_Masturbate)
				bed = (__instance as JobDriver_Masturbate).Bed;
			else if (__instance is JobDriver_Rape)
				bed = (__instance?.Partner?.jobs?.curDriver as JobDriver_Sex)?.Bed;

			*/

			if ((__instance.Target as Pawn)?.jobs?.curDriver is JobDriver_SexBaseReciever) {

				Pawn Target = __instance.Target as Pawn;

				if (!(Target.jobs.curDriver as JobDriver_SexBaseReciever).parteners.Contains(pawn)) {
					(Target.jobs.curDriver as JobDriver_SexBaseReciever).parteners.Add(pawn);
				}

				bool quickie = (__instance is JobDriver_SexQuick) && AnimationSettings.fastAnimForQuickie;

				int preAnimDuration = __instance.duration;
				int AnimationTimeTicks = 0;

				if (bed != null) {
                    RerollAnimations(Target, out AnimationTimeTicks, bed as Thing, __instance.sexType, quickie, sexProps: __instance.Sexprops);
                }
				else {
					RerollAnimations(Target, out AnimationTimeTicks, sexType: __instance.sexType, fastAnimForQuickie: quickie, sexProps: __instance.Sexprops);
				}

				//Modify Orgasm ticks to only orgasm as many times as RJW stock orgasm allows
				if(AnimationTimeTicks != 0)
                {
					__instance.orgasmstick = preAnimDuration * __instance.orgasmstick / AnimationTimeTicks;
				}

			}
		}

		public static void RerollAnimations(Pawn pawn, out int AnimationTimeTicks, Thing bed = null, xxx.rjwSextype sexType = xxx.rjwSextype.None, bool fastAnimForQuickie = false, rjw.SexProps sexProps = null) {

			AnimationTimeTicks = 0;

			if(pawn == null || !(pawn.jobs?.curDriver is JobDriver_SexBaseReciever)) {
				Log.Error("Error: Tried to reroll animations when pawn isn't sexing");
				return;
			}

			List<Pawn> pawnsToAnimate = (pawn.jobs.curDriver as JobDriver_SexBaseReciever).parteners.ToList();

			if (!pawnsToAnimate.Contains(pawn)) {
				pawnsToAnimate = pawnsToAnimate.Append(pawn).ToList();
			}

			for(int i = 0; i < pawnsToAnimate.Count; i++) {

				if(pawnsToAnimate[i].TryGetComp<CompBodyAnimator>() == null) {
					Log.Error("Error: " + pawnsToAnimate[i].Name + " of race " + pawnsToAnimate[i].def.defName + " does not have CompBodyAnimator attached!");
					break;
				}
			}

			AnimationDef anim = AnimationUtility.tryFindAnimation(ref pawnsToAnimate, sexType, sexProps);

			if (anim != null) {

				bool mirror = GenTicks.TicksGame % 2 == 0;

				IntVec3 pos = pawn.Position;

				for (int i = 0; i < anim.actors.Count; i++)
				{
					pawnsToAnimate[i].TryGetComp<CompBodyAnimator>().isAnimating = false;
				}

				for (int i = 0; i < pawnsToAnimate.Count; i++) {

					if (bed != null)
						pawnsToAnimate[i].TryGetComp<CompBodyAnimator>().setAnchor(bed);
					else {

						pawnsToAnimate[i].TryGetComp<CompBodyAnimator>().setAnchor(pos);
					}

					bool shiver = pawnsToAnimate[i].jobs.curDriver is JobDriver_SexBaseRecieverRaped;
					pawnsToAnimate[i].TryGetComp<CompBodyAnimator>().StartAnimation(anim, pawnsToAnimate, i, mirror, shiver, fastAnimForQuickie);

					(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).ticks_left = anim.animationTimeTicks;
					(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).sex_ticks = anim.animationTimeTicks;
					(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).duration = anim.animationTimeTicks;

					AnimationTimeTicks = anim.animationTimeTicks;

					if(!AnimationSettings.hearts) {
						(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).ticks_between_hearts = Int32.MaxValue;
					}

				}
			}
			else {
				Log.Message("No animation found");

				/*

				//if pawn isn't already animating,
				if (!pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
					(pawn.jobs.curDriver as JobDriver_SexBaseReciever).increase_time(duration);
					//they'll just do the thrusting anim
				}

				*/
			}
		}
	}

	[HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "End")]
	static class HarmonyPatch_JobDriver_SexBaseInitiator_End {

		public static void Postfix(ref JobDriver_SexBaseInitiator __instance) {

			if ((__instance.Target as Pawn)?.jobs?.curDriver is JobDriver_SexBaseReciever) {
				if (__instance.pawn.TryGetComp<CompBodyAnimator>().isAnimating) {

					List<Pawn> parteners = ((__instance.Target as Pawn)?.jobs.curDriver as JobDriver_SexBaseReciever).parteners;

					for (int i = 0; i < parteners.Count; i++) {

						//prevents pawns who started a new anim from stopping their new anim
						if (!((parteners[i].jobs.curDriver as JobDriver_SexBaseInitiator) != null && (parteners[i].jobs.curDriver as JobDriver_SexBaseInitiator).Target != __instance.pawn))
							parteners[i].TryGetComp<CompBodyAnimator>().isAnimating = false;

					}

					__instance.Target.TryGetComp<CompBodyAnimator>().isAnimating = false;

					if (xxx.is_human((__instance.Target as Pawn))) {
						(__instance.Target as Pawn)?.Drawer.renderer.graphics.ResolveApparelGraphics();
						PortraitsCache.SetDirty((__instance.Target as Pawn));
					}
				}

				((__instance.Target as Pawn)?.jobs.curDriver as JobDriver_SexBaseReciever).parteners.Remove(__instance.pawn);

			}

			if (xxx.is_human(__instance.pawn)) {
				__instance.pawn.Drawer.renderer.graphics.ResolveApparelGraphics();
				PortraitsCache.SetDirty(__instance.pawn);
			}
		}
	}
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using rjw;
using Verse;
using Verse.AI;
using RimWorld;
using HarmonyLib;

namespace Rimworld_Animations {
    [HarmonyPatch(typeof(JobGiver_JoinInBed), "TryGiveJob")]
    public static class HarmonyPatch_JoinInBedGiveJob {

        public static bool Prefix(ref Job __result, ref Pawn pawn) {

			__result = null;

			if (!RJWHookupSettings.HookupsEnabled)
				return false;

			if (pawn.Drafted)
				return false;

			if (!SexUtility.ReadyForHookup(pawn))
				return false;

			// We increase the time right away to prevent the fairly expensive check from happening too frequently
			SexUtility.IncreaseTicksToNextHookup(pawn);

			// If the pawn is a whore, or recently had sex, skip the job unless they're really horny
			if (!xxx.is_frustrated(pawn) && (xxx.is_whore(pawn) || !SexUtility.ReadyForLovin(pawn)))
				return false;

			// This check attempts to keep groups leaving the map, like guests or traders, from turning around to hook up
			if (pawn.mindState?.duty?.def == DutyDefOf.TravelOrLeave) {
				// TODO: Some guest pawns keep the TravelOrLeave duty the whole time, I think the ones assigned to guard the pack animals.
				// That's probably ok, though it wasn't the intention.
				if (RJWSettings.DebugLogJoinInBed) ModLog.Message($"JoinInBed.TryGiveJob:({xxx.get_pawnname(pawn)}): has TravelOrLeave, no time for lovin [ANIM JOBGIVER]!");
				return false;
			}

			if ((pawn.CurJob == null || pawn.CurJob.def == JobDefOf.LayDown) && CasualSex_Helper.CanHaveSex(pawn)) {
				//--Log.Message("   finding partner");
				Pawn partner = CasualSex_Helper.find_partner(pawn, pawn.Map, bedsex: true);

				//--Log.Message("   checking partner");
				if (partner == null)
					return false;

				// Can never be null, since find checks for bed.
				Building_Bed bed = partner.CurrentBed();

				// Interrupt current job.
				if (pawn.CurJob != null && pawn.jobs.curDriver != null)
					pawn.jobs.curDriver.EndJobWith(JobCondition.InterruptForced);

				__result = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("JoinInBedAnimation", true), partner, bed);
				return false;
			}

			return false;

		}

	}
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse.AI;
using rjw;
using HarmonyLib;
using Verse;

namespace Rimworld_Animations {

    [HarmonyPatch(typeof(Pawn_JobTracker), "TryTakeOrderedJob")]
    class HarmonyPatch_PlayAnimJoinInBedRMB {
        public static void Prefix(ref Job job) {
            if(job.def == xxx.casual_sex) {
                if (AnimationSettings.debugMode || RJWSettings.DevMode)
                    Log.Message("Replacing vanilla RJW JoinInBed JobDriver for animation JobDriver");
                job = new Job(DefDatabase<JobDef>.GetNamed("JoinInBedAnimation", true), job.targetA, job.targetB, job.targetC);
            }

        }

    }
}


code:
using HarmonyLib;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace Rimworld_Animations
{
    [HarmonyPatch(typeof(JobDriver_Sex), "PlaySexSound")]
    class HarmonyPatch_PlaySexSounds
    {
        public static bool Prefix(JobDriver_Sex __instance)
        {
            if (__instance.pawn.TryGetComp<CompBodyAnimator>().isAnimating)
            {
                return false;
            }

            return true;
        }
    }
}