diff --git a/1.3/Source/Mod/RimJobWorldCum.csproj b/1.3/Source/Mod/RimJobWorldCum.csproj
index 1e94d1f..148b9d0 100644
--- a/1.3/Source/Mod/RimJobWorldCum.csproj
+++ b/1.3/Source/Mod/RimJobWorldCum.csproj
@@ -40,7 +40,7 @@
False
- ..\..\..\..\..\..\..\workshop\content\294100\818773962\v1.2\Assemblies\HugsLib.dll
+ ..\..\..\..\..\..\..\workshop\content\294100\818773962\v1.3\Assemblies\HugsLib.dll
False
diff --git a/1.4/Assemblies/RimJobWorldCum.dll b/1.4/Assemblies/RimJobWorldCum.dll
new file mode 100644
index 0000000..dbec018
Binary files /dev/null and b/1.4/Assemblies/RimJobWorldCum.dll differ
diff --git a/1.4/Defs/HediffsDef/Hediff_CumController.xml b/1.4/Defs/HediffsDef/Hediff_CumController.xml
new file mode 100644
index 0000000..854f8e0
--- /dev/null
+++ b/1.4/Defs/HediffsDef/Hediff_CumController.xml
@@ -0,0 +1,49 @@
+
+
+
+
+ Hediff_CumController
+ rjwcum.Hediff_CumController
+ Cum
+ Covered in cum.
+ false
+ 0.01
+ 1
+
+
+
+
+ false
+ false
+
+
+ minor
+ false
+
+
+ 0.3
+ little
+
+ 0.2
+ -0.1
+
+
+
+ 0.6
+ extensive
+
+ 0.3
+ -0.3
+
+
+
+ 0.8
+ full
+
+ -0.1
+ -0.5
+
+
+
+
+
\ No newline at end of file
diff --git a/1.4/Defs/HediffsDef/Hediffs_Cum.xml b/1.4/Defs/HediffsDef/Hediffs_Cum.xml
new file mode 100644
index 0000000..3a77e61
--- /dev/null
+++ b/1.4/Defs/HediffsDef/Hediffs_Cum.xml
@@ -0,0 +1,72 @@
+
+
+
+ rjwcum.Hediff_Cum
+ Hediff_Cum
+ Cum
+ cum
+ cum.
+ cum on {1}
+ (0.95,0.95,0.95)
+ false
+ false
+ false
+ false
+ 1
+ 0.001
+
+ true
+
+
+
+ little
+
+
+ 0.25
+ some
+
+
+ 0.5
+ dripping
+
+
+ 0.8
+ drenched
+
+
+
+
+
+ 1800
+ 0.01
+
+
+
+
+
+ Hediff_Cum
+ Cum
+ cum
+ cum
+ cum on {1}
+ (0.95,0.95,0.95)
+
+
+
+ Hediff_InsectSpunk
+ Insect spunk.
+ insect spunk
+ insect spunk
+ insect spunk on {1}
+ (0.6,0.83,0.35)
+
+
+
+ Hediff_MechaFluids
+ Mechanoid fluids.
+ mechanoid fluids
+ mechanoid fluids
+ mecha fluids on {1}
+ (0.37,0.71,0.82)
+
+
\ No newline at end of file
diff --git a/1.4/Defs/JobDefs/Jobs_CleanSelf.xml b/1.4/Defs/JobDefs/Jobs_CleanSelf.xml
new file mode 100644
index 0000000..12ae606
--- /dev/null
+++ b/1.4/Defs/JobDefs/Jobs_CleanSelf.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ CleanSelf
+ rjwcum.JobDriver_CleanSelf
+ cleaning self
+ true
+
+
\ No newline at end of file
diff --git a/1.4/Defs/WorkGiverDefs/WorkGivers_CleanSelf.xml b/1.4/Defs/WorkGiverDefs/WorkGivers_CleanSelf.xml
new file mode 100644
index 0000000..62b7637
--- /dev/null
+++ b/1.4/Defs/WorkGiverDefs/WorkGivers_CleanSelf.xml
@@ -0,0 +1,16 @@
+
+
+
+ CleanSelf
+ clean self
+ rjwcum.WorkGiver_CleanSelf
+ Cleaning
+ clean self
+ cleaning self
+ false
+ 11
+
+ Manipulation
+
+
+
\ No newline at end of file
diff --git a/1.4/Languages/English/Keyed/Cum.xml b/1.4/Languages/English/Keyed/Cum.xml
new file mode 100644
index 0000000..54ce568
--- /dev/null
+++ b/1.4/Languages/English/Keyed/Cum.xml
@@ -0,0 +1,15 @@
+
+
+ Debug
+
+ Enable cum on body (Hediffs)
+ Enables cum hediffs in health tab.
+ Adjust cum amount on body
+ All cum applied to pawn bodies will be multiplied by this amount.
+ Enable cum overlays (Visuals)
+ Enables cum overlay for pawn drawer(may have conflicts with mods that change pawn appearance and other issues).\nRequires Cum on body option enabled.
+ Hero manual CleanSelf
+ Hero pawn will only clean cum from body if manually told so.
+ DubsBadHygiene block CleanSelf
+ If DubsBadHygiene installed, pawns will only clean cum from body in buckets, showers, baths, hottubs etc.
+
\ No newline at end of file
diff --git a/1.4/Patches/FacialAnimation_compatibility.xml b/1.4/Patches/FacialAnimation_compatibility.xml
new file mode 100644
index 0000000..7a6b70e
--- /dev/null
+++ b/1.4/Patches/FacialAnimation_compatibility.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ [NL] Facial Animation - WIP
+
+
+ Always
+
+
+ /Defs/FacialAnimation.FaceAnimationDef[defName="Wear" or defName="Wear2" or defName="Wear3"]/targetJobs
+ Always
+
+ CleanSelf
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1.4/Source/Mod/CumBase.cs b/1.4/Source/Mod/CumBase.cs
new file mode 100644
index 0000000..294e079
--- /dev/null
+++ b/1.4/Source/Mod/CumBase.cs
@@ -0,0 +1,53 @@
+using System;
+using HugsLib;
+using HugsLib.Settings;
+using Verse;
+
+namespace rjwcum
+{
+ public class CumBase : ModBase
+ {
+ public override string ModIdentifier
+ {
+ get
+ {
+ return "RJW_Cum";
+ }
+ }
+
+ public static SettingHandle debug;
+ public static SettingHandle cum_on_body;
+ public static SettingHandle cum_overlays;
+ public static SettingHandle cum_on_body_amount;
+ public static SettingHandle manual_hero_CleanSelf;
+ public static SettingHandle dubsDBH_block_CleanSelf;
+
+ public override void DefsLoaded()
+ {
+ debug = Settings.GetHandle("debug",
+ Translator.Translate("debug"),
+ Translator.Translate("debug_desc"),
+ false);
+ cum_on_body = Settings.GetHandle("cum_on_body",
+ Translator.Translate("cum_on_body"),
+ Translator.Translate("cum_on_body_desc"),
+ true);
+ cum_overlays = Settings.GetHandle("cum_overlays",
+ Translator.Translate("cum_overlays"),
+ Translator.Translate("cum_overlays_desc"),
+ true);
+ cum_on_body_amount = Settings.GetHandle("cum_on_body_amount",
+ Translator.Translate("cum_on_body_amount"),
+ Translator.Translate("cum_on_body_amount_desc"),
+ 1.0f);
+ manual_hero_CleanSelf = Settings.GetHandle("manual_hero_CleanSelf",
+ Translator.Translate("manual_hero_CleanSelf"),
+ Translator.Translate("manual_hero_CleanSelf_desc"),
+ true);
+ dubsDBH_block_CleanSelf = Settings.GetHandle("dubsDBH_block_CleanSelf",
+ Translator.Translate("dubsDBH_block_CleanSelf"),
+ Translator.Translate("dubsDBH_block_CleanSelf_desc"),
+ true);
+ }
+ }
+}
diff --git a/1.4/Source/Mod/CumHelper.cs b/1.4/Source/Mod/CumHelper.cs
new file mode 100644
index 0000000..9de0f69
--- /dev/null
+++ b/1.4/Source/Mod/CumHelper.cs
@@ -0,0 +1,620 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using RimWorld;
+using Verse;
+using HarmonyLib;
+using UnityEngine;
+//using Multiplayer.API;
+using rjw;
+
+namespace rjwcum
+{
+ [StaticConstructorOnStartup]
+ public static class CumHelper
+ {
+ /*
+ contains many important functions of use to the other classes
+ */
+
+ //amount of cum per sex act:
+
+ public static readonly Dictionary splatchAdjust;//saves x (horizontal) and z (vertical) adjustments of texture positiion for each unique combination of bodyType and bodyPart
+ public static readonly Dictionary layerAdjust;//saves y adjustments (drawing plane) for left/right appendages + bodyPart combinations to hide spunk if pawn looks in the wrong direction
+
+ //structs are used to pack related variables together - used as keys for the dictionaries
+ public struct key//allows to save all unique combinations of bodyType and bodyPart
+ {
+ public readonly BodyTypeDef bodyType;
+ public readonly BodyPartDef bodyPart;
+ public key(BodyTypeDef bodyType, BodyPartDef bodyPart)
+ {
+ this.bodyType = bodyType;
+ this.bodyPart = bodyPart;
+ }
+ }
+
+ //for the 4 directions, use arrays to store the different adjust for north, east, south, west (in that order)
+ public struct values
+ {
+ public readonly float[] x;
+ public readonly float[] z;
+ //public readonly bool over_clothing;//on gentials: hide when clothes are worn - in case of the other body parts it can't be said (for now) if it was added on the clothing or not
+ public values(float[] xAdjust, float[] zAdjust)
+ {
+ x = xAdjust;
+ z = zAdjust;
+ //this.over_clothing = over_clothing;
+ }
+ }
+
+
+ public struct key_layer//used to save left/right appendage + bodyPart combinations
+ {
+ public readonly bool left_side;
+ public readonly BodyPartDef bodyPart;
+
+ public key_layer(bool left_side, BodyPartDef bodyPart)
+ {
+ this.left_side = left_side;
+ this.bodyPart = bodyPart;
+ }
+ }
+
+ public struct values_layer//saves the y-adjustments for different body parts and sides -> e.g. allows hiding spunk on the right arm if pawn is looking to the left (aka west)
+ {
+ public readonly float[] y;
+
+ public values_layer(float[] yAdjust)
+ {
+ y = yAdjust;
+ }
+ }
+
+ //get defs of the rjw parts
+ public static BodyPartDef genitalsDef = BodyDefOf.Human.AllParts.Find(bpr => string.Equals(bpr.def.defName, "Genitals")).def;
+ public static BodyPartDef anusDef = BodyDefOf.Human.AllParts.Find(bpr => string.Equals(bpr.def.defName, "Anus")).def;
+ public static BodyPartDef chestDef = BodyDefOf.Human.AllParts.Find(bpr => string.Equals(bpr.def.defName, "Chest")).def;
+
+
+ static CumHelper()
+ {
+ splatchAdjust = new Dictionary();
+ //maybe there is a more elegant way to save and load this data, but I don't know about it
+
+ //structure explained:
+ //1) key: struct consisting of bodyType + bodyPart (= unique key for every combination of bodyType + part)
+ //2) values: struct containing positioning information (xAdjust: horizontal positioning, yAdjust: vertical positioning, zAdjust: whether to draw above or below pawn
+ //note: arms, hands, and legs (which are only visible from one direction) values need not be inverted between west and east
+
+ //BodyType Thin
+ splatchAdjust.Add(new key(BodyTypeDefOf.Thin, BodyPartDefOf.Arm), new values(new float[] { -0.13f, 0.05f, 0.13f, 0.05f }, new float[] { 0f, 0f, 0f, 0f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Thin, BodyPartDefOf.Hand), new values(new float[] { -0.12f, 0.15f, 0.12f, 0.15f }, new float[] { -0.25f, -0.25f, -0.25f, -0.25f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Thin, BodyPartDefOf.Head), new values(new float[] { 0f, -0.23f, 0f, 0.23f }, new float[] { 0.37f, 0.35f, 0.33f, 0.35f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Thin, BodyPartDefOf.Jaw), new values(new float[] { 0f, -0.19f, 0f, 0.19f }, new float[] { 0.15f, 0.15f, 0.15f, 0.15f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Thin, BodyPartDefOf.Leg), new values(new float[] { -0.1f, 0.1f, 0.1f, 0.1f }, new float[] { -0.4f, -0.4f, -0.4f, -0.4f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Thin, BodyPartDefOf.Neck), new values(new float[] { 0f, -0.07f, 0f, 0.07f }, new float[] { 0.06f, 0.06f, 0.06f, 0.06f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Thin, BodyPartDefOf.Torso), new values(new float[] { 0f, 0f, 0f, 0f }, new float[] { -0.18f, -0.20f, -0.25f, -0.25f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Thin, genitalsDef), new values(new float[] { 0f, 0.01f, 0f, -0.01f }, new float[] { 0, -0.35f, -0.35f, -0.35f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Thin, anusDef), new values(new float[] { 0, 0.18f, 0, -0.18f }, new float[] { -0.42f, -0.35f, 0, -0.35f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Thin, chestDef), new values(new float[] { 0f, -0.1f, 0f, 0.1f }, new float[] { -0.06f, -0.05f, -0.06f, -0.05f }));
+
+ //BodyType Female
+ splatchAdjust.Add(new key(BodyTypeDefOf.Female, BodyPartDefOf.Arm), new values(new float[] { -0.17f, 0f, 0.17f, 0f }, new float[] { 0f, 0f, 0f, 0f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Female, BodyPartDefOf.Hand), new values(new float[] { -0.17f, 0.1f, 0.17f, 0.1f }, new float[] { -0.25f, -0.25f, -0.25f, -0.25f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Female, BodyPartDefOf.Head), new values(new float[] { 0f, -0.23f, 0f, 0.23f }, new float[] { 0.37f, 0.35f, 0.33f, 0.35f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Female, BodyPartDefOf.Jaw), new values(new float[] { 0f, -0.19f, 0f, 0.19f }, new float[] { 0.15f, 0.15f, 0.15f, 0.15f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Female, BodyPartDefOf.Leg), new values(new float[] { -0.2f, 0.1f, 0.2f, 0.1f }, new float[] { -0.4f, -0.4f, -0.4f, -0.4f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Female, BodyPartDefOf.Neck), new values(new float[] { 0f, -0.07f, 0f, 0.07f }, new float[] { 0.06f, 0.06f, 0.06f, 0.06f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Female, BodyPartDefOf.Torso), new values(new float[] { 0f, -0.05f, 0f, 0.05f }, new float[] { -0.20f, -0.25f, -0.25f, -0.25f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Female, genitalsDef), new values(new float[] { 0f, -0.10f, 0f, 0.10f }, new float[] { 0, -0.42f, -0.45f, -0.42f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Female, anusDef), new values(new float[] { 0, 0.26f, 0, -0.26f }, new float[] { -0.42f, -0.35f, 0, -0.35f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Female, chestDef), new values(new float[] { 0f, -0.12f, 0f, 0.12f }, new float[] { -0.06f, -0.05f, -0.06f, -0.05f }));
+
+ //BodyType Male
+ splatchAdjust.Add(new key(BodyTypeDefOf.Male, BodyPartDefOf.Arm), new values(new float[] { -0.21f, 0.05f, 0.21f, 0.05f }, new float[] { 0f, -0.02f, 0f, -0.02f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Male, BodyPartDefOf.Hand), new values(new float[] { -0.17f, 0.07f, 0.17f, 0.07f }, new float[] { -0.25f, -0.25f, -0.25f, -0.25f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Male, BodyPartDefOf.Head), new values(new float[] { 0f, -0.23f, 0f, 0.23f }, new float[] { 0.37f, 0.35f, 0.33f, 0.35f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Male, BodyPartDefOf.Jaw), new values(new float[] { 0f, -0.19f, 0f, 0.19f }, new float[] { 0.15f, 0.15f, 0.15f, 0.15f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Male, BodyPartDefOf.Leg), new values(new float[] { -0.17f, 0.07f, 0.17f, 0.07f }, new float[] { -0.4f, -0.4f, -0.4f, -0.4f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Male, BodyPartDefOf.Neck), new values(new float[] { 0f, -0.07f, 0f, 0.07f }, new float[] { 0.06f, 0.06f, 0.06f, 0.06f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Male, BodyPartDefOf.Torso), new values(new float[] { 0f, -0.05f, 0f, 0.05f }, new float[] { -0.20f, -0.25f, -0.25f, -0.25f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Male, genitalsDef), new values(new float[] { 0f, -0.07f, 0f, 0.07f }, new float[] { 0, -0.35f, -0.42f, -0.35f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Male, anusDef), new values(new float[] { 0, 0.17f, 0, -0.17f }, new float[] { -0.42f, -0.35f, 0, -0.35f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Male, chestDef), new values(new float[] { 0f, -0.16f, 0f, 0.16f }, new float[] { -0.06f, -0.05f, -0.06f, -0.05f }));
+
+ //BodyType Hulk
+ splatchAdjust.Add(new key(BodyTypeDefOf.Hulk, BodyPartDefOf.Arm), new values(new float[] { -0.3f, 0.05f, 0.3f, 0.05f }, new float[] { 0f, -0.02f, 0f, -0.02f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Hulk, BodyPartDefOf.Hand), new values(new float[] { -0.22f, 0.07f, 0.22f, 0.07f }, new float[] { -0.28f, -0.28f, -0.28f, -0.28f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Hulk, BodyPartDefOf.Head), new values(new float[] { 0f, -0.23f, 0f, 0.23f }, new float[] { 0.37f, 0.35f, 0.33f, 0.35f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Hulk, BodyPartDefOf.Jaw), new values(new float[] { 0f, -0.19f, 0f, 0.19f }, new float[] { 0.15f, 0.15f, 0.15f, 0.15f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Hulk, BodyPartDefOf.Leg), new values(new float[] { -0.17f, 0.07f, 0.17f, 0.07f }, new float[] { -0.5f, -0.5f, -0.5f, -0.5f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Hulk, BodyPartDefOf.Neck), new values(new float[] { 0f, -0.07f, 0f, 0.07f }, new float[] { 0.06f, 0.06f, 0.06f, 0.06f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Hulk, BodyPartDefOf.Torso), new values(new float[] { 0f, -0.05f, 0f, 0.05f }, new float[] { -0.20f, -0.3f, -0.3f, -0.3f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Hulk, genitalsDef), new values(new float[] { 0f, -0.02f, 0f, 0.02f }, new float[] { 0, -0.55f, -0.55f, -0.55f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Hulk, anusDef), new values(new float[] { 0, 0.35f, 0, -0.35f }, new float[] { -0.5f, -0.5f, 0, -0.5f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Hulk, chestDef), new values(new float[] { 0f, -0.22f, 0f, 0.22f }, new float[] { -0.06f, -0.05f, -0.06f, -0.05f }));
+
+ //BodyType Fat
+ splatchAdjust.Add(new key(BodyTypeDefOf.Fat, BodyPartDefOf.Arm), new values(new float[] { -0.3f, 0.05f, 0.3f, 0.05f }, new float[] { 0f, -0.02f, 0f, -0.02f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Fat, BodyPartDefOf.Hand), new values(new float[] { -0.32f, 0.07f, 0.32f, 0.07f }, new float[] { -0.28f, -0.28f, -0.28f, -0.28f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Fat, BodyPartDefOf.Head), new values(new float[] { 0f, -0.23f, 0f, 0.23f }, new float[] { 0.37f, 0.35f, 0.33f, 0.35f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Fat, BodyPartDefOf.Jaw), new values(new float[] { 0f, -0.19f, 0f, 0.19f }, new float[] { 0.15f, 0.15f, 0.15f, 0.15f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Fat, BodyPartDefOf.Leg), new values(new float[] { -0.17f, 0.07f, 0.17f, 0.07f }, new float[] { -0.45f, -0.45f, -0.45f, -0.45f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Fat, BodyPartDefOf.Neck), new values(new float[] { 0f, -0.07f, 0f, 0.07f }, new float[] { 0.06f, 0.06f, 0.06f, 0.06f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Fat, BodyPartDefOf.Torso), new values(new float[] { 0f, -0.15f, 0f, 0.15f }, new float[] { -0.20f, -0.3f, -0.3f, -0.3f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Fat, genitalsDef), new values(new float[] { 0f, -0.25f, 0f, 0.25f }, new float[] { 0, -0.45f, -0.50f, -0.45f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Fat, anusDef), new values(new float[] { 0, 0.35f, 0, -0.35f }, new float[] { -0.5f, -0.4f, 0, -0.4f }));
+ splatchAdjust.Add(new key(BodyTypeDefOf.Fat, chestDef), new values(new float[] { 0f, -0.27f, 0f, 0.27f }, new float[] { -0.07f, -0.05f, -0.07f, -0.05f }));
+
+
+ //now for the layer/plane adjustments:
+ layerAdjust = new Dictionary();
+
+ //left body parts:
+ //in theory, all body parts not coming in pairs should have the bool as false -> be listed as right, so I wouldn't need to add them here, but it doesn't hurt to be safe
+ layerAdjust.Add(new key_layer(true, BodyPartDefOf.Arm), new values_layer(new float[] { 0f, -99f, 0f, 0f }));//0.00 = drawn over body (=visible) if the pawn looks in any direction except west, in which case it's hidden (-99)
+ layerAdjust.Add(new key_layer(true, BodyPartDefOf.Hand), new values_layer(new float[] { 0f, -99f, 0f, 0f }));
+ layerAdjust.Add(new key_layer(true, BodyPartDefOf.Leg), new values_layer(new float[] { 0f, -99f, 0f, 0f }));
+
+ layerAdjust.Add(new key_layer(true, BodyPartDefOf.Head), new values_layer(new float[] { 0.02f, 0.02f, 0.02f, 0.02f }));//drawn from all directions, 0.02 = over hair
+ layerAdjust.Add(new key_layer(true, BodyPartDefOf.Jaw), new values_layer(new float[] { -9f, 0.01f, 0.01f, 0.01f }));//0.01 = drawn over head but under hair, only hidden if facing north
+ layerAdjust.Add(new key_layer(true, BodyPartDefOf.Neck), new values_layer(new float[] { 0f, 0f, 0f, 0f }));
+ layerAdjust.Add(new key_layer(true, BodyPartDefOf.Torso), new values_layer(new float[] { 0f, 0f, 0f, 0f }));
+ layerAdjust.Add(new key_layer(true, genitalsDef), new values_layer(new float[] { -99f, 0f, 0f, 0f }));//only hidden if facing north
+ layerAdjust.Add(new key_layer(true, anusDef), new values_layer(new float[] { 0f, 0f, -99f, 0f }));
+ layerAdjust.Add(new key_layer(true, chestDef), new values_layer(new float[] { -99f, 0f, 0f, 0f }));
+
+ //right body parts:
+ layerAdjust.Add(new key_layer(false, BodyPartDefOf.Arm), new values_layer(new float[] { 0f, 0f, 0f, -99f }));
+ layerAdjust.Add(new key_layer(false, BodyPartDefOf.Hand), new values_layer(new float[] { 0f, 0f, 0f, -99f }));
+ layerAdjust.Add(new key_layer(false, BodyPartDefOf.Leg), new values_layer(new float[] { 0f, 0f, 0f, -99f }));
+
+ layerAdjust.Add(new key_layer(false, BodyPartDefOf.Head), new values_layer(new float[] { 0.02f, 0.02f, 0.02f, 0.02f }));
+ layerAdjust.Add(new key_layer(false, BodyPartDefOf.Jaw), new values_layer(new float[] { -99f, 0.01f, 0.01f, 0.01f }));
+ layerAdjust.Add(new key_layer(false, BodyPartDefOf.Neck), new values_layer(new float[] { 0f, 0f, 0f, 0f }));
+ layerAdjust.Add(new key_layer(false, BodyPartDefOf.Torso), new values_layer(new float[] { 0f, 0f, 0f, 0f }));
+ layerAdjust.Add(new key_layer(false, genitalsDef), new values_layer(new float[] { -99f, 0f, 0f, 0f }));
+ layerAdjust.Add(new key_layer(false, anusDef), new values_layer(new float[] { 0f, 0f, -99f, 0f }));
+ layerAdjust.Add(new key_layer(false, chestDef), new values_layer(new float[] { -99f, 0f, 0f, 0f }));
+
+ }
+
+ //all body parts that cum can theoretically be applied to:
+ public static List getAllowedBodyParts()
+ {
+ List allowedParts = new List();
+
+ allowedParts.Add(BodyPartDefOf.Arm);
+ allowedParts.Add(BodyPartDefOf.Hand);
+ allowedParts.Add(BodyPartDefOf.Leg);
+ allowedParts.Add(BodyPartDefOf.Head);
+ allowedParts.Add(BodyPartDefOf.Jaw);
+ allowedParts.Add(BodyPartDefOf.Neck);
+ allowedParts.Add(BodyPartDefOf.Torso);
+ allowedParts.Add(genitalsDef);
+ allowedParts.Add(anusDef);
+ allowedParts.Add(chestDef);
+
+ return allowedParts;
+ }
+
+ //get valid body parts for a specific pawn
+ public static IEnumerable getAvailableBodyParts(Pawn pawn)
+ {
+ //get all non-missing body parts:
+ IEnumerable bodyParts = pawn.health.hediffSet.GetNotMissingParts(BodyPartHeight.Undefined, BodyPartDepth.Outside, null, null);
+ BodyPartRecord anus = pawn.def.race.body.AllParts.Find(bpr => string.Equals(bpr.def.defName, "Anus"));//not found by above function since depth is "inside"
+
+ if (anus != null)
+ {
+ bodyParts = bodyParts.AddItem(anus);
+ }
+
+ //filter for allowed body parts (e.g. no single fingers/toes):
+ List filterParts = CumHelper.getAllowedBodyParts();
+
+ IEnumerable filteredParts = bodyParts.Where(item1 => filterParts.Any(item2 => item2.Equals(item1.def)));
+ return filteredParts;
+ }
+
+
+ public const int CUM_NORMAL = 0;
+ public const int CUM_INSECT = 1;
+ public const int CUM_MECHA = 2;
+
+ public static readonly Color color_normal = new Color(0.95f, 0.95f, 0.95f);
+ public static readonly Color color_insect = new Color(0.6f, 0.83f, 0.35f);//green-yellowish
+ public static readonly Color color_mecha = new Color(0.37f, 0.71f, 0.82f);//cyan-ish
+
+ //name should be self-explanatory:
+ public static void cumOn(Pawn receiver, BodyPartRecord bodyPart, float amount = 0.2f, Pawn giver = null, int cumType = CUM_NORMAL)
+ {
+ Hediff_Cum hediff;
+ if (cumType == CUM_NORMAL)
+ {
+ hediff = (Hediff_Cum)HediffMaker.MakeHediff(HediffDefOf.Hediff_Cum, receiver, null);
+ }
+ else if (cumType == CUM_INSECT)
+ {
+ hediff = (Hediff_Cum)HediffMaker.MakeHediff(HediffDefOf.Hediff_InsectSpunk, receiver, null);
+ }
+ else
+ {
+ hediff = (Hediff_Cum)HediffMaker.MakeHediff(HediffDefOf.Hediff_MechaFluids, receiver, null);
+ }
+
+ hediff.Severity = amount;//if this body part is already maximally full -> spill over to other parts
+
+ //idea: here, a log entry that can act as source could be linked to the hediff - maybe reuse the playlog entry of rjw:
+ hediff.cumType = cumType;
+
+ try
+ {
+ //error when adding to missing part
+ receiver.health.AddHediff(hediff, bodyPart, null, null);
+ }
+ catch
+ {
+
+ }
+
+ //Log.Message(xxx.get_pawnname(receiver) + " cum amount" + amount);
+ //causes significant memory leak, fixx someday
+ //if (amount > 1f)//spillover in case of very large amounts: just apply hediff a second time
+ //{
+ // Hediff_cum hediff2 = (Hediff_cum)HediffMaker.MakeHediff(hediff.def, receiver, null);
+ // hediff2.cumType = cumType;
+ // hediff2.Severity = amount - 1f;
+ // receiver.health.AddHediff(hediff2, bodyPart, null, null);
+ //}
+
+ //always also add cumcontroller hediff as manager
+ receiver.health.AddHediff(HediffDefOf.Hediff_CumController);
+ }
+
+ //if spunk on one body part reaches a certain level, it can spill over to others, this function returns from where to where
+ //[SyncMethod]
+ public static BodyPartDef spillover(BodyPartDef sourcePart)
+ {
+ //caution: danger of infinite loop if circular spillover between 2 full parts -> don't define possible circles
+ BodyPartDef newPart = null;
+ int sel;
+ //Rand.PopState();
+ //Rand.PushState(RJW_Multiplayer.PredictableSeed());
+ if (sourcePart == BodyPartDefOf.Torso)
+ {
+ sel = Rand.Range(0, 4);
+ if (sel == 0)
+ {
+ newPart = BodyPartDefOf.Arm;
+ }
+ else if (sel == 1)
+ {
+ newPart = BodyPartDefOf.Leg;
+ }
+ else if (sel == 2)
+ {
+ newPart = BodyPartDefOf.Neck;
+ }
+ else if (sel == 3)
+ {
+ newPart = chestDef;
+ }
+ }
+ else if (sourcePart == BodyPartDefOf.Jaw)
+ {
+ sel = Rand.Range(0, 4);
+ if (sel == 0)
+ {
+ newPart = BodyPartDefOf.Head;
+ }
+ else if (sel == 1)
+ {
+ newPart = BodyPartDefOf.Torso;
+ }
+ else if (sel == 2)
+ {
+ newPart = BodyPartDefOf.Neck;
+ }
+ else if (sel == 3)
+ {
+ newPart = chestDef;
+ }
+ }
+ else if (sourcePart == genitalsDef)
+ {
+ sel = Rand.Range(0, 2);
+ if (sel == 0)
+ {
+ newPart = BodyPartDefOf.Leg;
+ }
+ else if (sel == 1)
+ {
+ newPart = BodyPartDefOf.Torso;
+ }
+ }
+ else if (sourcePart == anusDef)
+ {
+ sel = Rand.Range(0, 2);
+ if (sel == 0)
+ {
+ newPart = BodyPartDefOf.Leg;
+ }
+ else if (sel == 1)
+ {
+ newPart = BodyPartDefOf.Torso;
+ }
+ }
+ else if (sourcePart == chestDef)
+ {
+ sel = Rand.Range(0, 3);
+ if (sel == 0)
+ {
+ newPart = BodyPartDefOf.Arm;
+ }
+ else if (sel == 1)
+ {
+ newPart = BodyPartDefOf.Torso;
+ }
+ else if (sel == 2)
+ {
+ newPart = BodyPartDefOf.Neck;
+ }
+ }
+ return newPart;
+ }
+
+ //determines who is the active male (or equivalent) in the exchange and the amount of cum dispensed and where to
+ //[SyncMethod]
+ public static void calculateAndApplyCum(SexProps props)
+ {
+ if (!CumBase.cum_on_body) return;
+ Pawn pawn = props.pawn;
+ Pawn partner = props.partner;
+
+ Pawn giver, receiver;
+ //Rand.PopState();
+ //Rand.PushState(RJW_Multiplayer.PredictableSeed());
+
+ List giverparts;
+ List pawnparts = pawn.GetGenitalsList();
+ List partnerparts = partner != pawn ? partner.GetGenitalsList(): null; // masturbation
+
+ //dispenser of the seed
+ if (xxx.is_mechanoid(pawn) || Genital_Helper.has_penis_fertile(pawn, pawnparts) || Genital_Helper.has_ovipositorF(pawn, pawnparts))
+ {
+ giver = pawn;
+ giverparts = pawnparts;
+ receiver = partner;
+ }
+ else if (partner != null && props.isCoreLovin && (xxx.is_mechanoid(partner) || Genital_Helper.has_penis_fertile(partner, partnerparts) || Genital_Helper.has_ovipositorF(partner, partnerparts)))
+ {
+ giver = partner;
+ giverparts = partnerparts;
+ receiver = pawn;
+ }
+ else//female on female or genderless - no cum dispensed; maybe add futa support?
+ {
+ return;
+ }
+
+ //slimes do not waste fluids?
+ //if (xxx.is_slime(giver)) return;
+
+ //determine entity:
+ int entityType = CumHelper.CUM_NORMAL;
+ if (xxx.is_mechanoid(giver))
+ {
+ entityType = CumHelper.CUM_MECHA;
+ }
+ else if (xxx.is_insect(giver))
+ {
+ entityType = CumHelper.CUM_INSECT;
+ }
+
+ //ModLog.Message("giver " + xxx.get_pawnname(giver));
+ //ModLog.Message("receiver " + xxx.get_pawnname(receiver));
+ //ModLog.Message("Cumtype " + entityType);
+
+ //get pawn genitalia:
+ BodyPartRecord genitals;
+ if (xxx.is_mechanoid(giver))
+ {
+ genitals = giver.RaceProps.body.AllParts.Find(x => string.Equals(x.def.defName, "MechGenitals"));
+ }
+ else//insects, animals, humans
+ {
+ genitals = giver.RaceProps.body.AllParts.Find(x => x.def == CumHelper.genitalsDef);
+
+ }
+ //no cum without genitals
+ if (genitals == null)
+ {
+ return;
+ }
+
+ float cumAmount = giver.BodySize; //fallback for mechanoinds and w/e without hediffs
+ float horniness = 1f;
+ float ageScale = Math.Min(80 / SexUtility.ScaleToHumanAge(giver), 1.0f);//calculation lifted from rjw
+
+ if (xxx.is_mechanoid(giver) && giverparts.NullOrEmpty())
+ {
+ //use default above
+ }
+ else if (giverparts.NullOrEmpty())
+ return;
+ else
+ {
+ var penisHediff = giverparts.FindAll((Hediff hed) => hed.def.defName.ToLower().Contains("penis")).InRandomOrder().FirstOrDefault();
+
+ if (penisHediff == null)
+ penisHediff = giverparts.FindAll((Hediff hed) => hed.def.defName.ToLower().Contains("ovipositorf")).InRandomOrder().FirstOrDefault();
+ if (penisHediff == null)
+ penisHediff = giverparts.FindAll((Hediff hed) => hed.def.defName.ToLower().Contains("ovipositorm")).InRandomOrder().FirstOrDefault();
+ if (penisHediff == null)
+ penisHediff = giverparts.FindAll((Hediff hed) => hed.def.defName.ToLower().Contains("tentacle")).InRandomOrder().FirstOrDefault();
+
+ if (penisHediff != null)
+ {
+ cumAmount = penisHediff.Severity * giver.BodySize;
+
+ CompHediffBodyPart chdf = penisHediff.TryGetComp();
+ if (chdf != null)
+ {
+ if (chdf.FluidAmmount != 0)
+ cumAmount = chdf.FluidAmmount * chdf.FluidModifier;
+ }
+ //ModLog.Message("cumAmount base " + cumAmount);
+
+ Need sexNeed = giver?.needs?.AllNeeds.Find(x => string.Equals(x.def.defName, "Sex"));
+ if (sexNeed != null)//non-humans don't have it - therefore just use the default value
+ {
+ horniness = 1f - sexNeed.CurLevel;
+ }
+ }
+ else
+ {
+ //something is wrong... vagina?
+ return;
+ }
+ }
+
+ cumAmount *= CumBase.cum_on_body_amount;
+ //ModLog.Message("cumAmount after cum_on_body_amount_adjust after " + cumAmount);
+ cumAmount *= horniness;
+ //ModLog.Message("cumAmount after horniness mod " + cumAmount);
+ cumAmount *= ageScale;
+ //ModLog.Message("cumAmount after ageScale mod " + cumAmount);
+ cumAmount /= 10;
+ //ModLog.Message("cumAmount final " + cumAmount);
+
+ //TODO: cumHelper Autofellatio
+ //if no partner -> masturbation, apply some cum on self:
+ //if (partner == null && sextype == xxx.rjwSextype.Autofellatio)
+ //{
+ // if (!xxx.is_slime(giver))
+ // cumHelper.cumOn(giver, BodyPartDefOf.Jaw, cumAmount, giver);
+ // return;
+ //}
+ if (props.sexType == xxx.rjwSextype.Masturbation)
+ {
+ if (!xxx.is_slime(giver))
+ CumHelper.cumOn(giver, genitals, cumAmount * 0.3f, giver);//pawns are usually not super-messy -> only apply 30%
+ return;
+ }
+ else if (receiver != null)
+ {
+ List targetParts = new List();//which to apply cum on
+ IEnumerable availableParts = CumHelper.getAvailableBodyParts(receiver);
+ BodyPartRecord randomPart;//not always needed
+
+ switch (props.sexType)
+ {
+ case rjw.xxx.rjwSextype.Anal:
+ targetParts.Add(receiver.RaceProps.body.AllParts.Find(x => x.def == CumHelper.anusDef));
+ break;
+ case rjw.xxx.rjwSextype.Boobjob:
+ targetParts.Add(receiver.RaceProps.body.AllParts.Find(x => x.def == CumHelper.chestDef));
+ break;
+ case rjw.xxx.rjwSextype.DoublePenetration:
+ targetParts.Add(receiver.RaceProps.body.AllParts.Find(x => x.def == CumHelper.anusDef));
+ targetParts.Add(receiver.RaceProps.body.AllParts.Find(x => x.def == CumHelper.genitalsDef));
+ break;
+ case rjw.xxx.rjwSextype.Fingering:
+ cumAmount = 0;
+ break;
+ case rjw.xxx.rjwSextype.Fisting:
+ cumAmount = 0;
+ break;
+ case rjw.xxx.rjwSextype.Footjob:
+ //random part:
+ availableParts.TryRandomElement(out randomPart);
+ targetParts.Add(randomPart);
+ break;
+ case rjw.xxx.rjwSextype.Handjob:
+ //random part:
+ availableParts.TryRandomElement(out randomPart);
+ targetParts.Add(randomPart);
+ break;
+ case rjw.xxx.rjwSextype.Masturbation:
+ cumAmount *= 2f;
+ break;
+ case rjw.xxx.rjwSextype.MechImplant:
+ //one of the openings:
+ int random = Rand.Range(0, 3);
+ if (random == 0)
+ {
+ targetParts.Add(receiver.RaceProps.body.AllParts.Find(x => x.def == CumHelper.genitalsDef));
+ }
+ else if (random == 1)
+ {
+ targetParts.Add(receiver.RaceProps.body.AllParts.Find(x => x.def == CumHelper.anusDef));
+ }
+ else if (random == 2)
+ {
+ targetParts.Add(receiver.RaceProps.body.AllParts.Find(x => x.def == BodyPartDefOf.Jaw));
+ }
+ break;
+ case rjw.xxx.rjwSextype.MutualMasturbation:
+ //random
+ availableParts.TryRandomElement(out randomPart);
+ targetParts.Add(randomPart);
+ break;
+ case rjw.xxx.rjwSextype.None:
+ cumAmount = 0;
+ break;
+ case rjw.xxx.rjwSextype.Oral:
+ targetParts.Add(receiver.RaceProps.body.AllParts.Find(x => x.def == BodyPartDefOf.Jaw));
+ break;
+ case rjw.xxx.rjwSextype.Scissoring:
+ //I guess if it came to here, a male must be involved?
+ targetParts.Add(receiver.RaceProps.body.AllParts.Find(x => x.def == CumHelper.genitalsDef));
+ break;
+ case rjw.xxx.rjwSextype.Vaginal:
+ targetParts.Add(receiver.RaceProps.body.AllParts.Find(x => x.def == CumHelper.genitalsDef));
+ break;
+ }
+
+ if (cumAmount > 0)
+ {
+ if (receiver != null && xxx.is_slime(receiver))
+ {
+ //slime absorb cum
+ //this needs balancing, since cumamount ranges 0-10(?) which is fine for cum/hentai but not very realistic for feeding
+ //using TransferNutrition for now
+ //Log.Message("cumAmount " + cumAmount);
+ //float nutrition_amount = cumAmount/10;
+
+ Need_Food need = need = giver.needs.TryGetNeed();
+ if (need == null)
+ {
+ //Log.Message("xxx::TransferNutrition() " + xxx.get_pawnname(pawn) + " doesn't track nutrition in itself, probably shouldn't feed the others");
+ return;
+ }
+
+ if (receiver?.needs?.TryGetNeed() != null)
+ {
+ //Log.Message("xxx::TransferNutrition() " + xxx.get_pawnname(partner) + " can receive");
+ float nutrition_amount = Math.Min(need.MaxLevel / 15f, need.CurLevel); //body size is taken into account implicitly by need.MaxLevel
+ receiver.needs.food.CurLevel += nutrition_amount;
+ }
+ }
+ else
+ {
+ CumHelper.cumOn(giver, genitals, cumAmount * 0.3f, giver, entityType);//cum on self - smaller amount
+ if (receiver != null)
+ foreach (BodyPartRecord bpr in targetParts)
+ {
+ if (bpr != null)
+ {
+ CumHelper.cumOn(receiver, bpr, cumAmount, giver, entityType);//cum on partner
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/1.4/Source/Mod/DefOf/HediffDefOf.cs b/1.4/Source/Mod/DefOf/HediffDefOf.cs
new file mode 100644
index 0000000..46e8255
--- /dev/null
+++ b/1.4/Source/Mod/DefOf/HediffDefOf.cs
@@ -0,0 +1,17 @@
+using RimWorld;
+using Verse;
+
+namespace rjwcum
+{
+ [DefOf]
+ public static class HediffDefOf
+ {
+ public static HediffDef Hediff_Cum;//for humans & animals
+ public static HediffDef Hediff_InsectSpunk;
+ public static HediffDef Hediff_MechaFluids;
+
+ public static HediffDef Hediff_CumController;//cum hediff manager
+
+ }
+
+}
diff --git a/1.4/Source/Mod/DefOf/JobDefOf.cs b/1.4/Source/Mod/DefOf/JobDefOf.cs
new file mode 100644
index 0000000..f8569e6
--- /dev/null
+++ b/1.4/Source/Mod/DefOf/JobDefOf.cs
@@ -0,0 +1,11 @@
+using RimWorld;
+using Verse;
+
+namespace rjwcum
+{
+ [DefOf]
+ public static class JobDefOf
+ {
+ public static JobDef CleanSelf;
+ }
+}
diff --git a/1.4/Source/Mod/Hediffs/Hediff_Cum.cs b/1.4/Source/Mod/Hediffs/Hediff_Cum.cs
new file mode 100644
index 0000000..7652b64
--- /dev/null
+++ b/1.4/Source/Mod/Hediffs/Hediff_Cum.cs
@@ -0,0 +1,155 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Verse;
+using UnityEngine;
+//using Multiplayer.API;
+
+namespace rjwcum
+{
+ public class Hediff_Cum : HediffWithComps
+ {
+ public int cumType = CumHelper.CUM_NORMAL;//-> different colors
+
+ public string giverName = null;//not utilized right now, maybe in the future save origin of the cum
+
+ public override string LabelInBrackets
+ {
+ get
+ {
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.Append(base.LabelInBrackets);
+ if (this.sourceHediffDef != null)
+ {
+ if (stringBuilder.Length != 0)
+ {
+ stringBuilder.Append(", ");
+ }
+ stringBuilder.Append(this.sourceHediffDef.label);
+ }
+ else if (this.source != null)
+ {
+ if (stringBuilder.Length != 0)
+ {
+ stringBuilder.Append(", ");
+ }
+ stringBuilder.Append(this.source.label);
+ if (this.sourceBodyPartGroup != null)
+ {
+ stringBuilder.Append(" ");
+ stringBuilder.Append(this.sourceBodyPartGroup.LabelShort);
+ }
+ }
+ return stringBuilder.ToString();
+ }
+ }
+
+
+ public override string SeverityLabel
+ {
+ get
+ {
+ if (this.Severity == 0f)
+ {
+ return null;
+ }
+ return this.Severity.ToString("F1");
+ }
+ }
+
+ //[SyncMethod]
+ public override bool TryMergeWith(Hediff other)
+ {
+ //if a new cum hediff is added to the same body part, they are combined. if severity reaches more than 1, spillover to other body parts occurs
+
+ Hediff_Cum hediff_cum = other as Hediff_Cum;
+ if (hediff_cum != null && hediff_cum.def == this.def && hediff_cum.Part == base.Part && this.def.injuryProps.canMerge)
+ {
+ cumType = hediff_cum.cumType;//take over new creature color
+
+ float totalAmount = hediff_cum.Severity + this.Severity;
+ if (totalAmount > 1.0f)
+ {
+ BodyPartDef spillOverTo = CumHelper.spillover(this.Part.def);//cumHelper saves valid other body parts for spillover
+ if (spillOverTo != null)
+ {
+ //Rand.PopState();
+ //Rand.PushState(RJW_Multiplayer.PredictableSeed());
+ IEnumerable availableParts = CumHelper.getAvailableBodyParts(pawn);//gets all non missing, valid body parts
+ IEnumerable filteredParts = availableParts.Where(x => x.def == spillOverTo);//filters again for valid spill target
+ if (!filteredParts.EnumerableNullOrEmpty())
+ {
+ BodyPartRecord spillPart = null;
+ spillPart = filteredParts.RandomElement();//then pick one
+ if (spillPart != null)
+ {
+ CumHelper.cumOn(pawn, spillPart, totalAmount - this.Severity, null, cumType);
+ }
+ }
+ }
+ }
+
+ return (base.TryMergeWith(other));
+
+ }
+ return (false);
+ }
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ Scribe_Values.Look(ref cumType, "cumType", CumHelper.CUM_NORMAL);
+
+ if (Scribe.mode == LoadSaveMode.PostLoadInit && base.Part == null)
+ {
+ //Log.Error("Hediff_cum has null part after loading.", false);
+ this.pawn.health.hediffSet.hediffs.Remove(this);
+ return;
+ }
+ }
+
+ //handles the icon in the health tab and its color
+ public override TextureAndColor StateIcon
+ {
+ get
+ {
+ TextureAndColor tex = TextureAndColor.None;
+ Color color = Color.white;
+ switch (cumType)
+ {
+ case CumHelper.CUM_NORMAL:
+ color = CumHelper.color_normal;
+ break;
+ case CumHelper.CUM_INSECT:
+ color = CumHelper.color_insect;
+ break;
+ case CumHelper.CUM_MECHA:
+ color = CumHelper.color_mecha;
+ break;
+ }
+
+ Texture2D tex2d = CumTextures.CumIcon_little;
+ switch (this.CurStageIndex)
+ {
+ case 0:
+ tex2d = CumTextures.CumIcon_little;
+ break;
+ case 1:
+ tex2d = CumTextures.CumIcon_some;
+ break;
+ case 2:
+ tex2d = CumTextures.CumIcon_dripping;
+ break;
+ case 3:
+ tex2d = CumTextures.CumIcon_drenched;
+ break;
+ }
+
+ tex = new TextureAndColor(tex2d, color);
+
+ return tex;
+ }
+ }
+
+ }
+}
diff --git a/1.4/Source/Mod/Hediffs/Hediff_CumController.cs b/1.4/Source/Mod/Hediffs/Hediff_CumController.cs
new file mode 100644
index 0000000..a3a3224
--- /dev/null
+++ b/1.4/Source/Mod/Hediffs/Hediff_CumController.cs
@@ -0,0 +1,272 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Verse;
+using RimWorld;
+using UnityEngine;
+//using Multiplayer.API;
+
+namespace rjwcum
+{
+ class Hediff_CumController : HediffWithComps
+ {
+ /*
+ Whenever cum is applied, this hediff is also added to the pawn.
+ Since there is always only a single hediff of this type on the pawn, it serves as master controller, adding up the individual cum hediffs, applying debuffs and drawing overlays
+ */
+
+ private static readonly float cumWeight = 0.2f;//how much individual cum_hediffs contribute to the overall bukkake severity
+ List hediffs_cum;
+ Dictionary splatches;
+
+ public override void ExposeData()
+ {
+ base.ExposeData();
+ //Scribe_Values.Look>(ref splatches, "splatches", new Dictionary()); - causes errors when loading. for now, just make a new dictionary
+ splatches = new Dictionary();//instead of loading, just recreate anew
+ hediffs_cum = new List();
+ }
+
+ public override void PostMake()
+ {
+ base.PostMake();
+ splatches = new Dictionary();
+ }
+
+ public override void PostTick()
+ {
+ if (pawn.RaceProps.Humanlike)//for now, only humans are supported
+ {
+ hediffs_cum = this.pawn.health.hediffSet.hediffs.FindAll(x => (x.def == HediffDefOf.Hediff_Cum || x.def == HediffDefOf.Hediff_InsectSpunk || x.def == HediffDefOf.Hediff_MechaFluids));
+ float Level = CalculateLevel();//sum of severity of all the cum hediffs x cumWeight
+ this.Severity = Level;
+ bool updatePortrait = false;
+
+ //loop through all cum hediffs, add missing ones to dictionary
+ for (int i = 0; i < hediffs_cum.Count(); i++)
+ {
+ Hediff_Cum h = (Hediff_Cum)hediffs_cum[i];
+ string ID = h.GetUniqueLoadID();//unique ID for each hediff
+ if (!splatches.ContainsKey(ID))//if it isn't here yet, make new object
+ {
+ updatePortrait = true;
+ bool leftSide = h.Part.Label.Contains("left") ? true : false;//depending on whether the body part is left or right, drawing-offset on x-aixs may be inverted
+
+ splatches[ID] = new CumSplatch(h, pawn.story.bodyType, h.Part.def, leftSide, h.cumType);
+ }
+ }
+
+
+
+ //remove splatch objects once their respective cum hediff is gone
+ List removeKeys = new List();
+ foreach (string key in splatches.Keys)
+ {
+ CumSplatch s = splatches[key];
+
+ if (!hediffs_cum.Contains(s.hediff_cum))
+ {
+ removeKeys.Add(key);
+ updatePortrait = true;
+ }
+ }
+ //loop over and remove elements that should be destroyed:
+ foreach (string key in removeKeys)
+ {
+ CumSplatch s = splatches[key];
+ splatches.Remove(key);
+ }
+
+ if (updatePortrait)//right now, portraits are only updated when a completely new cum hediff is added or an old one removed - maybe that should be done more frequently
+ {
+ PortraitsCache.SetDirty(pawn);
+ }
+ }
+ }
+
+ //called from the PawnWoundDrawer (see HarmonyPatches)
+ public void DrawCum(Vector3 drawLoc, Quaternion quat, bool forPortrait, float angle, bool inBed = false)
+ {
+ Rot4 bodyFacing = pawn.Rotation;
+ int facingDir = bodyFacing.AsInt;//0: north, 1:east, 2:south, 3:west
+ foreach (string key in splatches.Keys)
+ {
+ CumSplatch s = splatches[key];
+ s.Draw(drawLoc, quat, forPortrait, facingDir, angle, inBed);
+ }
+ }
+
+ //new Hediff_CumController added to pawn -> just combine the two
+ public override bool TryMergeWith(Hediff other)
+ {
+ if (other == null || other.def != this.def)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private float CalculateLevel()
+ {
+ float num = 0f;
+ for (int i = 0; i < hediffs_cum.Count; i++)
+ {
+ num += hediffs_cum[i].Severity * cumWeight;
+ }
+ return num;
+ }
+
+ //class for handling drawing of the individual splatches
+ private class CumSplatch
+ {
+ public readonly Hediff_Cum hediff_cum;
+ public readonly Material cumMaterial;
+ public readonly BodyPartDef bodyPart;
+ private bool mirrorMesh;
+
+ private const float maxSize = 0.20f;//1.0 = 1 tile
+ private const float minSize = 0.05f;
+
+ //data taken from CumHelper.cs:
+ private readonly float[] xAdjust;
+ private readonly float[] zAdjust;
+ private readonly float[] yAdjust;
+
+ public CumSplatch(Hediff_Cum hediff, BodyTypeDef bodyType, BodyPartDef bodyPart, bool leftSide = false, int cumType = CumHelper.CUM_NORMAL)
+ {
+ hediff_cum = hediff;
+ cumMaterial = new Material(CumTextures.pickRandomSplatch());//needs to create new material in order to allow for different colors
+ cumMaterial.SetTextureScale("_MainTex", new Vector2(-1, 1));
+ this.bodyPart = bodyPart;
+ //Rand.PopState();
+ //Rand.PushState(RJW_Multiplayer.PredictableSeed());
+
+ //set color:
+ switch (cumType)
+ {
+ case CumHelper.CUM_NORMAL:
+ cumMaterial.color = CumHelper.color_normal;
+ break;
+ case CumHelper.CUM_INSECT:
+ cumMaterial.color = CumHelper.color_insect;
+ break;
+ case CumHelper.CUM_MECHA:
+ cumMaterial.color = CumHelper.color_mecha;
+ break;
+ }
+
+ //if (!MP.enabled)
+ mirrorMesh = (Rand.Value > 0.5f);//in 50% of the cases, flip mesh horizontally for more variance
+
+ //x,y,z adjustments to draw splatches over the approximately correct locations; values stored in Cum helper - accessed by unique combinations of bodyTypes and bodyParts
+ CumHelper.key k = new CumHelper.key(bodyType, bodyPart);
+ if (CumHelper.splatchAdjust.Keys.Contains(k))
+ {
+ CumHelper.values helperValues = CumHelper.splatchAdjust[k];
+
+ //invert, x-adjust (horizontal) depending on left/right body side:
+ if (!leftSide)
+ {
+ float[] xAdjTemp = new float[4];
+ for (int i = 0; i < xAdjTemp.Length; i++)
+ {
+ xAdjTemp[i] = helperValues.x[i] * -1f;
+ }
+ xAdjust = xAdjTemp;
+ }
+ else
+ {
+ xAdjust = helperValues.x;
+ }
+
+ zAdjust = helperValues.z;//vertical adjustment
+
+ }
+ else//fallback in the the key can't be found
+ {
+ //if (RJWSettings.DevMode)
+ //{
+ // ModLog.Message("created cum splatch for undefined body type or part. BodyType: " + bodyType + " , BodyPart: " + bodyPart);
+ //}
+ xAdjust = new float[] { 0f, 0f, 0f, 0f };
+ zAdjust = new float[] { 0f, 0f, 0f, 0f };
+ }
+
+
+ //y adjustments: plane/layer of drawing, > 0 -> above certain objects, < 0 -> below
+ CumHelper.key_layer k2 = new CumHelper.key_layer(leftSide, bodyPart);
+
+ if (CumHelper.layerAdjust.Keys.Contains(k2))
+ {
+ CumHelper.values_layer helperValues_layer = CumHelper.layerAdjust[k2];
+ yAdjust = helperValues_layer.y;
+ }
+ else
+ {
+ yAdjust = new float[] { 0.02f, 0.02f, 0.02f, 0.02f };//over body in every direction
+ }
+
+ }
+
+ public void Draw(Vector3 drawPos, Quaternion quat, bool forPortrait, int facingDir = 0, float angle = 0,bool inBed=false)
+ {
+ if (inBed)
+ {
+ if (this.bodyPart != BodyPartDefOf.Jaw && this.bodyPart != BodyPartDefOf.Head)//when pawn is in bed (=bed with sheets), only draw cum on head
+ {
+ return;
+ }
+ }
+
+ //these two create new mesh instance and never destroying it, filling ram and crashing
+ //float size = minSize+((maxSize-minSize)*hediff_cum.Severity);
+ //mesh = MeshMakerPlanes.NewPlaneMesh(size);
+
+ //use core MeshPool.plane025 instead
+
+ //if (mirrorMesh)//horizontal flip
+ //{
+ //mesh = flipMesh(mesh);
+ //}
+
+ //rotation:
+ if (angle == 0)//normal situation (pawn standing upright)
+ {
+ drawPos.x += xAdjust[facingDir];
+ drawPos.z += zAdjust[facingDir];
+ }
+ else//if downed etc, more complex calculation becomes necessary
+ {
+ float radian = angle / 180 * (float)Math.PI;
+ radian = -radian;
+ drawPos.x += Mathf.Cos(radian) * xAdjust[hediff_cum.pawn.Rotation.AsInt] - Mathf.Sin(radian) * zAdjust[facingDir];//facingDir doesn't appear to be chosen correctly in all cases
+ drawPos.z += Mathf.Cos(radian) * zAdjust[hediff_cum.pawn.Rotation.AsInt] + Mathf.Sin(radian) * xAdjust[facingDir];
+ }
+
+ //drawPos.y += yAdjust[facingDir];// 0.00: over body; 0.01: over body but under face, 0.02: over face, but under hair, -99 = "never" visible
+ drawPos.y += yAdjust[facingDir];
+
+ GenDraw.DrawMeshNowOrLater(MeshPool.plane025, drawPos, quat, cumMaterial, forPortrait);
+ }
+
+ //flips mesh UV horizontally, thereby mirroring the texture
+ private Mesh flipMesh(Mesh meshToFlip)
+ {
+ var uvs = meshToFlip.uv;
+ if (uvs.Length != 4)
+ {
+ return (meshToFlip);
+ }
+ for (var i = 0; i < uvs.Length; i++)
+ {
+ if (Mathf.Approximately(uvs[i].x, 1.0f))
+ uvs[i].x = 0.0f;
+ else
+ uvs[i].x = 1.0f;
+ }
+ meshToFlip.uv = uvs;
+ return (meshToFlip);
+ }
+ }
+ }
+}
diff --git a/1.4/Source/Mod/JobDrivers/JobDriver_CleanSelf.cs b/1.4/Source/Mod/JobDrivers/JobDriver_CleanSelf.cs
new file mode 100644
index 0000000..d1d4ce3
--- /dev/null
+++ b/1.4/Source/Mod/JobDrivers/JobDriver_CleanSelf.cs
@@ -0,0 +1,53 @@
+using RimWorld;
+using System.Collections.Generic;
+using Verse;
+using Verse.AI;
+
+namespace rjwcum
+{
+ class JobDriver_CleanSelf : JobDriver
+ {
+ float cleanAmount = 1f;//severity of a single cumHediff removed per cleaning-round; 1f = remove entirely
+ int cleaningTime = 120;//ticks - 120 = 2 real seconds, 3 in-game minutes
+
+ public override bool TryMakePreToilReservations(bool errorOnFailed)
+ {
+ return pawn.Reserve(pawn, job, 1, -1, null, errorOnFailed);
+ }
+
+ protected override IEnumerable MakeNewToils()
+ {
+ this.FailOn(delegate
+ {
+ List hediffs = pawn.health.hediffSet.hediffs;
+ return !hediffs.Exists(x => x.def == HediffDefOf.Hediff_CumController);//fail if cum disappears - means that also all the cum is gone
+ });
+ Toil cleaning = Toils_General.Wait(cleaningTime, TargetIndex.None);//duration of
+ cleaning.WithProgressBarToilDelay(TargetIndex.A);
+
+ yield return cleaning;
+ yield return new Toil()
+ {
+ initAction = delegate ()
+ {
+ //get one of the cum hediffs, reduce its severity
+ Hediff hediff = pawn.health.hediffSet.hediffs.Find(x => (x.def == HediffDefOf.Hediff_Cum || x.def == HediffDefOf.Hediff_InsectSpunk || x.def == HediffDefOf.Hediff_MechaFluids));
+ if (hediff != null)
+ {
+ if (hediff.Severity >= 0.5)
+ {
+ if (hediff.def == HediffDefOf.Hediff_InsectSpunk)
+ {
+ Thing jelly = ThingMaker.MakeThing(ThingDefOf.InsectJelly);
+ jelly.SetForbidden(true, false);
+ GenPlace.TryPlaceThing(jelly, pawn.PositionHeld, pawn.MapHeld, ThingPlaceMode.Near);
+ }
+ }
+ hediff.Severity -= cleanAmount;
+ }
+ }
+ };
+ yield break;
+ }
+ }
+}
diff --git a/1.4/Source/Mod/Patch_AddCumOnOrgasm.cs b/1.4/Source/Mod/Patch_AddCumOnOrgasm.cs
new file mode 100644
index 0000000..a348076
--- /dev/null
+++ b/1.4/Source/Mod/Patch_AddCumOnOrgasm.cs
@@ -0,0 +1,66 @@
+using Verse;
+using HarmonyLib;
+using rjw;
+using System;
+//using Multiplayer.API;
+
+namespace rjwcum
+{
+ ///
+ ///apply cum to pawn after vanilla sex
+ ///
+ [HarmonyPatch(typeof(SexUtility), "Aftersex")]
+ [StaticConstructorOnStartup]
+ static class Aftersex_Cum_Apply
+ {
+ [HarmonyPostfix]
+ private static void Aftersex_Cum_Patch(SexProps props)
+ {
+ try
+ {
+ if (props.isCoreLovin)
+ if (!props.usedCondom)
+ {
+ CumHelper.calculateAndApplyCum(props);
+ //SexUtility.CumFilthGenerator(props.pawn);
+ //SexUtility.CumFilthGenerator(props.partner);
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Error(e.ToString());
+ }
+ }
+ }
+
+ ///
+ ///apply cum to pawn after rjw orgasm
+ ///
+ [HarmonyPatch(typeof(JobDriver_Sex), "Orgasm")]
+ [StaticConstructorOnStartup]
+ static class Orgasm_Cum_Apply
+ {
+ [HarmonyPrefix]
+ private static bool Orgasm_Cum_Patch(JobDriver_Sex __instance)
+ {
+ try
+ {
+ if (__instance.sex_ticks > __instance.orgasmstick) //~3s at speed 1
+ {
+ return true;
+ }
+ var props = __instance.Sexprops;
+ if (!props.usedCondom)
+ {
+ CumHelper.calculateAndApplyCum(props);
+ //SexUtility.CumFilthGenerator(props.pawn);
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Error(e.ToString());
+ }
+ return true;
+ }
+ }
+}
diff --git a/1.4/Source/Mod/Patch_AddGizmo.cs b/1.4/Source/Mod/Patch_AddGizmo.cs
new file mode 100644
index 0000000..e995f01
--- /dev/null
+++ b/1.4/Source/Mod/Patch_AddGizmo.cs
@@ -0,0 +1,68 @@
+using System.Collections.Generic;
+using Verse;
+using HarmonyLib;
+using rjw;
+//using Multiplayer.API;
+
+namespace rjwcum
+{
+ //adds new gizmo for adding cum for testing
+ [HarmonyPatch(typeof(Pawn), "GetGizmos")]
+ class Patch_AddGizmo
+ {
+ [HarmonyPriority(99),HarmonyPostfix]
+ static IEnumerable AddCum_test(IEnumerable __result, Pawn __instance)
+ {
+
+ foreach (Gizmo entry in __result)
+ {
+ yield return entry;
+ }
+
+ if (Prefs.DevMode )//&& RJWSettings.DevMode && !MP.IsInMultiplayer)
+ {
+ Command_Action addCum = new Command_Action();
+ addCum.defaultDesc = "AddCumHediff";
+ addCum.defaultLabel = "AddCum";
+ addCum.action = delegate ()
+ {
+ AddCum(__instance);
+ };
+
+ yield return addCum;
+ }
+ }
+
+ //[SyncMethod]
+ static void AddCum(Pawn pawn)
+ {
+ //Log.Message("add cum button is pressed for " + pawn);
+
+ if (!pawn.Dead && pawn.records != null)
+ {
+ //get all acceptable body parts:
+ IEnumerable filteredParts = CumHelper.getAvailableBodyParts(pawn);
+
+ //select random part:
+ BodyPartRecord randomPart;
+ //filteredParts.TryRandomElement(out randomPart);
+ //for testing - choose either genitals or anus:
+ //Rand.PopState();
+ //Rand.PushState(RJW_Multiplayer.PredictableSeed());
+ if (Rand.Value > 0.5f)
+ {
+ randomPart = pawn.RaceProps.body.AllParts.Find(x => x.def == xxx.anusDef);
+ }
+ else
+ {
+ randomPart = pawn.RaceProps.body.AllParts.Find(x => x.def == xxx.genitalsDef);
+ }
+
+ if (randomPart != null)
+ {
+ CumHelper.cumOn(pawn, randomPart, 0.2f, null, CumHelper.CUM_NORMAL);
+ }
+ };
+ }
+ }
+}
diff --git a/1.4/Source/Mod/Patch_JobDriver_DubsBadHygiene.cs b/1.4/Source/Mod/Patch_JobDriver_DubsBadHygiene.cs
new file mode 100644
index 0000000..3ed4168
--- /dev/null
+++ b/1.4/Source/Mod/Patch_JobDriver_DubsBadHygiene.cs
@@ -0,0 +1,85 @@
+using System;
+using HarmonyLib;
+using Verse;
+using Verse.AI;
+using rjw;
+
+namespace rjwcum
+{
+ [HarmonyPatch(typeof(JobDriver), "Cleanup")]
+ internal static class Patch_JobDriver_DubsBadHygiene
+ {
+ //not very good solution, some other mod can have same named jobdriver but w/e
+
+ //Dubs Bad Hygiene washing
+ private readonly static Type JobDriver_useWashBucket = AccessTools.TypeByName("JobDriver_useWashBucket");
+ //private readonly static Type JobDriver_washAtCell = AccessTools.TypeByName("JobDriver_washAtCell");
+
+ private readonly static Type JobDriver_UseHotTub = AccessTools.TypeByName("JobDriver_UseHotTub");
+ private readonly static Type JobDriver_takeShower = AccessTools.TypeByName("JobDriver_takeShower");
+ private readonly static Type JobDriver_takeBath = AccessTools.TypeByName("JobDriver_takeBath");
+
+ [HarmonyPostfix]
+ private static void Cleanup_cum(JobDriver __instance, JobCondition condition)
+ {
+ try
+ {
+ if (__instance == null)
+ return;
+
+ if (condition == JobCondition.Succeeded)
+ {
+ Do_cleanup_cum(__instance);
+ }
+ }
+ catch (Exception e)
+ {
+ Log.Error(e.ToString());
+ }
+ }
+
+ public static void Do_cleanup_cum(JobDriver __instance)
+ {
+ Pawn pawn = __instance.pawn;
+
+ //ModLog.Message("patches_DubsBadHygiene::on_cleanup_driver" + xxx.get_pawnname(pawn));
+
+ if (xxx.DubsBadHygieneIsActive)
+ //clear one instance of cum
+ if (
+ __instance.GetType() == JobDriver_useWashBucket// ||
+ //__instance.GetType() == JobDriver_washAtCell
+ )
+ {
+ Hediff hediff = pawn.health.hediffSet.hediffs.Find(x => (x.def == HediffDefOf.Hediff_Cum
+ || x.def == HediffDefOf.Hediff_InsectSpunk
+ || x.def == HediffDefOf.Hediff_MechaFluids
+ ));
+ if (hediff != null)
+ {
+ //ModLog.Message("patches_DubsBadHygiene::" + __instance.GetType() + " clear => " + hediff.Label);
+ hediff.Severity -= 1f;
+ }
+ }
+ //clear all instance of cum
+ else if (
+ __instance.GetType() == JobDriver_UseHotTub ||
+ __instance.GetType() == JobDriver_takeShower ||
+ __instance.GetType() == JobDriver_takeBath
+ )
+ {
+ foreach (Hediff hediff in pawn.health.hediffSet.hediffs)
+ {
+ if (hediff.def == HediffDefOf.Hediff_Cum ||
+ hediff.def == HediffDefOf.Hediff_InsectSpunk ||
+ hediff.def == HediffDefOf.Hediff_MechaFluids
+ )
+ {
+ //ModLog.Message("patches_DubsBadHygiene::" + __instance.GetType() + " clear => " + hediff.Label);
+ hediff.Severity -= 1f;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/1.4/Source/Mod/Patch_RenderOverBody.cs b/1.4/Source/Mod/Patch_RenderOverBody.cs
new file mode 100644
index 0000000..1437182
--- /dev/null
+++ b/1.4/Source/Mod/Patch_RenderOverBody.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using Verse;
+using HarmonyLib;
+using UnityEngine;
+using System;
+using RimWorld;
+//using Multiplayer.API;
+
+namespace rjwcum
+{
+
+ [HarmonyPatch(typeof(RimWorld.PawnOverlayDrawer))]
+ [HarmonyPatch("RenderPawnOverlay")]
+ [HarmonyPatch(new Type[] { typeof(Vector3), typeof(Mesh), typeof(Quaternion), typeof(bool), typeof(PawnOverlayDrawer.OverlayLayer), typeof(Rot4), typeof(bool) })]
+ class Patch_RenderPawnOverlay
+ {
+ [HarmonyPostfix]
+ static void DrawCum(RimWorld.PawnOverlayDrawer __instance, Vector3 drawLoc, Mesh bodyMesh, Quaternion quat, bool drawNow, PawnOverlayDrawer.OverlayLayer layer, Rot4 pawnRot, bool? overApparel = null)
+ {
+ if (!CumBase.cum_overlays) return;
+
+ Pawn pawn = Traverse.Create(__instance).Field("pawn").GetValue();//get local variable
+
+ //TODO add support for animals? unlikely as they has weird meshes
+ //for now, only draw humans
+ if (pawn.RaceProps.Humanlike) //&& CumOverlayBase.cum_overlays)
+ {
+ //find cum hediff. if it exists, use its draw function
+ List hediffs = pawn.health.hediffSet.hediffs;
+ if (hediffs.Exists(x => x.def == HediffDefOf.Hediff_CumController))
+ {
+ Hediff_CumController h = hediffs.Find(x => x.def == HediffDefOf.Hediff_CumController) as Hediff_CumController;
+
+ quat.ToAngleAxis(out float angle, out Vector3 axis);//angle changes when pawn is e.g. downed
+
+ //adjustments if the pawn is sleeping in a bed:
+ bool inBed = false;
+ Building_Bed building_Bed = pawn.CurrentBed();
+ if (building_Bed != null)
+ {
+ inBed = !building_Bed.def.building.bed_showSleeperBody;
+ AltitudeLayer altLayer = (AltitudeLayer)Mathf.Max((int)building_Bed.def.altitudeLayer, 15);
+ Vector3 vector2 = pawn.Position.ToVector3ShiftedWithAltitude(altLayer);
+ vector2.y += 0.02734375f+0.01f;//just copied from rimworld code+0.01f
+ drawLoc.y = vector2.y;
+ }
+
+ h.DrawCum(drawLoc, quat, layer == PawnOverlayDrawer.OverlayLayer.Head, angle);
+ }
+ }
+
+ }
+ }
+}
diff --git a/1.4/Source/Mod/RimJobWorldCum.csproj b/1.4/Source/Mod/RimJobWorldCum.csproj
new file mode 100644
index 0000000..6cff0eb
--- /dev/null
+++ b/1.4/Source/Mod/RimJobWorldCum.csproj
@@ -0,0 +1,79 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {3FC2D442-19B8-4CF9-9D35-CD13B6AC7B28}
+ Library
+ rjwcum
+ RimJobWorldCum
+ v4.7.2
+ 512
+
+
+ AnyCPU
+ true
+ full
+ false
+ ..\..\Assemblies\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ ..\..\Assemblies\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\Lib.Harmony.2.2.2\lib\net472\0Harmony.dll
+
+
+ ..\..\..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll
+ False
+
+
+ ..\..\..\..\..\..\..\workshop\content\294100\818773962\v1.4\Assemblies\HugsLib.dll
+ False
+
+
+ ..\..\..\..\rjw\1.4\Assemblies\RJW.dll
+ False
+
+
+
+
+ ..\..\..\..\..\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll
+ False
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/1.4/Source/Mod/Textures.cs b/1.4/Source/Mod/Textures.cs
new file mode 100644
index 0000000..6ec85f6
--- /dev/null
+++ b/1.4/Source/Mod/Textures.cs
@@ -0,0 +1,58 @@
+using Verse;
+using UnityEngine;
+//using Multiplayer.API;
+
+namespace rjwcum
+{
+ [StaticConstructorOnStartup]
+ public static class CumTextures
+ {
+ //UI:
+ public static readonly Texture2D CumIcon_little = ContentFinder.Get("CumIcon_little", true);
+ public static readonly Texture2D CumIcon_some = ContentFinder.Get("CumIcon_some", true);
+ public static readonly Texture2D CumIcon_dripping = ContentFinder.Get("CumIcon_dripping", true);
+ public static readonly Texture2D CumIcon_drenched = ContentFinder.Get("CumIcon_drenched", true);
+
+ //on pawn:
+ public static readonly Material cumSplatch1 = MaterialPool.MatFrom("splatch_1", ShaderDatabase.Cutout);
+ public static readonly Material cumSplatch2 = MaterialPool.MatFrom("splatch_2", ShaderDatabase.Cutout);
+ public static readonly Material cumSplatch3 = MaterialPool.MatFrom("splatch_3", ShaderDatabase.Cutout);
+ public static readonly Material cumSplatch4 = MaterialPool.MatFrom("splatch_4", ShaderDatabase.Cutout);
+ public static readonly Material cumSplatch5 = MaterialPool.MatFrom("splatch_5", ShaderDatabase.Cutout);
+ public static readonly Material cumSplatch6 = MaterialPool.MatFrom("splatch_6", ShaderDatabase.Cutout);
+ public static readonly Material cumSplatch7 = MaterialPool.MatFrom("splatch_7", ShaderDatabase.Cutout);
+ public static readonly Material cumSplatch8 = MaterialPool.MatFrom("splatch_8", ShaderDatabase.Cutout);
+ public static readonly Material cumSplatch9 = MaterialPool.MatFrom("splatch_9", ShaderDatabase.Cutout);
+
+
+ //[SyncMethod]
+ public static Material pickRandomSplatch()
+ {
+ //Rand.PopState();
+ //Rand.PushState(RJW_Multiplayer.PredictableSeed());
+ int rand = Rand.Range(0, 8);
+ switch (rand)
+ {
+ case 0:
+ return cumSplatch1;
+ case 1:
+ return cumSplatch2;
+ case 2:
+ return cumSplatch3;
+ case 3:
+ return cumSplatch4;
+ case 4:
+ return cumSplatch5;
+ case 5:
+ return cumSplatch6;
+ case 6:
+ return cumSplatch7;
+ case 7:
+ return cumSplatch8;
+ case 8:
+ return cumSplatch9;
+ }
+ return null;
+ }
+ }
+}
diff --git a/1.4/Source/Mod/WorkGivers/WorkGiver_CleanSelf.cs b/1.4/Source/Mod/WorkGivers/WorkGiver_CleanSelf.cs
new file mode 100644
index 0000000..533b827
--- /dev/null
+++ b/1.4/Source/Mod/WorkGivers/WorkGiver_CleanSelf.cs
@@ -0,0 +1,76 @@
+using RimWorld;
+using Verse;
+using Verse.AI;
+using rjw;
+
+namespace rjwcum
+{
+ public class WorkGiver_CleanSelf : WorkGiver_Scanner
+ {
+ public override PathEndMode PathEndMode
+ {
+ get
+ {
+ return PathEndMode.InteractionCell;
+ }
+ }
+
+ public override Danger MaxPathDanger(Pawn pawn)
+ {
+ return Danger.Deadly;
+ }
+
+ public override ThingRequest PotentialWorkThingRequest
+ {
+ get
+ {
+ return ThingRequest.ForGroup(ThingRequestGroup.Pawn);
+ }
+ }
+
+ //conditions for self-cleaning job to be available
+ public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ if (xxx.DubsBadHygieneIsActive && CumBase.dubsDBH_block_CleanSelf) // selfclean only in shower/bath etc
+ return false;
+
+ if (pawn != t)
+ return false;
+
+ if (!pawn.CanReserve(t, 1, -1, null, forced))
+ return false;
+
+ if (pawn.IsDesignatedHero())
+ {
+ if (!forced && CumBase.manual_hero_CleanSelf)
+ {
+ //ModLog.Message("WorkGiver_CleanSelf::not player interaction for hero, exit");
+ return false;
+ }
+ if (!pawn.IsHeroOwner())
+ {
+ //ModLog.Message("WorkGiver_CleanSelf::player interaction for not owned hero, exit");
+ return false;
+ }
+ }
+
+ Hediff hediff = pawn.health.hediffSet.hediffs.Find(x => (x.def == HediffDefOf.Hediff_CumController));
+ if (hediff == null)
+ return false;
+
+ int minAge = 3 * 2500;//3 hours in-game must have passed
+ if (!forced)
+ if (!(hediff.ageTicks > minAge))
+ {
+ //ModLog.Message("WorkGiver_CleanSelf:: 3 hours in-game must pass to self-clean, exit");
+ return false;
+ }
+ return true;
+ }
+
+ public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
+ {
+ return JobMaker.MakeJob(JobDefOf.CleanSelf);
+ }
+ }
+}
diff --git a/1.4/Source/Mod/packages.config b/1.4/Source/Mod/packages.config
new file mode 100644
index 0000000..c5bef78
--- /dev/null
+++ b/1.4/Source/Mod/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/1.4/Source/Properties/AssemblyInfo.cs b/1.4/Source/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..78fe39f
--- /dev/null
+++ b/1.4/Source/Properties/AssemblyInfo.cs
@@ -0,0 +1,32 @@
+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("RimJobWorld Cum")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("RimJobWorld Cum")]
+[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("c2825019-7f0b-456d-85a3-479c1a2a8805")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]
diff --git a/1.4/Source/mod.sln b/1.4/Source/mod.sln
new file mode 100644
index 0000000..ee41d20
--- /dev/null
+++ b/1.4/Source/mod.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30907.101
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RimJobWorldCum", "Mod\RimJobWorldCum.csproj", "{3FC2D442-19B8-4CF9-9D35-CD13B6AC7B28}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3FC2D442-19B8-4CF9-9D35-CD13B6AC7B28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3FC2D442-19B8-4CF9-9D35-CD13B6AC7B28}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3FC2D442-19B8-4CF9-9D35-CD13B6AC7B28}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3FC2D442-19B8-4CF9-9D35-CD13B6AC7B28}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {5A0C2732-36A9-4ACA-807E-019E02C37E10}
+ EndGlobalSection
+EndGlobal
diff --git a/1.4/Textures/CumIcon_drenched.png b/1.4/Textures/CumIcon_drenched.png
new file mode 100644
index 0000000..842f804
Binary files /dev/null and b/1.4/Textures/CumIcon_drenched.png differ
diff --git a/1.4/Textures/CumIcon_dripping.png b/1.4/Textures/CumIcon_dripping.png
new file mode 100644
index 0000000..daea570
Binary files /dev/null and b/1.4/Textures/CumIcon_dripping.png differ
diff --git a/1.4/Textures/CumIcon_little.png b/1.4/Textures/CumIcon_little.png
new file mode 100644
index 0000000..f7abde3
Binary files /dev/null and b/1.4/Textures/CumIcon_little.png differ
diff --git a/1.4/Textures/CumIcon_some.png b/1.4/Textures/CumIcon_some.png
new file mode 100644
index 0000000..d36e3f1
Binary files /dev/null and b/1.4/Textures/CumIcon_some.png differ
diff --git a/1.4/Textures/splatch_1.png b/1.4/Textures/splatch_1.png
new file mode 100644
index 0000000..1e930d3
Binary files /dev/null and b/1.4/Textures/splatch_1.png differ
diff --git a/1.4/Textures/splatch_2.png b/1.4/Textures/splatch_2.png
new file mode 100644
index 0000000..514a2f1
Binary files /dev/null and b/1.4/Textures/splatch_2.png differ
diff --git a/1.4/Textures/splatch_3.png b/1.4/Textures/splatch_3.png
new file mode 100644
index 0000000..452bc0c
Binary files /dev/null and b/1.4/Textures/splatch_3.png differ
diff --git a/1.4/Textures/splatch_4.png b/1.4/Textures/splatch_4.png
new file mode 100644
index 0000000..7f21495
Binary files /dev/null and b/1.4/Textures/splatch_4.png differ
diff --git a/1.4/Textures/splatch_5.png b/1.4/Textures/splatch_5.png
new file mode 100644
index 0000000..ee101c1
Binary files /dev/null and b/1.4/Textures/splatch_5.png differ
diff --git a/1.4/Textures/splatch_6.png b/1.4/Textures/splatch_6.png
new file mode 100644
index 0000000..b9de710
Binary files /dev/null and b/1.4/Textures/splatch_6.png differ
diff --git a/1.4/Textures/splatch_7.png b/1.4/Textures/splatch_7.png
new file mode 100644
index 0000000..c281066
Binary files /dev/null and b/1.4/Textures/splatch_7.png differ
diff --git a/1.4/Textures/splatch_8.png b/1.4/Textures/splatch_8.png
new file mode 100644
index 0000000..0fe2c43
Binary files /dev/null and b/1.4/Textures/splatch_8.png differ
diff --git a/1.4/Textures/splatch_9.png b/1.4/Textures/splatch_9.png
new file mode 100644
index 0000000..0e6a788
Binary files /dev/null and b/1.4/Textures/splatch_9.png differ
diff --git a/About/About.xml b/About/About.xml
index 94baf15..b3d2aaa 100644
--- a/About/About.xml
+++ b/About/About.xml
@@ -6,6 +6,7 @@
https://gitgud.io/Ed86/rjw-cum
1.3
+ 1.4
rjw.cum