code:
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
  <Operation Class="PatchOperationSequence">
    <success>Always</success>
    <operations>

        <xpath>/Defs/ThingDef[@Name="BaseZombie"]/comps</xpath>
        <success>Always</success>
	    <match Class="PatchOperationAdd">
		  <xpath>/Defs/ThingDef[@Name="BaseZombie"]/comps</xpath>
		  <value>

		  </value>
	    </match>

    </operations>
  </Operation>
</Patch>


code:
<?xml version="1.0" encoding="utf-8"?>
<Patch>
  <Operation Class="PatchOperationFindMod">
    <mods>

[NL] Facial Animation - WIP

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

          <xpath>/Defs/FacialAnimation.FaceAnimationDef[defName="Lovin" or defName="Lovin2"]/targetJobs</xpath>
          <success>Always</success>
          <value>

RJW_Masturbate

GettinBred

Bestiality

BestialityForFemale

ViolateCorpse

Quickie

GettingQuickie

GettinRaped

JoinInBed

GettinLoved

GettinLicked

GettinSucked

WhoreIsServingVisitors

JoinInBedAnimation

GettinLovedAnimation

          </value>

          <xpath>/Defs/FacialAnimation.FaceAnimationDef[defName="WaitCombat" or defName="Wait_Combat_Rare"]/targetJobs</xpath>
          <success>Always</success>
          <value>

RapeComfortPawn

RandomRape

RapeEnemy

          </value>

          <xpath>/Defs/FacialAnimation.FaceAnimationDef[defName="StandAndBeSociallyActive"]/targetJobs</xpath>
          <success>Always</success>
          <value>

WhoreInvitingVisitors

          </value>

          <xpath>/Defs/FacialAnimation.FaceAnimationDef[defName="Wear" or defName="Wear2" or defName="Wear3"]/targetJobs</xpath>
          <success>Always</success>
          <value>

CleanSelf

StruggleInBondageGear

          </value>

          <mods>

Rimworld-Animations

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

                <xpath>/Defs/FacialAnimation.FaceAnimationDef[defName="Lovin" or defName="Lovin2"]/animationFrames/li[1]/headOffset</xpath>
                <success>Always</success>

                <xpath>/Defs/FacialAnimation.FaceAnimationDef[defName="Lovin"]/animationFrames/li[2]/headOffset</xpath>
                <success>Always</success>

                <xpath>/Defs/FacialAnimation.FaceAnimationDef[defName="Lovin"]/animationFrames/li[3]/headOffset</xpath>
                <success>Always</success>

            </operations>
          </match>

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

license: unknown


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/ThingDef/comps</xpath>
                    <success>Always</success>
                    <nomatch Class="PatchOperationAdd">
                        <xpath>/Defs/ThingDef</xpath>
                        <value>
                            <comps />
                        </value>
                    </nomatch>

                    <xpath>/Defs/ThingDef[@Name="SK_BasePawn"]/comps</xpath>
                    <value>

                    </value>

                    <xpath>/Defs/ThingDef[@Name="BaseAnimalPawn"]/comps</xpath>
                    <value>

                    </value>

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

</Patch>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
	<Operation Class="PatchOperationFindMod">
		<mods>

Epona race Renaissance

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

					<xpath>Defs/AlienRace.ThingDef_AlienRace[defName = "Alien_Epona"]/alienRace/generalSettings/alienPartGenerator/bodyAddons/li[hediffGraphics/Epona_OHPG_female="Things/Pawn/Addons/Breasts/Breasts"]/drawnInBed</xpath>
					<match Class="PatchOperationReplace">
						<xpath>Defs/AlienRace.ThingDef_AlienRace[defName = "Alien_Epona"]/alienRace/generalSettings/alienPartGenerator/bodyAddons/li[hediffGraphics/Epona_OHPG_female="Things/Pawn/Addons/Breasts/Breasts"]/drawnInBed</xpath>
						<value>
							<drawnInBed>false</drawnInBed>
						</value>
					</match>

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


code:
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
	<Operation Class="PatchOperationFindMod">
		<mods>

