diff --git a/About/About.xml b/About/About.xml
new file mode 100644
index 0000000..12628e6
--- /dev/null
+++ b/About/About.xml
@@ -0,0 +1,12 @@
+
+
+
+ RimJobWorld - Sex Toys and Masturbation
+ c0ffee.SexToysMasturbation
+ c0ffee
+
+ 1.1
+ 1.2
+
+ Sex toys for use with RJW.
+
\ No newline at end of file
diff --git a/Assemblies/RJW-ToysAndMasturbation.dll b/Assemblies/RJW-ToysAndMasturbation.dll
new file mode 100644
index 0000000..17a7d54
Binary files /dev/null and b/Assemblies/RJW-ToysAndMasturbation.dll differ
diff --git a/Defs/JobDefs/Jobs_MasturbateToy.xml b/Defs/JobDefs/Jobs_MasturbateToy.xml
new file mode 100644
index 0000000..c4c3306
--- /dev/null
+++ b/Defs/JobDefs/Jobs_MasturbateToy.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ MasturbateWithToy
+ RJW_ToysAndMasturbation.JobDriver_MasturbateWithToy
+ Masturbatin' with toy.
+ false
+
+
+
+
diff --git a/Defs/ThingDefs/SexToys.xml b/Defs/ThingDefs/SexToys.xml
new file mode 100644
index 0000000..4711b19
--- /dev/null
+++ b/Defs/ThingDefs/SexToys.xml
@@ -0,0 +1,85 @@
+
+
+
+ ThingWithComps
+
+
+
+ CompQuality
+
+
+
+ true
+ 0.0
+ Item
+
+ Manufactured
+
+ Industrial
+ true
+ 8
+ Item
+ Never
+
+ Exotic
+
+ Sellable
+ true
+
+
+
+
+ SexToysDildo
+
+ A simple dildo for masturbation.
+
+
+ Things/SexToys/Dildo
+ CutoutComplex
+ Graphic_Single
+
+
+
+ 1200
+ 20
+ 0
+ 1
+ 85
+ 0
+ 0.2
+
+
+
+ Metallic
+ Woody
+ Stony
+
+
+ 5
+ true
+
+
+ GeneralLaborSpeed
+ Crafting
+ Smith
+ Recipe_Smith
+
+ ElectricSmithy
+ FueledSmithy
+
+ UnfinishedWeapon
+
+
+ Root
+
+
+ Uranium
+ Gold
+ Silver
+ Jade
+ Plasteel
+
+
+
+
+
\ No newline at end of file
diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..b9131ed
--- /dev/null
+++ b/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("RJW-ToysAndMasturbation")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("RJW-ToysAndMasturbation")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("a75640f8-d269-44ca-b27c-b8861f2da2dc")]
+
+// 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/RJW-ToysAndMasturbation.csproj b/RJW-ToysAndMasturbation.csproj
new file mode 100644
index 0000000..1f7b602
--- /dev/null
+++ b/RJW-ToysAndMasturbation.csproj
@@ -0,0 +1,81 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {A75640F8-D269-44CA-B27C-B8861F2DA2DC}
+ Library
+ Properties
+ RJW_ToysAndMasturbation
+ RJW-ToysAndMasturbation
+ v4.7.2
+ 512
+ true
+
+
+ false
+ none
+ false
+ Assemblies\
+ DEBUG;TRACE
+ prompt
+ 4
+ Auto
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\..\..\..\workshop\content\294100\2009463077\Current\Assemblies\0Harmony.dll
+ False
+
+
+ ..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll
+ False
+
+
+ ..\RJW\1.1\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/RJW-ToysAndMasturbation.sln b/RJW-ToysAndMasturbation.sln
new file mode 100644
index 0000000..e4db0df
--- /dev/null
+++ b/RJW-ToysAndMasturbation.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29905.134
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RJW-ToysAndMasturbation", "RJW-ToysAndMasturbation.csproj", "{A75640F8-D269-44CA-B27C-B8861F2DA2DC}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A75640F8-D269-44CA-B27C-B8861F2DA2DC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A75640F8-D269-44CA-B27C-B8861F2DA2DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A75640F8-D269-44CA-B27C-B8861F2DA2DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A75640F8-D269-44CA-B27C-B8861F2DA2DC}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {08CD1E1E-A226-4A0F-9469-52A3C67FEAED}
+ EndGlobalSection
+EndGlobal
diff --git a/Source/DefOfs/MasturbateToyDefOf.cs b/Source/DefOfs/MasturbateToyDefOf.cs
new file mode 100644
index 0000000..dfca664
--- /dev/null
+++ b/Source/DefOfs/MasturbateToyDefOf.cs
@@ -0,0 +1,21 @@
+using RimWorld;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+
+namespace RJW_ToysAndMasturbation {
+
+ [DefOf]
+ public static class MasturbateToyDefOf {
+
+ public static JobDef MasturbateWithToy;
+
+ static MasturbateToyDefOf() {
+ DefOfHelper.EnsureInitializedInCtor(typeof(JobDefOf));
+ }
+
+ }
+}
diff --git a/Source/JobDrivers/JobDriver_MasturbateWithToy.cs b/Source/JobDrivers/JobDriver_MasturbateWithToy.cs
new file mode 100644
index 0000000..66d2523
--- /dev/null
+++ b/Source/JobDrivers/JobDriver_MasturbateWithToy.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+using RimWorld;
+using rjw;
+using Verse.AI;
+
+namespace RJW_ToysAndMasturbation {
+ class JobDriver_MasturbateWithToy : JobDriver_SexBaseInitiator {
+
+ public IntVec3 cell => (IntVec3)job.GetTarget(iCell);
+ public override bool TryMakePreToilReservations(bool errorOnFailed) {
+ return ReservationUtility.Reserve(pawn, job.targetA, job, stackCount: 1, errorOnFailed: false);
+ }
+
+ public new void setup_ticks() {
+ base.setup_ticks();
+ duration = (int)(xxx.is_frustrated(base.pawn) ? (2500f * Rand.Range(0.2f, 0.7f)) : (2500f * Rand.Range(0.2f, 0.4f)));
+ }
+
+ protected override IEnumerable MakeNewToils() {
+ setup_ticks();
+ ToilFailConditions.FailOn(this, () => pawn.Downed);
+ ToilFailConditions.FailOn(this, () => pawn.IsBurning());
+ ToilFailConditions.FailOn(this, () => pawn.IsFighting());
+ ToilFailConditions.FailOn(this, () => pawn.Drafted);
+
+
+ yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.ClosestTouch).FailOnDespawnedNullOrForbidden(TargetIndex.A);
+ yield return Toils_Haul.StartCarryThing(TargetIndex.A);
+
+ if (!SexToyUtility.isRJWAnimationsLoaded || true /*True for now, anims come later; disable once created animations*/) {
+ //place down if anims isn't loaded
+ yield return Toils_Haul.CarryHauledThingToCell(TargetIndex.C);
+ yield return Toils_Haul.PlaceHauledThingInCell(TargetIndex.C, Toils_Goto.GotoCell(TargetIndex.C, PathEndMode.OnCell), storageMode: false);
+ }
+
+ yield return Toils_Goto.GotoCell(TargetIndex.C, PathEndMode.OnCell);
+
+
+ Toil masturbationToil = Toils_General.Wait(duration);
+ masturbationToil.handlingFacing = true;
+ masturbationToil.defaultCompleteMode = ToilCompleteMode.Never;
+ masturbationToil.initAction = delegate { Start(); };
+ masturbationToil.tickAction = delegate {
+ duration--;
+ if (Gen.IsHashIntervalTick(pawn, ticks_between_hearts)) {
+ ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
+ }
+ SexTick(pawn, null);
+ SexUtility.reduce_rest(pawn);
+ if (duration <= 0) ReadyForNextToil();
+ };
+ masturbationToil.AddFinishAction(delegate { End(); });
+ yield return masturbationToil;
+
+ Toil AfterToil = new Toil();
+ AfterToil.initAction = delegate {
+ SexUtility.Aftersex(pawn);
+ if(SexUtility.ConsiderCleaning(pawn)) {
+ LocalTargetInfo filth = GridsUtility.GetFirstThing(pawn.PositionHeld, pawn.Map);
+ Job cleanup = JobMaker.MakeJob(JobDefOf.Clean);
+ cleanup.AddQueuedTarget(TargetIndex.A, filth);
+ pawn.jobs.jobQueue.EnqueueFirst(cleanup);
+ }
+
+ };
+ yield return AfterToil;
+
+
+ }
+
+ }
+}
diff --git a/Source/JobGivers/JobGiver_MasturbateWithToy.cs b/Source/JobGivers/JobGiver_MasturbateWithToy.cs
new file mode 100644
index 0000000..57a6856
--- /dev/null
+++ b/Source/JobGivers/JobGiver_MasturbateWithToy.cs
@@ -0,0 +1,40 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using RimWorld;
+using rjw;
+using Verse;
+using Verse.AI;
+
+namespace RJW_ToysAndMasturbation {
+ class JobGiver_MasturbateWithToy : ThinkNode_JobGiver {
+
+ public static IntVec3 FapLocation(Pawn p) => (new JobGiver_Masturbate()).FindFapLocation(p);
+
+ protected override Job TryGiveJob(Pawn pawn) {
+
+ if (pawn.Drafted) {
+ return null;
+ }
+ if (!xxx.can_be_fucked(pawn) && !xxx.can_fuck(pawn)) {
+ return null;
+ }
+ if ((SexUtility.ReadyForLovin(pawn) && (!xxx.is_whore(pawn) || pawn.IsPrisoner || xxx.is_slave(pawn))) || xxx.is_frustrated(pawn)) {
+ if (RJWPreferenceSettings.FapInBed && pawn.jobs.curDriver is JobDriver_LayDown) {
+ Building_Bed bed = ((JobDriver_LayDown)pawn.jobs.curDriver).Bed;
+ if (bed != null) {
+ return JobMaker.MakeJob(xxx.Masturbate, null, bed, bed.Position);
+ }
+ }
+ else if (RJWPreferenceSettings.FapEverywhere && (xxx.is_frustrated(pawn) || xxx.has_quirk(pawn, "Exhibitionist"))) {
+ return JobMaker.MakeJob(xxx.Masturbate, null, null, FapLocation(pawn));
+ }
+ }
+ return null;
+
+ }
+
+ }
+}
diff --git a/Source/ThingComps/CompProperties_SexToy.cs b/Source/ThingComps/CompProperties_SexToy.cs
new file mode 100644
index 0000000..3dc7f3c
--- /dev/null
+++ b/Source/ThingComps/CompProperties_SexToy.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using RimWorld;
+using Verse;
+
+namespace RJW_ToysAndMasturbation {
+ public class CompProperties_SexToy : CompProperties {
+
+ public CompProperties_SexToy() {
+ compClass = typeof(CompSexToy);
+ }
+
+ }
+}
diff --git a/Source/ThingComps/CompSexToy.cs b/Source/ThingComps/CompSexToy.cs
new file mode 100644
index 0000000..3fb58d1
--- /dev/null
+++ b/Source/ThingComps/CompSexToy.cs
@@ -0,0 +1,59 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+using RimWorld;
+using Verse.AI;
+using rjw;
+
+namespace RJW_ToysAndMasturbation {
+ public class CompSexToy : ThingComp {
+
+ public static IntVec3 FapLocation(Pawn p) => (new JobGiver_Masturbate()).FindFapLocation(p);
+
+ public override IEnumerable CompFloatMenuOptions(Pawn pawn) {
+
+ if (!pawn.CanReach(parent, PathEndMode.Touch, Danger.Deadly)) {
+ yield return new FloatMenuOption(FloatMenuOptionLabel(pawn) + " (" + "NoPath".Translate() + ")", null);
+ }
+ else if (!pawn.CanReserve(parent)) {
+ yield return new FloatMenuOption(FloatMenuOptionLabel(pawn) + " (" + "Reserved".Translate() + ")", null);
+ }
+ else if (!pawn.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation)) {
+ yield return new FloatMenuOption(FloatMenuOptionLabel(pawn) + " (" + "Incapable".Translate() + ")", null);
+ }
+ else if (!xxx.can_be_fucked(pawn) && !xxx.can_fuck(pawn)) {
+ yield return new FloatMenuOption(FloatMenuOptionLabel(pawn) + " (" + "Incapable".Translate() + ")", null);
+ }
+
+ else {
+
+ yield return new FloatMenuOption(FloatMenuOptionLabel(pawn), delegate {
+
+ if (RJWPreferenceSettings.FapInBed && pawn.jobs.curDriver is JobDriver_LayDown) {
+ Building_Bed bed = ((JobDriver_LayDown)pawn.jobs.curDriver).Bed;
+ if (bed != null) {
+ Job j = JobMaker.MakeJob(MasturbateToyDefOf.MasturbateWithToy, parent, bed, bed.Position);
+ j.count = 1;
+ pawn.jobs.TryTakeOrderedJob(j);
+ }
+ }
+ else {
+ Job j = JobMaker.MakeJob(MasturbateToyDefOf.MasturbateWithToy, parent, null, FapLocation(pawn));
+ j.count = 1;
+ pawn.jobs.TryTakeOrderedJob(j);
+ }
+ });
+
+
+ }
+
+ }
+
+ private string FloatMenuOptionLabel(Pawn pawn) {
+ return "Use sex toy";
+ }
+ }
+}
diff --git a/Source/Utils/SexToyUtility.cs b/Source/Utils/SexToyUtility.cs
new file mode 100644
index 0000000..c7bf323
--- /dev/null
+++ b/Source/Utils/SexToyUtility.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Verse;
+
+namespace RJW_ToysAndMasturbation {
+
+ [StaticConstructorOnStartup]
+ public static class SexToyUtility {
+
+ public static bool isRJWAnimationsLoaded = false;
+
+ static SexToyUtility() {
+ if(LoadedModManager.RunningMods.Any((ModContentPack x) => x.Name == "Rimworld-Animations")) {
+ isRJWAnimationsLoaded = true;
+ }
+ }
+
+ }
+}
diff --git a/Textures/Things/SexToys/Dildo.png b/Textures/Things/SexToys/Dildo.png
new file mode 100644
index 0000000..2a5ce54
Binary files /dev/null and b/Textures/Things/SexToys/Dildo.png differ
diff --git a/Textures/Things/SexToys/Dildo_m.png b/Textures/Things/SexToys/Dildo_m.png
new file mode 100644
index 0000000..7fac6c5
Binary files /dev/null and b/Textures/Things/SexToys/Dildo_m.png differ
diff --git a/Textures/Things/SexToys/dildo2.mdp b/Textures/Things/SexToys/dildo2.mdp
new file mode 100644
index 0000000..98442a0
Binary files /dev/null and b/Textures/Things/SexToys/dildo2.mdp differ
diff --git a/Textures/Things/SexToys/dildo_M.mdp b/Textures/Things/SexToys/dildo_M.mdp
new file mode 100644
index 0000000..804d610
Binary files /dev/null and b/Textures/Things/SexToys/dildo_M.mdp differ