diff --git a/1.5/Assemblies/0MultiplayerAPI.dll b/1.5/Assemblies/0MultiplayerAPI.dll
new file mode 100644
index 0000000..9648606
Binary files /dev/null and b/1.5/Assemblies/0MultiplayerAPI.dll differ
diff --git a/1.5/Assemblies/RJW.dll b/1.5/Assemblies/RJW.dll
new file mode 100644
index 0000000..c1b1980
Binary files /dev/null and b/1.5/Assemblies/RJW.dll differ
diff --git a/1.5/Assemblies/Rimworld-Animations.dll b/1.5/Assemblies/Rimworld-Animations.dll
new file mode 100644
index 0000000..93d7989
Binary files /dev/null and b/1.5/Assemblies/Rimworld-Animations.dll differ
diff --git a/1.5/Defs/MainTabDefs/MainButtonDef.xml b/1.5/Defs/MainTabDefs/MainButtonDef.xml
new file mode 100644
index 0000000..0674d24
--- /dev/null
+++ b/1.5/Defs/MainTabDefs/MainButtonDef.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ OffsetManager
+
+ Control pawn offsets
+ Rimworld_Animations.MainTabWindow_OffsetConfigure
+ 54
+ false
+ UI/MainTab
+ true
+
+
+
\ No newline at end of file
diff --git a/1.5/Defs/SoundDefs/Sounds_Sex.xml b/1.5/Defs/SoundDefs/Sounds_Sex.xml
new file mode 100644
index 0000000..f05a1a8
--- /dev/null
+++ b/1.5/Defs/SoundDefs/Sounds_Sex.xml
@@ -0,0 +1,212 @@
+
+
+
+
+ Cum
+ MapOnly
+
+ 1
+ 1
+
+
+
+
+ Sex/cum
+
+
+
+ 30
+ 40
+
+
+ 0.8
+ 1.2
+
+
+ 0
+ 51.86047
+
+ False
+
+
+
+
+ Sex
+ MapOnly
+
+ 1
+ 1
+
+
+
+
+ Sex/kucyu04
+
+
+
+ 16
+ 16
+
+
+ 0.8
+ 1.2
+
+
+ 0
+ 51.86047
+
+ False
+
+
+
+
+ Suck
+ MapOnly
+
+ 1
+ 1
+
+
+
+
+ Sex/Suck/Suck_1
+
+
+ Sex/Suck/Suck_2
+
+
+ Sex/Suck/Suck_3
+
+
+ Sex/Suck/Suck_4
+
+
+ Sex/Suck/Suck_5
+
+
+ Sex/Suck/Suck_6
+
+
+ Sex/Suck/Suck_7
+
+
+ Sex/Suck/Suck_8
+
+
+ Sex/Suck/Suck_9
+
+
+ Sex/Suck/Suck_10
+
+
+
+ 20
+ 35
+
+
+ 1.0
+ 1.0
+
+
+ 0
+ 51.86047
+
+ NeverTwice
+ false
+
+
+
+
+ Fuck
+ MapOnly
+
+ 1
+ 1
+
+
+
+
+ Sex/Clap_1
+
+
+ Sex/Clap_2
+
+
+ Sex/Clap_3
+
+
+ Sex/Clap_4
+
+
+ Sex/Clap_5
+
+
+ Sex/Clap_6
+
+
+ Sex/Clap_7
+
+
+ Sex/Clap_8
+
+
+
+ 45
+ 70
+
+
+ 1.0
+ 1.0
+
+
+ 0
+ 51.86047
+
+ NeverTwice
+ false
+
+
+
+
+ Slimy
+ MapOnly
+
+ 1
+ 1
+
+
+
+
+ Sex/Slime/Slimy1
+
+
+ Sex/Slime/Slimy2
+
+
+ Sex/Slime/Slimy3
+
+
+ Sex/Slime/Slimy4
+
+
+ Sex/Slime/Slimy5
+
+
+
+ 45
+ 75
+
+
+ 1.4
+ 1.8
+
+
+ 0
+ 100
+
+ NeverTwice
+ false
+
+
+
+
\ No newline at end of file
diff --git a/1.5/Patch_HatsDisplaySelection/Patch_HatsDisplaySelection.csproj b/1.5/Patch_HatsDisplaySelection/Patch_HatsDisplaySelection.csproj
new file mode 100644
index 0000000..8da21a6
--- /dev/null
+++ b/1.5/Patch_HatsDisplaySelection/Patch_HatsDisplaySelection.csproj
@@ -0,0 +1,75 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {BA766964-1716-422D-A09E-29426F8EB9D5}
+ Library
+ Properties
+ Patch_HatsDisplaySelection
+ Patch_HatsDisplaySelection
+ v4.7.2
+ 512
+ true
+
+
+ false
+ none
+ false
+ 1.2\Assemblies\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\..\..\..\..\workshop\content\294100\2009463077\Current\Assemblies\0Harmony.dll
+ False
+
+
+ ..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll
+ False
+
+
+ ..\..\..\..\..\workshop\content\294100\1542291825\1.2\Assemblies\HatDisplaySelection.dll
+ False
+
+
+ ..\1.2\Assemblies\Rimworld-Animations.dll
+ False
+
+
+
+
+
+
+
+
+
+
+ ..\..\..\RimWorldWin64_Data\Managed\UnityEngine.dll
+ False
+
+
+ ..\..\..\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1.5/Patch_HumanoidAlienRaces/Assemblies/Patch_HumanoidAlienRaces.dll b/1.5/Patch_HumanoidAlienRaces/Assemblies/Patch_HumanoidAlienRaces.dll
new file mode 100644
index 0000000..bba2133
Binary files /dev/null and b/1.5/Patch_HumanoidAlienRaces/Assemblies/Patch_HumanoidAlienRaces.dll differ
diff --git a/1.5/Patch_HumanoidAlienRaces/Patch_HumanoidAlienRaces.csproj b/1.5/Patch_HumanoidAlienRaces/Patch_HumanoidAlienRaces.csproj
new file mode 100644
index 0000000..78070ff
--- /dev/null
+++ b/1.5/Patch_HumanoidAlienRaces/Patch_HumanoidAlienRaces.csproj
@@ -0,0 +1,82 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {0AD4F9B2-AE28-4E42-86D6-611FB93B7FF0}
+ Library
+ Properties
+ Patch_HumanoidAlienRaces
+ Patch_HumanoidAlienRaces
+ v4.8
+ 512
+ true
+
+
+
+ false
+ none
+ false
+ Assemblies\
+ DEBUG;TRACE
+ prompt
+ 4
+ Off
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\..\..\..\..\..\workshop\content\294100\839005762\1.4\Assemblies\0Harmony.dll
+ False
+
+
+ ..\..\..\..\..\..\workshop\content\294100\839005762\1.4\Assemblies\AlienRace.dll
+ False
+
+
+ ..\..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll
+ False
+
+
+ ..\..\..\..\..\..\workshop\content\294100\818773962\v1.4\Assemblies\HugsLib.dll
+ False
+
+
+ ..\Assemblies\Rimworld-Animations.dll
+ False
+
+
+ ..\..\..\rjw\1.4\Assemblies\RJW.dll
+ False
+
+
+
+
+
+
+
+
+
+
+ ..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.dll
+ False
+
+
+ ..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll
+ False
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1.5/Patch_HumanoidAlienRaces/Properties/AssemblyInfo.cs b/1.5/Patch_HumanoidAlienRaces/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..8139424
--- /dev/null
+++ b/1.5/Patch_HumanoidAlienRaces/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+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("Patch_HumanoidAlienRaces")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Patch_HumanoidAlienRaces")]
+[assembly: AssemblyCopyright("Copyright © 2022")]
+[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("0ad4f9b2-ae28-4e42-86d6-611fb93b7ff0")]
+
+// 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")]
diff --git a/1.5/Patches/CompatibilityPatch_FacialAnimation.xml b/1.5/Patches/CompatibilityPatch_FacialAnimation.xml
new file mode 100644
index 0000000..125d79f
--- /dev/null
+++ b/1.5/Patches/CompatibilityPatch_FacialAnimation.xml
@@ -0,0 +1,130 @@
+
+
+
+
+ [NL] Facial Animation - WIP
+
+
+ Always
+
+
+ /Defs/FacialAnimation.FaceAnimationDef[defName="Lovin" or defName="Lovin2"]/targetJobs
+ Always
+
+ RJW_Masturbate
+ GettinBred
+ Bestiality
+ BestialityForFemale
+ ViolateCorpse
+ Quickie
+ GettingQuickie
+ GettinRaped
+ JoinInBed
+ GettinLoved
+ GettinLicked
+ GettinSucked
+ WhoreIsServingVisitors
+ JoinInBedAnimation
+ GettinLovedAnimation
+
+
+
+ /Defs/FacialAnimation.FaceAnimationDef[defName="WaitCombat" or defName="Wait_Combat_Rare"]/targetJobs
+ Always
+
+ RapeComfortPawn
+ RandomRape
+ RapeEnemy
+
+
+
+ /Defs/FacialAnimation.FaceAnimationDef[defName="StandAndBeSociallyActive"]/targetJobs
+ Always
+
+ WhoreInvitingVisitors
+
+
+
+ /Defs/FacialAnimation.FaceAnimationDef[defName="Wear" or defName="Wear2" or defName="Wear3"]/targetJobs
+ Always
+
+ CleanSelf
+ StruggleInBondageGear
+
+
+
+
+ Rimworld-Animations
+
+
+ Always
+
+
+ /Defs/FacialAnimation.FaceAnimationDef[defName="Lovin" or defName="Lovin2"]/animationFrames/li[1]/headOffset
+ Always
+
+
+ /Defs/FacialAnimation.FaceAnimationDef[defName="Lovin"]/animationFrames/li[2]/headOffset
+ Always
+
+
+ /Defs/FacialAnimation.FaceAnimationDef[defName="Lovin"]/animationFrames/li[3]/headOffset
+ Always
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1.5/Sounds/Sex/Clap_1.wav b/1.5/Sounds/Sex/Clap_1.wav
new file mode 100644
index 0000000..bccd0f2
Binary files /dev/null and b/1.5/Sounds/Sex/Clap_1.wav differ
diff --git a/1.5/Sounds/Sex/Clap_2.wav b/1.5/Sounds/Sex/Clap_2.wav
new file mode 100644
index 0000000..a382f59
Binary files /dev/null and b/1.5/Sounds/Sex/Clap_2.wav differ
diff --git a/1.5/Sounds/Sex/Clap_3.wav b/1.5/Sounds/Sex/Clap_3.wav
new file mode 100644
index 0000000..65cf39b
Binary files /dev/null and b/1.5/Sounds/Sex/Clap_3.wav differ
diff --git a/1.5/Sounds/Sex/Clap_4.wav b/1.5/Sounds/Sex/Clap_4.wav
new file mode 100644
index 0000000..3ae1b3e
Binary files /dev/null and b/1.5/Sounds/Sex/Clap_4.wav differ
diff --git a/1.5/Sounds/Sex/Clap_5.wav b/1.5/Sounds/Sex/Clap_5.wav
new file mode 100644
index 0000000..65144e7
Binary files /dev/null and b/1.5/Sounds/Sex/Clap_5.wav differ
diff --git a/1.5/Sounds/Sex/Clap_6.wav b/1.5/Sounds/Sex/Clap_6.wav
new file mode 100644
index 0000000..0026325
Binary files /dev/null and b/1.5/Sounds/Sex/Clap_6.wav differ
diff --git a/1.5/Sounds/Sex/Clap_7.wav b/1.5/Sounds/Sex/Clap_7.wav
new file mode 100644
index 0000000..6d7de2a
Binary files /dev/null and b/1.5/Sounds/Sex/Clap_7.wav differ
diff --git a/1.5/Sounds/Sex/Clap_8.wav b/1.5/Sounds/Sex/Clap_8.wav
new file mode 100644
index 0000000..1af5710
Binary files /dev/null and b/1.5/Sounds/Sex/Clap_8.wav differ
diff --git a/1.5/Sounds/Sex/Slime/Slimy1.wav b/1.5/Sounds/Sex/Slime/Slimy1.wav
new file mode 100644
index 0000000..3cfbd74
Binary files /dev/null and b/1.5/Sounds/Sex/Slime/Slimy1.wav differ
diff --git a/1.5/Sounds/Sex/Slime/Slimy2.wav b/1.5/Sounds/Sex/Slime/Slimy2.wav
new file mode 100644
index 0000000..36a9197
Binary files /dev/null and b/1.5/Sounds/Sex/Slime/Slimy2.wav differ
diff --git a/1.5/Sounds/Sex/Slime/Slimy3.wav b/1.5/Sounds/Sex/Slime/Slimy3.wav
new file mode 100644
index 0000000..40aff1e
Binary files /dev/null and b/1.5/Sounds/Sex/Slime/Slimy3.wav differ
diff --git a/1.5/Sounds/Sex/Slime/Slimy4.wav b/1.5/Sounds/Sex/Slime/Slimy4.wav
new file mode 100644
index 0000000..0b6404e
Binary files /dev/null and b/1.5/Sounds/Sex/Slime/Slimy4.wav differ
diff --git a/1.5/Sounds/Sex/Slime/Slimy5.wav b/1.5/Sounds/Sex/Slime/Slimy5.wav
new file mode 100644
index 0000000..ea310db
Binary files /dev/null and b/1.5/Sounds/Sex/Slime/Slimy5.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Suck_1.wav b/1.5/Sounds/Sex/Suck/Suck_1.wav
new file mode 100644
index 0000000..4f1eafd
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Suck_1.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Suck_10.wav b/1.5/Sounds/Sex/Suck/Suck_10.wav
new file mode 100644
index 0000000..284cda3
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Suck_10.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Suck_2.wav b/1.5/Sounds/Sex/Suck/Suck_2.wav
new file mode 100644
index 0000000..a8305c1
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Suck_2.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Suck_3.wav b/1.5/Sounds/Sex/Suck/Suck_3.wav
new file mode 100644
index 0000000..95e7348
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Suck_3.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Suck_4.wav b/1.5/Sounds/Sex/Suck/Suck_4.wav
new file mode 100644
index 0000000..753a023
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Suck_4.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Suck_5.wav b/1.5/Sounds/Sex/Suck/Suck_5.wav
new file mode 100644
index 0000000..8ecda9c
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Suck_5.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Suck_6.wav b/1.5/Sounds/Sex/Suck/Suck_6.wav
new file mode 100644
index 0000000..08567d6
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Suck_6.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Suck_7.wav b/1.5/Sounds/Sex/Suck/Suck_7.wav
new file mode 100644
index 0000000..a63b0e4
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Suck_7.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Suck_8.wav b/1.5/Sounds/Sex/Suck/Suck_8.wav
new file mode 100644
index 0000000..320da09
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Suck_8.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Suck_9.wav b/1.5/Sounds/Sex/Suck/Suck_9.wav
new file mode 100644
index 0000000..7ab538a
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Suck_9.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Swallow_1.wav b/1.5/Sounds/Sex/Suck/Swallow_1.wav
new file mode 100644
index 0000000..f3276cc
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Swallow_1.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Swallow_2.wav b/1.5/Sounds/Sex/Suck/Swallow_2.wav
new file mode 100644
index 0000000..09a7a00
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Swallow_2.wav differ
diff --git a/1.5/Sounds/Sex/Suck/Swallow_3.wav b/1.5/Sounds/Sex/Suck/Swallow_3.wav
new file mode 100644
index 0000000..2817b29
Binary files /dev/null and b/1.5/Sounds/Sex/Suck/Swallow_3.wav differ
diff --git a/1.5/Sounds/Sex/cum.wav b/1.5/Sounds/Sex/cum.wav
new file mode 100644
index 0000000..ef98437
Binary files /dev/null and b/1.5/Sounds/Sex/cum.wav differ
diff --git a/1.5/Sounds/Sex/kucyu04.wav b/1.5/Sounds/Sex/kucyu04.wav
new file mode 100644
index 0000000..3ae1ce8
Binary files /dev/null and b/1.5/Sounds/Sex/kucyu04.wav differ
diff --git a/1.5/Source/Actors/Actor.cs b/1.5/Source/Actors/Actor.cs
new file mode 100644
index 0000000..3b382dd
--- /dev/null
+++ b/1.5/Source/Actors/Actor.cs
@@ -0,0 +1,26 @@
+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 defNames;
+ public List requiredGenitals;
+ public List raceOffsets;
+ public List blacklistedRaces;
+ public bool initiator = false;
+ public string gender;
+ public bool isFucking = false;
+ public bool isFucked = false;
+ public bool controlGenitalAngle = false;
+ public List bodyDefTypes = new List();
+ public BodyTypeOffset bodyTypeOffset = new BodyTypeOffset();
+ public Vector3 offset = new Vector2(0, 0);
+ public List requiredGender;
+ public List tags = new List();
+ }
+}
diff --git a/1.5/Source/Actors/AlienRaceOffset.cs b/1.5/Source/Actors/AlienRaceOffset.cs
new file mode 100644
index 0000000..a5bbe20
--- /dev/null
+++ b/1.5/Source/Actors/AlienRaceOffset.cs
@@ -0,0 +1,15 @@
+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;
+
+ }
+}
diff --git a/1.5/Source/Actors/BodyTypeOffset.cs b/1.5/Source/Actors/BodyTypeOffset.cs
new file mode 100644
index 0000000..82a23a3
--- /dev/null
+++ b/1.5/Source/Actors/BodyTypeOffset.cs
@@ -0,0 +1,18 @@
+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;
+
+ }
+}
diff --git a/1.5/Source/Comps/CompProperties_ThingAnimator.cs b/1.5/Source/Comps/CompProperties_ThingAnimator.cs
new file mode 100644
index 0000000..34c67b1
--- /dev/null
+++ b/1.5/Source/Comps/CompProperties_ThingAnimator.cs
@@ -0,0 +1,17 @@
+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);
+ }
+ }
+}
diff --git a/1.5/Source/Comps/CompThingAnimator.cs b/1.5/Source/Comps/CompThingAnimator.cs
new file mode 100644
index 0000000..64dcc59
--- /dev/null
+++ b/1.5/Source/Comps/CompThingAnimator.cs
@@ -0,0 +1,23 @@
+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
+ {
+
+ public override void CompTick()
+ {
+ //todo: If item is held by pawn, and pawn is doing thingcomp animation,
+ //animate thingcomp; see CompPowerPlantWind for how thingcomps are animated
+ return;
+ }
+ }
+
+}
diff --git a/1.5/Source/MainTabWindows/MainTabWindow_OffsetConfigure.cs b/1.5/Source/MainTabWindows/MainTabWindow_OffsetConfigure.cs
new file mode 100644
index 0000000..45784d0
--- /dev/null
+++ b/1.5/Source/MainTabWindows/MainTabWindow_OffsetConfigure.cs
@@ -0,0 +1,136 @@
+using System.Collections.Generic;
+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 curPawn) {
+
+ if (CompBodyAnimator.IsAnimating(curPawn)) {
+
+ CompBodyAnimator compBodyAnimator = curPawn.TryGetComp();
+ AnimationDef def = compBodyAnimator.CurrentAnimation;
+ int ActorIndex = 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().ActorIndex + " Body Type (if any): " + bodyTypeDef + " Animation: " + def.label + (curPawn.TryGetComp().Mirror ? " mirrored" : ""));
+
+ if(curPawn.def.defName == "Human") {
+ listingStandard.Label("Warning--You generally don't want to change human offsets, only alien offsets");
+ }
+
+ 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().actorsInCurrentAnimation.Count; i++) {
+
+ Pawn actor = curPawn.TryGetComp().actorsInCurrentAnimation[i];
+
+ actor.TryGetComp()?.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();
+ }
+
+ if(AnimationSettings.rotation == null) {
+ if (AnimationSettings.debugMode)
+ Log.Message("New rotation");
+ AnimationSettings.rotation = new Dictionary();
+ }
+ }
+
+ public override void PostClose() {
+ base.PostClose();
+
+ LoadedModManager.GetMod().WriteSettings();
+ }
+ }
+}
diff --git a/1.5/Source/MainTabWindows/OffsetMainButtonDefOf.cs b/1.5/Source/MainTabWindows/OffsetMainButtonDefOf.cs
new file mode 100644
index 0000000..e7ad9eb
--- /dev/null
+++ b/1.5/Source/MainTabWindows/OffsetMainButtonDefOf.cs
@@ -0,0 +1,22 @@
+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));
+ }
+
+ }
+}
diff --git a/1.5/Source/MainTabWindows/WorldComponent_UpdateMainTab.cs b/1.5/Source/MainTabWindows/WorldComponent_UpdateMainTab.cs
new file mode 100644
index 0000000..2694419
--- /dev/null
+++ b/1.5/Source/MainTabWindows/WorldComponent_UpdateMainTab.cs
@@ -0,0 +1,24 @@
+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;
+ }
+
+
+ }
+}
diff --git a/1.5/Source/Patches/Harmony_PatchAll.cs b/1.5/Source/Patches/Harmony_PatchAll.cs
new file mode 100644
index 0000000..1c1d63f
--- /dev/null
+++ b/1.5/Source/Patches/Harmony_PatchAll.cs
@@ -0,0 +1,22 @@
+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());
+
+ }
+ }
+}
diff --git a/1.5/Source/Patches/RJWPatches/HarmonyPatch_PlaySexSounds.cs b/1.5/Source/Patches/RJWPatches/HarmonyPatch_PlaySexSounds.cs
new file mode 100644
index 0000000..402960e
--- /dev/null
+++ b/1.5/Source/Patches/RJWPatches/HarmonyPatch_PlaySexSounds.cs
@@ -0,0 +1,14 @@
+using HarmonyLib;
+using rjw;
+
+namespace Rimworld_Animations
+{
+ [HarmonyPatch(typeof(JobDriver_Sex), "PlaySexSound")]
+ class HarmonyPatch_PlaySexSounds
+ {
+ public static bool Prefix(JobDriver_Sex __instance)
+ {
+ return false;
+ }
+ }
+}
diff --git a/1.5/Source/Patches/RJWPatches/HarmonyPatch_SexTick.cs b/1.5/Source/Patches/RJWPatches/HarmonyPatch_SexTick.cs
new file mode 100644
index 0000000..9ba03b2
--- /dev/null
+++ b/1.5/Source/Patches/RJWPatches/HarmonyPatch_SexTick.cs
@@ -0,0 +1,61 @@
+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;
+ }
+
+ }
+
+}
diff --git a/1.5/Source/Patches/RJWPatches/HarmonyPatch_WorkGiverSex.cs b/1.5/Source/Patches/RJWPatches/HarmonyPatch_WorkGiverSex.cs
new file mode 100644
index 0000000..af4a755
--- /dev/null
+++ b/1.5/Source/Patches/RJWPatches/HarmonyPatch_WorkGiverSex.cs
@@ -0,0 +1,31 @@
+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.GetNamed("JoinInBedAnimation", true), t as Pawn, bed);
+ return false;
+
+ }
+
+ }
+
+ */
+}
diff --git a/1.5/Source/Patches/RJWPatches/JobDrivers/HarmonyPatch_JobDriver_JoinInBed.cs b/1.5/Source/Patches/RJWPatches/JobDrivers/HarmonyPatch_JobDriver_JoinInBed.cs
new file mode 100644
index 0000000..0f63b7d
--- /dev/null
+++ b/1.5/Source/Patches/RJWPatches/JobDrivers/HarmonyPatch_JobDriver_JoinInBed.cs
@@ -0,0 +1,62 @@
+using System.Collections.Generic;
+using System.Linq;
+using HarmonyLib;
+using RimWorld;
+using Verse;
+using rjw;
+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 __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;
+
+ __result = toils.AsEnumerable();
+ }
+ }
+}
diff --git a/1.5/Source/Patches/RJWPatches/JobDrivers/HarmonyPatch_JobDriver_SexBaseInitiator.cs b/1.5/Source/Patches/RJWPatches/JobDrivers/HarmonyPatch_JobDriver_SexBaseInitiator.cs
new file mode 100644
index 0000000..56d756c
--- /dev/null
+++ b/1.5/Source/Patches/RJWPatches/JobDrivers/HarmonyPatch_JobDriver_SexBaseInitiator.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+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(bed as Thing);
+ }
+ else {
+ RerollAnimations();
+ }
+
+
+ //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(Thing bed = null) {
+
+
+ }
+
+
+ static IEnumerable 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)
+ {
+ //stop animation
+ }
+ }
+}
diff --git a/1.5/Source/Patches/RJWPatches/JobDrivers/HarmonyPatch_JobDriver_SexBaseReceiverLoved.cs b/1.5/Source/Patches/RJWPatches/JobDrivers/HarmonyPatch_JobDriver_SexBaseReceiverLoved.cs
new file mode 100644
index 0000000..dfa116e
--- /dev/null
+++ b/1.5/Source/Patches/RJWPatches/JobDrivers/HarmonyPatch_JobDriver_SexBaseReceiverLoved.cs
@@ -0,0 +1,46 @@
+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 Transpiler(IEnumerable 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();
+ }
+
+ }
+}
diff --git a/1.5/Source/Patches/RimworldPatches/HarmonyPatch_Thing.cs b/1.5/Source/Patches/RimworldPatches/HarmonyPatch_Thing.cs
new file mode 100644
index 0000000..ec738f7
--- /dev/null
+++ b/1.5/Source/Patches/RimworldPatches/HarmonyPatch_Thing.cs
@@ -0,0 +1,33 @@
+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();
+ if (thingAnimator != null && thingAnimator.isAnimating)
+ {
+ thingAnimator.AnimateThing(__instance);
+ return false;
+
+ }
+
+ return true;
+
+ }
+
+
+ */
+ }
+}
diff --git a/1.5/Source/Settings/AnimationSettings.cs b/1.5/Source/Settings/AnimationSettings.cs
new file mode 100644
index 0000000..0a96621
--- /dev/null
+++ b/1.5/Source/Settings/AnimationSettings.cs
@@ -0,0 +1,98 @@
+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 offsets = new Dictionary();
+ public static Dictionary rotation = new Dictionary();
+
+ 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();
+
+ }
+
+ 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";
+ }
+ }
+}
diff --git a/1.5/Source/Utilities/AnimationUtility.cs b/1.5/Source/Utilities/AnimationUtility.cs
new file mode 100644
index 0000000..7df7d42
--- /dev/null
+++ b/1.5/Source/Utilities/AnimationUtility.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using RimWorld;
+using rjw.Modules.Interactions.Helpers;
+using rjw.Modules.Interactions.Objects;
+using UnityEngine;
+using Verse;
+using Verse.AI;
+using rjw.Modules.Interactions.Enums;
+
+namespace Rimworld_Animations {
+ public static class AnimationUtility {
+
+
+ public static bool GenitalCheckForPawn(List 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;
+
+ }
+ }
+}
diff --git a/1.5/Textures/UI/MainTab.png b/1.5/Textures/UI/MainTab.png
new file mode 100644
index 0000000..92f855f
Binary files /dev/null and b/1.5/Textures/UI/MainTab.png differ
diff --git a/Patch_HumanoidAlienRaces/1.4/Source/HarmonyPatch_AlienRace.cs b/Patch_HumanoidAlienRaces/1.4/Source/HarmonyPatch_AlienRace.cs
new file mode 100644
index 0000000..f06e987
--- /dev/null
+++ b/Patch_HumanoidAlienRaces/1.4/Source/HarmonyPatch_AlienRace.cs
@@ -0,0 +1,354 @@
+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()
+ {
+ (new Harmony("rjwanim")).Patch(AccessTools.Method(AccessTools.TypeByName("AlienRace.HarmonyPatches"), "DrawAddons"),
+ prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_AlienRace), "Prefix_AnimateHeadAddons")));
+ }
+
+ /* todo: replace jank prefix with this
+ public static void Prefix_DrawAddonsFinalHook(ref Pawn pawn, ref AlienPartGenerator.BodyAddon addon, ref Rot4 rot, ref Graphic graphic, ref Vector3 offsetVector, ref float angle, ref Material mat)
+ {
+ CompBodyAnimator animator = pawn.TryGetComp();
+
+ if (animator == null || !animator.isAnimating)
+ {
+ return;
+ }
+
+ if(addon.alignWithHead || addon.drawnInBed)
+ {
+ rot = animator.headFacing;
+ angle = animator.headAngle;
+ offsetVector += animator.deltaPos + animator.bodyAngle * animator.headBob;
+
+ }
+ else
+ {
+ rot = animator.bodyFacing;
+ angle = animator.bodyAngle;
+ offsetVector += animator.deltaPos;
+ }
+
+ }
+ */
+ public static bool Prefix_AnimateHeadAddons(PawnRenderFlags renderFlags, Vector3 vector, Vector3 headOffset, Pawn pawn, Quaternion quat, Rot4 rotation)
+ {
+
+ if (renderFlags.FlagSet(PawnRenderFlags.Portrait) || !CompBodyAnimator.IsAnimating(pawn)) return true;
+ if (!(pawn.def is ThingDef_AlienRace alienProps) || renderFlags.FlagSet(PawnRenderFlags.Invisible)) return false;
+
+ List addons = alienProps.alienRace.generalSettings.alienPartGenerator.bodyAddons;
+ AlienPartGenerator.AlienComp comp = pawn.GetComp();
+ CompBodyAnimator pawnAnimator = pawn.TryGetComp();
+
+ bool flag = renderFlags.FlagSet(PawnRenderFlags.Portrait);
+ bool flag2 = renderFlags.FlagSet(PawnRenderFlags.Invisible);
+
+ for (int i = 0; i < addons.Count; i++)
+ {
+ AlienPartGenerator.BodyAddon ba = addons[index: i];
+
+ if (!ba.CanDrawAddon(pawn: pawn)) continue;
+
+ bool forceDrawForBody = false;
+ if (alienProps.defName.Contains("Orassan") && ba.path.ToLower().Contains("tail"))
+ {
+ forceDrawForBody = true;
+ }
+ AlienPartGenerator.RotationOffset offset = ba.defaultOffsets.GetOffset((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing);
+ Vector3 a = (offset != null) ? offset.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, pawn.story.headType) : Vector3.zero;
+ AlienPartGenerator.RotationOffset offset2 = ba.offsets.GetOffset((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing);
+ Vector3 vector2 = a + ((offset2 != null) ? offset2.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, pawn.story.headType) : Vector3.zero);
+ vector2.y = (ba.inFrontOfBody ? (0.3f + vector2.y) : (-0.3f - vector2.y));
+ float num = ba.angle;
+ if (((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing) == Rot4.North)
+ {
+ if (ba.layerInvert)
+ {
+ vector2.y = 0f - vector2.y;
+ }
+ num = 0f;
+ }
+ if (((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing) == Rot4.East)
+ {
+ num = 0f - num;
+ vector2.x = 0f - vector2.x;
+ }
+ Graphic addonGraphic = comp.addonGraphics[i];
+
+ addonGraphic.drawSize = ((flag && ba.drawSizePortrait != Vector2.zero) ? ba.drawSizePortrait : ba.drawSize) * (ba.scaleWithPawnDrawsize ? (ba.alignWithHead ? ((flag ? comp.customPortraitHeadDrawSize : comp.customHeadDrawSize) * (ModsConfig.BiotechActive ? (pawn.ageTracker.CurLifeStage.headSizeFactor ?? 1.5f) : 1.5f)) : ((flag ? comp.customPortraitDrawSize : comp.customDrawSize) * (ModsConfig.BiotechActive ? pawn.ageTracker.CurLifeStage.bodySizeFactor : 1f) * 1.5f)) : (Vector2.one * 1.5f));
+
+ if ((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead)
+ {
+
+ Quaternion addonRotation = Quaternion.AngleAxis(pawnAnimator.headAngle < 0 ? 360 - (360 % pawnAnimator.headAngle) : pawnAnimator.headAngle, Vector3.up);
+
+ GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: pawnAnimator.headFacing), loc: vector + (ba.alignWithHead ? headOffset : headOffset - addonRotation * pawn.Drawer.renderer.BaseHeadOffsetAt(pawnAnimator.headFacing)) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f),
+ quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * addonRotation, mat: addonGraphic.MatAt(rot: pawnAnimator.headFacing), renderFlags.FlagSet(PawnRenderFlags.DrawNow));
+
+
+ }
+
+ else
+ {
+ Quaternion addonRotation;
+ if (AnimationSettings.controlGenitalRotation && ba.path.ToLower().Contains("penis"))
+ {
+ addonRotation = Quaternion.AngleAxis(pawnAnimator.genitalAngle, Vector3.up);
+ }
+ else
+ {
+ addonRotation = Quaternion.AngleAxis(pawnAnimator.bodyAngle, Vector3.up);
+ }
+
+ 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")))
+ {
+ GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: rotation), loc: vector + (ba.alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f),
+ quat: Quaternion.AngleAxis(angle: pawnAnimator.genitalAngle, axis: Vector3.up), mat: addonGraphic.MatAt(rot: rotation), renderFlags.FlagSet(PawnRenderFlags.DrawNow));
+ }
+
+ else
+ {
+ GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: rotation), loc: vector + (ba.alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f),
+ quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * addonRotation, mat: addonGraphic.MatAt(rot: rotation), renderFlags.FlagSet(PawnRenderFlags.DrawNow));
+ }
+
+ }
+
+
+ }
+
+ return false;
+ }
+ }
+
+
+
+ /*
+
+ [HarmonyPatch(typeof(AlienRace.HarmonyPatches), "DrawAddons")]
+ public static class HarmonyPatch_AlienRace {
+
+ public static void RenderHeadAddonInAnimation(Mesh mesh, Vector3 loc, Quaternion quat, Material mat, bool drawNow, Graphic graphic, AlienPartGenerator.BodyAddon bodyAddon, Vector3 v, Vector3 headOffset, Pawn pawn, PawnRenderFlags renderFlags, Vector3 vector, Rot4 rotation)
+ {
+
+ CompBodyAnimator pawnAnimator = pawn.TryGetComp();
+ AlienPartGenerator.AlienComp comp = pawn.GetComp();
+
+ if (pawnAnimator != null && pawnAnimator.isAnimating)
+ {
+
+ if((bodyAddon.drawnInBed || bodyAddon.alignWithHead))
+ {
+
+ AlienPartGenerator.RotationOffset offset = bodyAddon.defaultOffsets.GetOffset(rotation);
+ Vector3 a = (offset != null) ? offset.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, comp.crownType) : Vector3.zero;
+ AlienPartGenerator.RotationOffset offset2 = bodyAddon.offsets.GetOffset(rotation);
+ Vector3 vector2 = a + ((offset2 != null) ? offset2.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, comp.crownType) : Vector3.zero);
+ vector2.y = (bodyAddon.inFrontOfBody ? (0.3f + vector2.y) : (-0.3f - vector2.y));
+ float num = bodyAddon.angle;
+ if (rotation == Rot4.North)
+ {
+ if (bodyAddon.layerInvert)
+ {
+ vector2.y = -vector2.y;
+ }
+ num = 0f;
+ }
+ if (rotation == Rot4.East)
+ {
+ num = -num;
+ vector2.x = -vector2.x;
+ }
+
+ vector = vector + Quaternion.AngleAxis(pawnAnimator.bodyAngle, Vector3.up) * pawn.Drawer.renderer.BaseHeadOffsetAt(pawnAnimator.bodyFacing) - pawnAnimator.getPawnHeadOffset(); //convert vector into pseudo body pos for head
+ quat = Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up);
+ loc = vector + (bodyAddon.alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(Mathf.Acos(Quaternion.Dot(Quaternion.identity, quat)) * 2f * 57.29578f);
+ mat = graphic.MatAt(rot: pawnAnimator.headFacing);
+ }
+ else
+ {
+
+ AlienPartGenerator.RotationOffset offset = bodyAddon.defaultOffsets.GetOffset(rotation);
+ Vector3 a = (offset != null) ? offset.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, comp.crownType) : Vector3.zero;
+ AlienPartGenerator.RotationOffset offset2 = bodyAddon.offsets.GetOffset(rotation);
+ Vector3 vector2 = a + ((offset2 != null) ? offset2.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, comp.crownType) : Vector3.zero);
+ vector2.y = (bodyAddon.inFrontOfBody ? (0.3f + vector2.y) : (-0.3f - vector2.y));
+ float num = bodyAddon.angle;
+ if (rotation == Rot4.North)
+ {
+ if (bodyAddon.layerInvert)
+ {
+ vector2.y = -vector2.y;
+ }
+ num = 0f;
+ }
+ if (rotation == Rot4.East)
+ {
+ num = -num;
+ vector2.x = -vector2.x;
+ }
+ quat = Quaternion.AngleAxis(pawnAnimator.bodyAngle, Vector3.up);
+ loc = vector + (bodyAddon.alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(Mathf.Acos(Quaternion.Dot(Quaternion.identity, quat)) * 2f * 57.29578f);
+
+ }
+
+ }
+ GenDraw.DrawMeshNowOrLater(mesh, loc, quat, mat, drawNow);
+
+ /*
+ if (pawnAnimator != null && !renderFlags.FlagSet(PawnRenderFlags.Portrait) && pawnAnimator.isAnimating && (bodyAddon.drawnInBed || bodyAddon.alignWithHead))
+ {
+
+
+ if ((pawn.def as ThingDef_AlienRace).defName == "Alien_Orassan")
+ {
+ orassan = true;
+
+ if(bodyAddon.path.Contains("closed"))
+ {
+ return;
+ }
+
+ if (bodyAddon.bodyPart.Contains("ear"))
+
+ {
+ orassan = true;
+
+ orassanv = new Vector3(0, 0, 0.23f);
+ if (pawnAnimator.headFacing == Rot4.North)
+ {
+ orassanv.z -= 0.1f;
+ orassanv.y += 1f;
+
+ if(bodyAddon.bodyPart.Contains("left"))
+ {
+ orassanv.x += 0.03f;
+ } else
+ {
+ orassanv.x -= 0.03f;
+ }
+
+ }
+ else if (pawnAnimator.headFacing == Rot4.East)
+ {
+ orassanv.x -= 0.1f;
+ }
+ else if (pawnAnimator.headFacing == Rot4.West)
+ {
+ orassanv.x = 0.1f;
+ }
+ else
+ {
+ orassanv.z -= 0.1f;
+ orassanv.y += 1f;
+
+ if (bodyAddon.bodyPart.Contains("right"))
+ {
+ orassanv.x += 0.05f;
+ }
+ else
+ {
+ orassanv.x -= 0.05f;
+ }
+ }
+ orassanv = orassanv.RotatedBy(pawnAnimator.headAngle);
+ }
+ }
+
+
+
+
+
+ GenDraw.DrawMeshNowOrLater(mesh: graphic.MeshAt(rot: headRotInAnimation), loc: loc + orassanv + (bodyAddon.alignWithHead ? headOffset : Vector3.zero),// + v.RotatedBy(Mathf.Acos(Quaternion.Dot(Quaternion.identity, quat)) * 2f * 57.29578f),
+ quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * headQuatInAnimation, mat: graphic.MatAt(rot: pawnAnimator.headFacing), drawNow: drawNow);;
+ }
+
+ else
+ {
+
+ }
+
+
+ }
+
+
+ public static IEnumerable Transpiler(IEnumerable instructions)
+ {
+ List ins = instructions.ToList();
+ for (int i = 0; i < ins.Count; i++)
+ {
+
+ Type[] type = new Type[] { typeof(Mesh), typeof(Vector3), typeof(Quaternion), typeof(Material), typeof(bool) };
+
+
+ if (ins[i].OperandIs(AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater", type)))
+ {
+
+ yield return new CodeInstruction(OpCodes.Ldloc, (object)7); //graphic
+ yield return new CodeInstruction(OpCodes.Ldloc, (object)4); //bodyAddon
+ yield return new CodeInstruction(OpCodes.Ldloc, (object)5); //offsetVector/AddonOffset (v)
+ yield return new CodeInstruction(OpCodes.Ldarg, (object)2); //headOffset
+ yield return new CodeInstruction(OpCodes.Ldarg, (object)3); //pawn
+ yield return new CodeInstruction(OpCodes.Ldarg, (object)0); //renderflags
+ yield return new CodeInstruction(OpCodes.Ldarg, (object)1); //vector
+ yield return new CodeInstruction(OpCodes.Ldarg, (object)5); //rotation
+
+ yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(HarmonyPatch_AlienRace), "RenderHeadAddonInAnimation"));
+
+ }
+
+ else
+ {
+ yield return ins[i];
+ }
+ }
+ }
+
+ public static bool Prefix(PawnRenderFlags renderFlags, ref Vector3 vector, ref Vector3 headOffset, Pawn pawn, ref Quaternion quat, ref Rot4 rotation)
+ {
+ if(pawn == null)
+ {
+ return true;
+ }
+
+ CompBodyAnimator anim = pawn.TryGetComp();
+
+ if(anim == null)
+ {
+ return true;
+ }
+
+ if (anim != null && !renderFlags.FlagSet(PawnRenderFlags.Portrait) && anim.isAnimating)
+ {
+ //quat = Quaternion.AngleAxis(anim.bodyAngle, Vector3.up);
+ }
+
+ return true;
+
+ }
+ }
+
+
+ */
+
+}
+
+
diff --git a/Patch_HumanoidAlienRaces/1.5/Assemblies/Patch_HumanoidAlienRaces.dll b/Patch_HumanoidAlienRaces/1.5/Assemblies/Patch_HumanoidAlienRaces.dll
new file mode 100644
index 0000000..5673f48
Binary files /dev/null and b/Patch_HumanoidAlienRaces/1.5/Assemblies/Patch_HumanoidAlienRaces.dll differ
diff --git a/Patch_HumanoidAlienRaces/Patch_HumanoidAlienRaces.csproj b/Patch_HumanoidAlienRaces/Patch_HumanoidAlienRaces.csproj
index e34e96e..797d0a4 100644
--- a/Patch_HumanoidAlienRaces/Patch_HumanoidAlienRaces.csproj
+++ b/Patch_HumanoidAlienRaces/Patch_HumanoidAlienRaces.csproj
@@ -17,7 +17,7 @@
false
none
false
- 1.4\Assemblies\
+ 1.5\Assemblies\
DEBUG;TRACE
prompt
4
@@ -71,7 +71,7 @@
-
+
\ No newline at end of file
diff --git a/Patch_SexToysMasturbation/1.4/Source/Defs/SexToyAnimationDef.cs b/Patch_SexToysMasturbation/1.4/Source/Defs/SexToyAnimationDef.cs
new file mode 100644
index 0000000..a572d5e
--- /dev/null
+++ b/Patch_SexToysMasturbation/1.4/Source/Defs/SexToyAnimationDef.cs
@@ -0,0 +1,16 @@
+using Rimworld_Animations;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Patch_SexToysMasturbation
+{
+ public class SexToyAnimationDef : AnimationDef
+ {
+
+ public String requiredBodyPart = null;
+
+ }
+}
diff --git a/Patch_SexToysMasturbation/1.4/Source/Harmony/Harmony_PatchAll.cs b/Patch_SexToysMasturbation/1.4/Source/Harmony/Harmony_PatchAll.cs
new file mode 100644
index 0000000..b48ece7
--- /dev/null
+++ b/Patch_SexToysMasturbation/1.4/Source/Harmony/Harmony_PatchAll.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+using HarmonyLib;
+using System.Reflection;
+
+namespace Patch_SexToysMasturbation
+{
+
+ [StaticConstructorOnStartup]
+ public static class Harmony_PatchAll {
+
+ static Harmony_PatchAll() {
+
+ Harmony val = new Harmony("animtoyspatch");
+ val.PatchAll(Assembly.GetExecutingAssembly());
+
+ }
+ }
+}
diff --git a/Patch_SexToysMasturbation/1.4/Source/Patches/HarmonyPatch_JobDriver_SexBaseInitiator.cs b/Patch_SexToysMasturbation/1.4/Source/Patches/HarmonyPatch_JobDriver_SexBaseInitiator.cs
new file mode 100644
index 0000000..9f29203
--- /dev/null
+++ b/Patch_SexToysMasturbation/1.4/Source/Patches/HarmonyPatch_JobDriver_SexBaseInitiator.cs
@@ -0,0 +1,76 @@
+using HarmonyLib;
+using Rimworld_Animations;
+using rjw;
+using RJW_ToysAndMasturbation;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+
+namespace Patch_SexToysMasturbation
+{
+ [HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "Start")]
+ public class HarmonyPatch_JobDriver_SexBaseInitiator
+ {
+
+ public static void Postfix(ref JobDriver_SexBaseInitiator __instance)
+ {
+
+ if(__instance is JobDriver_MasturbateWithToy masturbateJobDriver)
+ {
+
+ Log.Message("Rerolling animations...");
+ Pawn pawn = masturbateJobDriver.pawn;
+ Thing sexToy = masturbateJobDriver.dildo;
+
+ RerollAnimationsForSexToy(pawn, sexToy, masturbateJobDriver.Bed);
+ }
+
+
+ }
+
+ public static void RerollAnimationsForSexToy(Pawn pawn, Thing thing, Thing bed)
+ {
+ CompSexToy sextoy = thing.TryGetComp();
+
+ SexToyAnimationDef anim = AnimSexToyUtility.tryFindAnimation(sextoy, pawn);
+
+ if (anim != null)
+ {
+ Log.Message("Playing anim " + anim.defName);
+
+ if(bed != null)
+ {
+ pawn.TryGetComp().setAnchor(bed);
+ thing.TryGetComp().setAnchor(bed);
+ }
+ else
+ {
+ pawn.TryGetComp().setAnchor(pawn.Position);
+ thing.TryGetComp().setAnchor(pawn.Position);
+ }
+
+ bool mirror = GenTicks.TicksGame % 2 == 0;
+
+ pawn.TryGetComp().StartAnimation(anim, new List { pawn }, 0, mirror);
+ thing.TryGetComp().StartAnimation(anim, pawn, mirror);
+
+ (pawn.jobs.curDriver as JobDriver_Sex).ticks_left = anim.animationTimeTicks;
+ (pawn.jobs.curDriver as JobDriver_Sex).sex_ticks = anim.animationTimeTicks;
+ (pawn.jobs.curDriver as JobDriver_Sex).orgasmStartTick = anim.animationTimeTicks;
+ (pawn.jobs.curDriver as JobDriver_Sex).duration = anim.animationTimeTicks;
+ }
+ else
+ {
+ Log.Message("No animation found");
+ }
+
+
+ }
+
+ }
+
+
+}
diff --git a/Patch_SexToysMasturbation/1.4/Source/Utilities/AnimSexToyUtility.cs b/Patch_SexToysMasturbation/1.4/Source/Utilities/AnimSexToyUtility.cs
new file mode 100644
index 0000000..5ab05b1
--- /dev/null
+++ b/Patch_SexToysMasturbation/1.4/Source/Utilities/AnimSexToyUtility.cs
@@ -0,0 +1,47 @@
+using rjw;
+using RJW_ToysAndMasturbation;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+
+namespace Patch_SexToysMasturbation
+{
+ public class AnimSexToyUtility
+ {
+
+ public static SexToyAnimationDef tryFindAnimation(CompSexToy sexToy, Pawn pawn)
+ {
+
+ IEnumerable options = DefDatabase.AllDefs.Where((SexToyAnimationDef x) =>
+ {
+
+ if(!sexToy.Props.requiredBodyParts.Contains(x.requiredBodyPart))
+ {
+ return false;
+ }
+
+ if(x.requiredBodyPart == "vagina" && !Genital_Helper.has_vagina(pawn))
+ {
+ return false;
+ }
+
+ return true;
+
+ });
+
+ if(options != null && options.Any())
+ {
+ return options.RandomElement();
+ }
+ else
+ {
+ return null;
+ }
+
+ }
+
+ }
+}
diff --git a/Patch_SexToysMasturbation/1.5/Assemblies/Patch_SexToysMasturbation.dll b/Patch_SexToysMasturbation/1.5/Assemblies/Patch_SexToysMasturbation.dll
new file mode 100644
index 0000000..858a7e1
Binary files /dev/null and b/Patch_SexToysMasturbation/1.5/Assemblies/Patch_SexToysMasturbation.dll differ
diff --git a/Patch_SexToysMasturbation/1.5/Source/Defs/SexToyAnimationDef.cs b/Patch_SexToysMasturbation/1.5/Source/Defs/SexToyAnimationDef.cs
new file mode 100644
index 0000000..a572d5e
--- /dev/null
+++ b/Patch_SexToysMasturbation/1.5/Source/Defs/SexToyAnimationDef.cs
@@ -0,0 +1,16 @@
+using Rimworld_Animations;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Patch_SexToysMasturbation
+{
+ public class SexToyAnimationDef : AnimationDef
+ {
+
+ public String requiredBodyPart = null;
+
+ }
+}
diff --git a/Patch_SexToysMasturbation/1.5/Source/Harmony/Harmony_PatchAll.cs b/Patch_SexToysMasturbation/1.5/Source/Harmony/Harmony_PatchAll.cs
new file mode 100644
index 0000000..b48ece7
--- /dev/null
+++ b/Patch_SexToysMasturbation/1.5/Source/Harmony/Harmony_PatchAll.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+using HarmonyLib;
+using System.Reflection;
+
+namespace Patch_SexToysMasturbation
+{
+
+ [StaticConstructorOnStartup]
+ public static class Harmony_PatchAll {
+
+ static Harmony_PatchAll() {
+
+ Harmony val = new Harmony("animtoyspatch");
+ val.PatchAll(Assembly.GetExecutingAssembly());
+
+ }
+ }
+}
diff --git a/Patch_SexToysMasturbation/1.5/Source/Patches/HarmonyPatch_JobDriver_SexBaseInitiator.cs b/Patch_SexToysMasturbation/1.5/Source/Patches/HarmonyPatch_JobDriver_SexBaseInitiator.cs
new file mode 100644
index 0000000..d38437d
--- /dev/null
+++ b/Patch_SexToysMasturbation/1.5/Source/Patches/HarmonyPatch_JobDriver_SexBaseInitiator.cs
@@ -0,0 +1,42 @@
+using HarmonyLib;
+using Rimworld_Animations;
+using rjw;
+using RJW_ToysAndMasturbation;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+
+namespace Patch_SexToysMasturbation
+{
+ [HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "Start")]
+ public class HarmonyPatch_JobDriver_SexBaseInitiator
+ {
+
+ public static void Postfix(ref JobDriver_SexBaseInitiator __instance)
+ {
+
+ if(__instance is JobDriver_MasturbateWithToy masturbateJobDriver)
+ {
+
+ Log.Message("Rerolling animations...");
+ Pawn pawn = masturbateJobDriver.pawn;
+ Thing sexToy = masturbateJobDriver.dildo;
+
+ RerollAnimationsForSexToy(pawn, sexToy, masturbateJobDriver.Bed);
+ }
+
+
+ }
+
+ public static void RerollAnimationsForSexToy(Pawn pawn, Thing thing, Thing bed)
+ {
+ //play anim for sex toy
+ }
+
+ }
+
+
+}
diff --git a/Patch_SexToysMasturbation/1.5/Source/Utilities/AnimSexToyUtility.cs b/Patch_SexToysMasturbation/1.5/Source/Utilities/AnimSexToyUtility.cs
new file mode 100644
index 0000000..f98ffe7
--- /dev/null
+++ b/Patch_SexToysMasturbation/1.5/Source/Utilities/AnimSexToyUtility.cs
@@ -0,0 +1,22 @@
+using rjw;
+using RJW_ToysAndMasturbation;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+
+namespace Patch_SexToysMasturbation
+{
+ public class AnimSexToyUtility
+ {
+
+ public static SexToyAnimationDef tryFindAnimation(CompSexToy sexToy, Pawn pawn)
+ {
+ return null;
+ //todo
+ }
+
+ }
+}
diff --git a/Patch_SexToysMasturbation/Patch_SexToysMasturbation.csproj b/Patch_SexToysMasturbation/Patch_SexToysMasturbation.csproj
index d274c6a..2eda7e4 100644
--- a/Patch_SexToysMasturbation/Patch_SexToysMasturbation.csproj
+++ b/Patch_SexToysMasturbation/Patch_SexToysMasturbation.csproj
@@ -18,7 +18,7 @@
false
none
false
- 1.4\Assemblies\
+ 1.5\Assemblies\
DEBUG;TRACE
prompt
4
@@ -72,18 +72,15 @@
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/Rimworld-Animations.csproj b/Rimworld-Animations.csproj
index 657ccf2..1f5a8fb 100644
--- a/Rimworld-Animations.csproj
+++ b/Rimworld-Animations.csproj
@@ -18,7 +18,7 @@
false
none
false
- 1.4\Assemblies\
+ 1.5\Assemblies\
DEBUG;TRACE
prompt
4
@@ -45,8 +45,7 @@
False
- ..\rjw\1.4\Assemblies\RJW.dll
- False
+ ..\rjw\1.5\Assemblies\RJW.dll
..\rjw-toys-and-masturbation\Assemblies\RJW-ToysAndMasturbation.dll
@@ -70,100 +69,60 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -175,10 +134,11 @@
-
-
-
-
+
+
+
+
+
\ No newline at end of file