Nyaron race

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

					<xpath>Defs/AlienRace.ThingDef_AlienRace[defName = "Alien_Nyaron"]/alienRace/generalSettings/alienPartGenerator/bodyAddons/li[bodyPart="tail"]</xpath>
					<match Class="PatchOperationAdd">
						<xpath>/Defs/AlienRace.ThingDef_AlienRace[defName = "Alien_Nyaron"]/alienRace/generalSettings/alienPartGenerator/bodyAddons/li[bodyPart="tail"]</xpath>
						<value>
							<drawnInBed>false</drawnInBed>
						</value>
					</match>

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


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);
		public List<string> requiredGender;
        public List<string> tags = new List<string>();
    }
}


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;

namespace Rimworld_Animations {
    public class AnimationStage
    {
        public string stageName;
        public int stageIndex;
        public int playTimeTicks = 0;
        public int playTimeTicksQuick = -1;
        public bool isLooping;
        public List<BaseAnimationClip> animationClips;
        public List<string> tags = new List<string>();

        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 Dictionary<int, string> SoundEffects = new Dictionary<int, string>();
        public List<ThingDef> types; //types of participants
        public int duration;
        public abstract void buildSimpleCurves();
        public string soundDef = null; //for playing sounds
        public int actor;
        public List<string> tags = new List<string>();
    }
}


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, 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;
using Verse;
using RimWorld;

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

        public SimpleCurve PositionX = new SimpleCurve();
        public SimpleCurve PositionZ = new SimpleCurve();
        public SimpleCurve Rotation = new SimpleCurve();

        public override void buildSimpleCurves() {
            int duration = 0;
            //getting the length of the whole clip
            foreach (ThingKeyframe frame in keyframes)
            {
                duration += frame.tickDuration;
            }

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

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

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

                    if (frame.positionZ.HasValue)
                        PositionZ.Add((float)frame.atTick / (float)duration, frame.positionZ.Value, true);

                    if (frame.rotation.HasValue)
                        Rotation.Add((float)frame.atTick / (float)duration, frame.rotation.Value, true);

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

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

                    if (frame.positionZ.HasValue)
                        PositionZ.Add((float)keyframePosition / (float)duration, frame.positionZ.Value, true);

                    if (frame.rotation.HasValue)
                        Rotation.Add((float)keyframePosition / (float)duration, frame.rotation.Value, true);

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

                }

            }
        }
    }
}


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;
        public float? atTick;
        public string soundEffect;
        public List<string> tags = new List<string>();
    }
}


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 bool? quiver;

    }
}


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

namespace Rimworld_Animations {
    public class ThingKeyframe : Keyframe
    {

        public float? positionX;
        public float? positionZ;
        public float? rotation;

    }
}


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;
        public bool fastAnimForQuickie = false;

        private AnimationDef anim;
        private AnimationStage stage {
            get
            {
                return 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;

            if (anim.actors.Count <= actor)
            {
                return;
            }
            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;
            this.fastAnimForQuickie = fastAnimForQuickie;

            if (fastAnimForQuickie && anim.animationStages.Any(x => x.playTimeTicksQuick >= 0) == false)
            {
                curStage = 1;
                animTicks = anim.animationStages[0].playTimeTicks;
            } else
            {
                curStage = 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) {

                GlobalTextureAtlasManager.TryMarkPawnFrameSetDirty(pawn);

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

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

        }

        public Rot4 AnimateHeadFacing()
        {
            return 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()
        {

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

            stageTicks++;

            if(stageTicks >= stage.playTimeTicks || (fastAnimForQuickie && stage.playTimeTicksQuick >= 0 && stageTicks >= stage.playTimeTicksQuick)) {

                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))
            {

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

        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));

            string bodyTypeDef = (pawn.story?.bodyType != null) ? pawn.story.bodyType.ToString() : "";

            if (AnimationSettings.offsets != null && AnimationSettings.offsets.ContainsKey(CurrentAnimation.defName + pawn.def.defName + bodyTypeDef + ActorIndex)) {
                deltaPos.x += AnimationSettings.offsets[CurrentAnimation.defName + pawn.def.defName + bodyTypeDef + ActorIndex].x * (mirror ? -1 : 1);
                deltaPos.z += AnimationSettings.offsets[CurrentAnimation.defName + pawn.def.defName + bodyTypeDef + 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 + bodyTypeDef + ActorIndex)) {
                float offsetRotation = AnimationSettings.rotation[CurrentAnimation.defName + pawn.def.defName + bodyTypeDef + 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));
            Vector2 bodyScale = (pawn.story?.bodyType?.bodyGraphicScale != null ? pawn.story.bodyType.bodyGraphicScale : Vector2.one);
            headBob.z *= bodyScale.x;

            lastDrawFrame = RealTime.frameCount;

        }

        public Vector3 getPawnHeadPosition() {

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

            return headPos;

        }

        public Vector3 getPawnHeadOffset()
        {
            return Quaternion.AngleAxis(bodyAngle, Vector3.up) * (pawn.Drawer.renderer.BaseHeadOffsetAt(headFacing) + headBob);

        }

        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 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 {
    public 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;
using Verse;

namespace Rimworld_Animations {
    public class CompProperties_ThingAnimator : CompProperties
    {

        public CompProperties_ThingAnimator()
        {
            base.compClass = typeof(CompThingAnimator);
        }
    }
}


code:
using RimWorld;
using rjw;
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 CompThingAnimator : ThingComp
    {
        Vector3 anchor;

        Pawn pawn;

        public bool isAnimating = false, mirror;

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

        public Vector3 deltaPos;

        AnimationDef anim;
        private ThingAnimationClip clip => (ThingAnimationClip)stage.animationClips[1];
        private AnimationStage stage
        {
            get
            {
                return anim.animationStages[curStage];
            }

        }

        public void StartAnimation(AnimationDef anim, Pawn pawn, bool mirror = false)
        {
            isAnimating = true;

            this.anim = anim;
            this.pawn = pawn;
            this.mirror = mirror;

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

            curStage = 0;
            clipPercent = 0;

            tickAnim();

        }

        public void setAnchor(IntVec3 position)
        {
            anchor = position.ToVector3();
        }

        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 tickAnim()
        {
            if (!isAnimating) return;
            animTicks++;

            if (animTicks < anim.animationTimeTicks)
            {
                tickStage();
            }
            else
            {
                if (LoopNeverending())
                {
                    ResetOnLoop();
                }
                else
                {
                    isAnimating = false;
                }

            }

        }

        public void tickStage()
        {
            if (stage == null)
            {
                isAnimating = false;
                return;
            }

            stageTicks++;

            if (stageTicks >= stage.playTimeTicks)
            {

                curStage++;

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

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

            }
            else
            {
                tickClip();
            }
        }

        public void tickClip()
        {
            clipTicks++;

            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 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();
            }

            anchor -= new Vector3(0.5f, 0, 0.5f);
        }
        private void calculateDrawValues()
        {

            //shift up and right 0.5f to align center
            deltaPos = new Vector3((clip.PositionX.Evaluate(clipPercent)) * (mirror ? -1 : 1) + 0.5f, AltitudeLayer.Item.AltitudeFor(), clip.PositionZ.Evaluate(clipPercent) + 0.5f);
            //Log.Message("Clip percent: " + clipPercent + " deltaPos: " + deltaPos);
            rotation = clip.Rotation.Evaluate(clipPercent) * (mirror ? -1 : 1);
        }

        public void AnimateThing(Thing thing)
        {
            thing.Graphic.Draw(deltaPos + anchor, mirror ? Rot4.West : Rot4.East, thing, rotation);
        }

        public bool LoopNeverending()
        {
            if (pawn?.jobs?.curDriver != null &&
                (pawn.jobs.curDriver is JobDriver_Sex) && (pawn.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 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<String> interactionDefTypes = null;
        public List<string> tags = new List<string>();

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


code:
using System;
using System.Collections.Generic;
using RimWorld;
using UnityEngine;
using Verse;
using Rimworld_Animations;

namespace Rimworld_Animations {

	[StaticConstructorOnStartup]
	public static class PawnWoundDrawerExtension
	{
		public static void RenderPawnOverlay(this PawnWoundDrawer pawnWoundDrawer, Vector3 drawLoc, Mesh bodyMesh, Quaternion quat, bool drawNow, PawnOverlayDrawer.OverlayLayer layer, Rot4 pawnRot, bool? overApparel = null, Pawn pawn = null, PawnRenderFlags flags = new PawnRenderFlags())
		{
			if (pawn == null)
			{ return; }

			if (!flags.FlagSet(PawnRenderFlags.Portrait) && layer == PawnOverlayDrawer.OverlayLayer.Head)
			{
				CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();
				if (pawnAnimator != null && pawnAnimator.isAnimating && pawn.Drawer.renderer.graphics.headGraphic != null)
				{
					pawnRot = pawnAnimator.headFacing;
					quat = Quaternion.AngleAxis(angle: pawnAnimator.headAngle, axis: Vector3.up);
					float y = drawLoc.y;
					drawLoc = pawnAnimator.getPawnHeadPosition() - Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up) * pawn.Drawer.renderer.BaseHeadOffsetAt(pawnAnimator.headFacing);
					drawLoc.y = y;
				}
			}

			pawnWoundDrawer.RenderPawnOverlay(drawLoc, bodyMesh, quat, drawNow, layer, pawnRot, overApparel);
		}
	}

}


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;

                    string bodyTypeDef = (curPawn.story?.bodyType != null) ? curPawn.story.bodyType.ToString() : "";

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

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

                    listingStandard.Label("Name: " + curPawn.Name + " Race: " + curPawn.def.defName + " Actor Index: " + curPawn.TryGetComp<CompBodyAnimator>().ActorIndex + " Body Type (if any): " + bodyTypeDef + " 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");
                    }

                    bool mirrored = curPawn.TryGetComp<CompBodyAnimator>().Mirror;

                    float.TryParse(listingStandard.TextEntryLabeled("X Offset: ", offsetX.ToString()), out offsetX);
                    offsetX = listingStandard.Slider(offsetX, -2, 2);

                    float.TryParse(listingStandard.TextEntryLabeled("Z Offset: ", offsetZ.ToString()), out offsetZ);
                    offsetZ = listingStandard.Slider(offsetZ, -2, 2);

                    float.TryParse(listingStandard.TextEntryLabeled("Rotation: ", rotation.ToString()), out 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 + bodyTypeDef + ActorIndex].x || offsetZ != AnimationSettings.offsets[def.defName + curPawn.def.defName + bodyTypeDef + ActorIndex].y) {
                        AnimationSettings.offsets[def.defName + curPawn.def.defName + bodyTypeDef + ActorIndex] = new Vector2(offsetX, offsetZ);

                    }

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

                }

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

            listingStandard.End();

        }

        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 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("rjwanim");
            val.PatchAll(Assembly.GetExecutingAssembly());

        }
    }
}


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("rjwanim")).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("rjwanim")).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].

(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 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("rjwanim")).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 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;
        }
    }
}


code:
using HarmonyLib;
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
{
    [HarmonyPatch(typeof(JobDriver_Sex), "SexTick")]
    public class HarmonyPatch_SexTick
    {
        public static bool Prefix(JobDriver_Sex __instance, Pawn pawn, Thing target)
        {

			if ((target is Pawn) &&
				!(
				(target as Pawn)?.jobs?.curDriver is JobDriver_SexBaseReciever
				&&
				((target as Pawn).jobs.curDriver as JobDriver_SexBaseReciever).parteners.Any()
				&&
				((target as Pawn).jobs.curDriver as JobDriver_SexBaseReciever).parteners[0] == pawn))
            {

				__instance.ticks_left--;
                __instance.sex_ticks--;
                __instance.Orgasm();

				if (pawn.IsHashIntervalTick(__instance.ticks_between_thrusts))
				{
					__instance.ChangePsyfocus(pawn, target);
					__instance.Animate(pawn, target);
					__instance.PlaySexSound();
					if (!__instance.Sexprops.isRape)
					{
						pawn.GainComfortFromCellIfPossible(false);
						if (target is Pawn)
						{
							(target as Pawn).GainComfortFromCellIfPossible(false);
						}
					}
					if(!__instance.isEndytophile)
                    {
						SexUtility.DrawNude(pawn, false);
                    }
				}

				return false;
            }

            return true;
        }

    }

}


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

namespace Rimworld_Animations {
    /*
    [HarmonyPatch(typeof(WorkGiver_Sex), "JobOnThing")]
    public static class HarmonyPatch_WorkGiverSex {

        public static bool Prefix(ref Job __result, ref Thing t) {

            Building_Bed bed = RestUtility.CurrentBed(t as Pawn);
            if (bed == null) {
                return false;
            }
            __result = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("JoinInBedAnimation", true), t as Pawn, bed);
            return false;

        }

    }

    */
}


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 System.Reflection.Emit;
using Verse.AI;

namespace Rimworld_Animations
{

    [HarmonyPatch(typeof(Bed_Utility), "in_same_bed")]
    public static class HarmonyPatch_JobDriver_InSameBedPatch
    {

        public static bool Prefix(Pawn partner, ref bool __result)
        {

            if(partner != null && partner.CurJobDef == xxx.casual_sex)
            {
                __result = true;
                return false;
            }

            return true;

        }

    }

    [HarmonyPatch(typeof(JobDriver_JoinInBed), "MakeNewToils")]
    public static class HarmonyPatch_JobDriver_JoinInBed
    {

        public static void Postfix(JobDriver_JoinInBed __instance, ref IEnumerable<Toil> __result)
        {

            var toils = __result.ToList();

            Toil goToPawnInBed = Toils_Goto.GotoThing(__instance.iTarget, PathEndMode.OnCell);
            goToPawnInBed.FailOn(() => !RestUtility.InBed(__instance.Partner) && __instance.Partner.CurJobDef != xxx.gettin_loved && !Bed_Utility.in_same_bed(__instance.Partner, __instance.pawn));

            toils[1] = goToPawnInBed;

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

                if (!(__instance.Partner.jobs.curDriver is JobDriver_SexBaseReciever)) // allows threesomes
                {
                    Job gettinLovedJob = JobMaker.MakeJob(xxx.gettin_loved, __instance.pawn, __instance.Bed); // new gettin loved toil that wakes up the pawn goes here
                    __instance.Partner.jobs.jobQueue.EnqueueFirst(gettinLovedJob);
                    __instance.Partner.jobs.EndCurrentJob(JobCondition.InterruptForced);
                }

            };

            toils[2] = startPartnerSex;

            toils[3].AddPreTickAction(() =>
            {
                if (!__instance.Partner.TryGetComp<CompBodyAnimator>().isAnimating)
                {
                    __instance.pawn.TryGetComp<CompBodyAnimator>().isAnimating = false;
                }
            });

            __result = toils.AsEnumerable();

        }

    }
}


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;
			}

			if(!AnimationSettings.PlayAnimForNonsexualActs && NonSexualAct(__instance))
            {
				return;
            }

			Pawn pawn = __instance.pawn;

			Building_Bed bed = __instance.Bed;

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

				Pawn Target = __instance.Target as 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.Sexprops.sexType, quickie, sexProps: __instance.Sexprops);
                }
				else {
					RerollAnimations(Target, out AnimationTimeTicks, sexType: __instance.Sexprops.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);

					int animTicks = anim.animationTimeTicks - (fastAnimForQuickie ? anim.animationStages[0].playTimeTicks : 0);
					(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).ticks_left = animTicks;
					(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).sex_ticks = animTicks;
					(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).orgasmStartTick = animTicks;
					(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).duration = animTicks;

					AnimationTimeTicks = animTicks;

					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
				}

				*/
			}
		}

		static IEnumerable<String> NonSexActRulePackDefNames = new String[]
		{
			"MutualHandholdingRP",
			"MutualMakeoutRP",
		};

		public static bool NonSexualAct(JobDriver_SexBaseInitiator sexBaseInitiator)
        {
			if(NonSexActRulePackDefNames.Contains(sexBaseInitiator.Sexprops.rulePack))
            {
				return true;
            }
			return false;
        }
	}

	[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 HarmonyLib;
using RimWorld;
using Verse;
using rjw;
using System.Reflection.Emit;
using Verse.AI;

namespace Rimworld_Animations
{

    [HarmonyPatch(typeof(JobDriver_SexBaseRecieverLoved), "MakeSexToil")]
    public static class HarmonyPatch_JobDriver_SexBaseReceiverLoved
    {
        public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> codeInstructions)
        {

            var ins = codeInstructions.ToList();
            for(int i = 0; i < ins.Count; i++)
            {
                if(i < ins.Count && ins[i].opcode == OpCodes.Call && ins[i].OperandIs(AccessTools.DeclaredMethod(typeof(Toils_LayDown), "LayDown"))) {

                    ins[i].operand = AccessTools.DeclaredMethod(typeof(HarmonyPatch_JobDriver_SexBaseReceiverLoved), "DoNotLayDown");
                    yield return ins[i];

                }

                else
                {
                    yield return ins[i];
                }
            }

        }

        public static Toil DoNotLayDown(TargetIndex bedOrRestSpotIndex, bool hasBed, bool lookForOtherJobs, bool canSleep = true, bool gainRestAndHealth = true, PawnPosture noBedLayingPosture = PawnPosture.LayingMask, bool deathrest = false)
        {
            return new Toil();
        }

    }
}


code:
using HarmonyLib;
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
{
    [HarmonyPatch(typeof(PawnRenderer), "DrawHeadHair")]
    public static class HarmonyPatch_HeadHair
    {
        public static void Prefix(ref Vector3 headOffset, ref float angle)
        {

        }

    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
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(RotDrawMode),
					typeof(PawnRenderFlags)
		}
	)]
	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, RotDrawMode bodyDrawType, PawnRenderFlags flags)
		{

			if (flags.FlagSet(PawnRenderFlags.Portrait)) return;

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

			if (bodyAnim != null && bodyAnim.isAnimating && pawn.Map == Find.CurrentMap)
			{
				bodyAnim.animatePawnBody(ref rootLoc, ref angle, ref bodyFacing);

			}

		}

		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> ins = instructions.ToList();

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

				if (i - 3 >= 0 && ins[i - 3].opcode == OpCodes.Call && ins[i - 3].operand != null && ins[i - 3].OperandIs(AccessTools.DeclaredMethod(typeof(PawnRenderer), "BaseHeadOffsetAt")))
                {

					yield return new CodeInstruction(OpCodes.Ldloca, (object)0);
					yield return new CodeInstruction(OpCodes.Ldloca, (object)9);
					yield return new CodeInstruction(OpCodes.Ldloca, (object)8);
					yield return new CodeInstruction(OpCodes.Ldarga, (object)2);
					yield return new CodeInstruction(OpCodes.Ldarg_0);
					yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(PawnRenderer), "pawn"));
					yield return new CodeInstruction(OpCodes.Ldarg, (object)6);
					yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(AnimationUtility), "AdjustHead"));
					yield return ins[i];
					//headFacing equals true
                }
				// Fixes the offsets for eye implants and wounds on the head during animations
				else if (ins[i].opcode == OpCodes.Callvirt && ins[i].operand != null && ins[i].OperandIs(AccessTools.DeclaredMethod(typeof(PawnOverlayDrawer), "RenderPawnOverlay")))
				{
					// Pass some additional info to a new overload of RenderOverBody
					yield return new CodeInstruction(OpCodes.Ldarg_0);
					yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(PawnRenderer), "pawn"));
					yield return new CodeInstruction(OpCodes.Ldarg_S, (object)6); // renderer flags
					yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(PawnWoundDrawerExtension), "RenderPawnOverlay"));
				}
				else
                {
					yield return ins[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.Text;
using System.Threading.Tasks;
using Verse;

namespace Rimworld_Animations
{
	[HarmonyPatch(typeof(PawnGraphicSet), "ResolveApparelGraphics")]
	public static class HarmonyPatch_ResolveApparelGraphics
	{
		public static bool Prefix(ref Pawn ___pawn)
		{

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


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

namespace Rimworld_Animations
{
	[HarmonyPatch(typeof(PawnRenderer), "RenderPawnAt")]
	public static class PawnRenderer_RenderPawnAt_Patch
	{
		static bool ClearCache(Pawn pawn)
		{
			return pawn.IsInvisible() || (pawn.TryGetComp<CompBodyAnimator>() != null && pawn.TryGetComp<CompBodyAnimator>().isAnimating);
		}

		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			var list = instructions.ToList();

			foreach (CodeInstruction i in instructions)
			{
				if (i.OperandIs(AccessTools.Method(typeof(PawnUtility), "IsInvisible")))
				{
					yield return new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PawnRenderer_RenderPawnAt_Patch), "ClearCache"));
				}
				else
				{
					yield return i;
				}
			}
		}
	}

}


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

namespace Rimworld_Animations
{
    [HarmonyPatch(typeof(Thing), "DrawAt")]
    public static class HarmonyPatch_Thing
    {

        public static bool Prefix(Thing __instance)
        {
            CompThingAnimator thingAnimator = __instance.TryGetComp<CompThingAnimator>();
            if (thingAnimator != null && thingAnimator.isAnimating)
            {
                thingAnimator.AnimateThing(__instance);
                return false;

            }

            return true;

        }

    }
}


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

namespace Rimworld_Animations {

    public class AnimationSettings : ModSettings {

        public static bool orgasmQuiver, rapeShiver, soundOverride = true, hearts = true, controlGenitalRotation = false, applySemenOnAnimationOrgasm = false, fastAnimForQuickie = false,
            PlayAnimForNonsexualActs = true;
        public static bool offsetTab = false, debugMode = false;
        public static float shiverIntensity = 2f;

        public static Dictionary<string, Vector2> offsets = new Dictionary<string, Vector2>();
        public static Dictionary<string, float> rotation = new Dictionary<string, float>();

        public override void ExposeData() {

            base.ExposeData();

            Scribe_Values.Look(ref debugMode, "RJWAnimations-AnimsDebugMode", false);
            Scribe_Values.Look(ref offsetTab, "RJWAnimations-EnableOffsetTab", false);
            Scribe_Values.Look(ref controlGenitalRotation, "RJWAnimations-controlGenitalRotation", false);
            Scribe_Values.Look(ref orgasmQuiver, "RJWAnimations-orgasmQuiver");
            Scribe_Values.Look(ref fastAnimForQuickie, "RJWAnimations-fastAnimForQuickie");
            Scribe_Values.Look(ref rapeShiver, "RJWAnimations-rapeShiver");
            Scribe_Values.Look(ref hearts, "RJWAnimation-sheartsOnLovin");
            Scribe_Values.Look(ref PlayAnimForNonsexualActs, "RJWAnims-PlayAnimForNonsexualActs");
            Scribe_Values.Look(ref applySemenOnAnimationOrgasm, "RJWAnimations-applySemenOnOrgasm", false);
            Scribe_Values.Look(ref soundOverride, "RJWAnimations-rjwAnimSoundOverride", true);
            Scribe_Values.Look(ref shiverIntensity, "RJWAnimations-shiverIntensity", 2f);
            //todo: save offsetsByDefName

            Scribe_Collections.Look(ref offsets, "RJWAnimations-animationOffsets");
            Scribe_Collections.Look(ref rotation, "RJWAnimations-rotationOffsets");

            //needs to be rewritten
            //probably somewhere in options?

        }

    }

    public class RJW_Animations : Mod {

        public RJW_Animations(ModContentPack content) : base(content) {
            GetSettings<AnimationSettings>();

        }

        public override void DoSettingsWindowContents(Rect inRect) {

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

            listingStandard.CheckboxLabeled("Enable Sound Override", ref AnimationSettings.soundOverride);
            listingStandard.CheckboxLabeled("Control Genital Rotation", ref AnimationSettings.controlGenitalRotation);
            listingStandard.CheckboxLabeled("Play Fast Animation for Quickie", ref AnimationSettings.fastAnimForQuickie);
            listingStandard.CheckboxLabeled("Apply Semen on Animation Orgasm", ref AnimationSettings.applySemenOnAnimationOrgasm);

            if(AnimationSettings.applySemenOnAnimationOrgasm) {
                listingStandard.Label("Recommended--turn down \"Cum on body percent\" in RJW settings to about 33%");
            }

            listingStandard.CheckboxLabeled("Enable Orgasm Quiver", ref AnimationSettings.orgasmQuiver);
            listingStandard.CheckboxLabeled("Enable Rape Shiver", ref AnimationSettings.rapeShiver);
            listingStandard.CheckboxLabeled("Enable hearts during lovin'", ref AnimationSettings.hearts);
            listingStandard.CheckboxLabeled("Play animation for nonsexual acts (handholding, makeout)", ref AnimationSettings.PlayAnimForNonsexualActs);
            listingStandard.CheckboxLabeled("Enable Animation Manager Tab", ref AnimationSettings.offsetTab);

            listingStandard.Label("Shiver/Quiver Intensity (default 2): " + AnimationSettings.shiverIntensity);
            AnimationSettings.shiverIntensity = listingStandard.Slider(AnimationSettings.shiverIntensity, 0.0f, 12f);

            listingStandard.CheckboxLabeled("Debug Mode", ref AnimationSettings.debugMode);

            listingStandard.End();
            base.DoSettingsWindowContents(inRect);
        }

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

        }

        public override string SettingsCategory() {
            return "RJW Animation Settings";
        }
    }
}