support for 1.4

This commit is contained in:
Ed86 2022-10-26 12:25:26 +03:00
parent 358b0d4f2e
commit b943cb55c6
39 changed files with 1912 additions and 1 deletions

View File

@ -40,7 +40,7 @@
<Private>False</Private>
</Reference>
<Reference Include="HugsLib">
<HintPath>..\..\..\..\..\..\..\workshop\content\294100\818773962\v1.2\Assemblies\HugsLib.dll</HintPath>
<HintPath>..\..\..\..\..\..\..\workshop\content\294100\818773962\v1.3\Assemblies\HugsLib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="RJW">

Binary file not shown.

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<!--master hediff adding up the smaller Cum splatches-->
<HediffDef>
<defName>Hediff_CumController</defName>
<hediffClass>rjwcum.Hediff_CumController</hediffClass>
<label>Cum</label>
<description>Covered in cum.</description>
<makesSickThought>false</makesSickThought>
<initialSeverity>0.01</initialSeverity>
<maxSeverity>1</maxSeverity>
<!--<injuryProps>-->
<!--<canMerge>true</canMerge>--><!-- this might not even be required-->
<!--</injuryProps>-->
<!--<scenarioCanAdd>true</scenarioCanAdd>-->
<isBad>false</isBad>
<tendable>false</tendable>
<stages>
<li>
<label>minor</label>
<becomeVisible>false</becomeVisible>
</li>
<li>
<minSeverity>0.3</minSeverity>
<label>little</label>
<statOffsets>
<Vulnerability>0.2</Vulnerability>
<SocialImpact>-0.1</SocialImpact>
</statOffsets>
</li>
<li>
<minSeverity>0.6</minSeverity>
<label>extensive</label>
<statOffsets>
<Vulnerability>0.3</Vulnerability>
<SocialImpact>-0.3</SocialImpact>
</statOffsets>
</li>
<li>
<minSeverity>0.8</minSeverity>
<label>full</label>
<statOffsets>
<Vulnerability>-0.1</Vulnerability><!--pawns prefer victims not being completely drenched-->
<SocialImpact>-0.5</SocialImpact>
</statOffsets>
</li>
</stages>
</HediffDef>
</Defs>

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<HediffDef Name="Hediff_Cum_Base" Abstract="True">
<hediffClass>rjwcum.Hediff_Cum</hediffClass>
<defName>Hediff_Cum</defName>
<label>Cum</label>
<labelNoun>cum</labelNoun>
<description>cum.</description>
<labelNounPretty>cum on {1}</labelNounPretty>
<defaultLabelColor>(0.95,0.95,0.95)</defaultLabelColor>
<isBad>false</isBad>
<tendable>false</tendable>
<makesSickThought>false</makesSickThought>
<makesAlert>false</makesAlert>
<maxSeverity>1</maxSeverity>
<initialSeverity>0.001</initialSeverity>
<injuryProps>
<canMerge>true</canMerge>
</injuryProps>
<stages>
<li>
<label>little</label>
</li>
<li>
<minSeverity>0.25</minSeverity>
<label>some</label>
</li>
<li>
<minSeverity>0.5</minSeverity>
<label>dripping</label>
</li>
<li>
<minSeverity>0.8</minSeverity>
<label>drenched</label>
</li>
</stages>
<comps>
<li Class="HediffCompProperties_SelfHeal">
<!--0.01*100*1800/60.0000-->
<healIntervalTicksStanding>1800</healIntervalTicksStanding><!-- 1 day = 60.000 ticks -->
<healAmount>0.01</healAmount><!--dries by itself, completely drying from 1.0 to 0.0 takes ~72h-->
</li>
</comps>
</HediffDef>
<HediffDef ParentName="Hediff_Cum_Base">
<defName>Hediff_Cum</defName>
<label>Cum</label>
<labelNoun>cum</labelNoun>
<description>cum</description>
<labelNounPretty>cum on {1}</labelNounPretty>
<defaultLabelColor>(0.95,0.95,0.95)</defaultLabelColor>
</HediffDef>
<HediffDef ParentName="Hediff_Cum_Base">
<defName>Hediff_InsectSpunk</defName>
<description>Insect spunk.</description>
<label>insect spunk</label>
<labelNoun>insect spunk</labelNoun>
<labelNounPretty>insect spunk on {1}</labelNounPretty>
<defaultLabelColor>(0.6,0.83,0.35)</defaultLabelColor>
</HediffDef>
<HediffDef ParentName="Hediff_Cum_Base">
<defName>Hediff_MechaFluids</defName>
<description>Mechanoid fluids.</description>
<label>mechanoid fluids</label>
<labelNoun>mechanoid fluids</labelNoun>
<labelNounPretty>mecha fluids on {1}</labelNounPretty>
<defaultLabelColor>(0.37,0.71,0.82)</defaultLabelColor>
</HediffDef>
</Defs>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<JobDef>
<defName>CleanSelf</defName>
<driverClass>rjwcum.JobDriver_CleanSelf</driverClass>
<reportString>cleaning self</reportString>
<casualInterruptible>true</casualInterruptible>
</JobDef>
</Defs>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<WorkGiverDef>
<defName>CleanSelf</defName>
<label>clean self</label>
<giverClass>rjwcum.WorkGiver_CleanSelf</giverClass>
<workType>Cleaning</workType>
<verb>clean self</verb>
<gerund>cleaning self</gerund>
<scanCells>false</scanCells>
<priorityInType>11</priorityInType><!-- slightly higher than normal cleaning (relatively unimportant)-->
<requiredCapacities>
<li>Manipulation</li>
</requiredCapacities>
</WorkGiverDef>
</Defs>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>
<debug>Debug</debug>
<debug_desc></debug_desc>
<cum_on_body>Enable cum on body (Hediffs)</cum_on_body>
<cum_on_body_desc>Enables cum hediffs in health tab.</cum_on_body_desc>
<cum_on_body_amount>Adjust cum amount on body</cum_on_body_amount>
<cum_on_body_amount_desc>All cum applied to pawn bodies will be multiplied by this amount. </cum_on_body_amount_desc>
<cum_overlays>Enable cum overlays (Visuals)</cum_overlays>
<cum_overlays_desc>Enables cum overlay for pawn drawer(may have conflicts with mods that change pawn appearance and other issues).\nRequires Cum on body option enabled.</cum_overlays_desc>
<manual_hero_CleanSelf>Hero manual CleanSelf</manual_hero_CleanSelf>
<manual_hero_CleanSelf_desc>Hero pawn will only clean cum from body if manually told so.</manual_hero_CleanSelf_desc>
<dubsDBH_block_CleanSelf>DubsBadHygiene block CleanSelf</dubsDBH_block_CleanSelf>
<dubsDBH_block_CleanSelf_desc>If DubsBadHygiene installed, pawns will only clean cum from body in buckets, showers, baths, hottubs etc.</dubsDBH_block_CleanSelf_desc>
</LanguageData>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Patch>
<Operation Class="PatchOperationFindMod">
<mods>
<li>[NL] Facial Animation - WIP</li>
</mods>
<match Class="PatchOperationSequence">
<success>Always</success>
<operations>
<li Class="PatchOperationAdd">
<xpath>/Defs/FacialAnimation.FaceAnimationDef[defName="Wear" or defName="Wear2" or defName="Wear3"]/targetJobs</xpath>
<success>Always</success>
<value>
<li>CleanSelf</li>
</value>
</li>
</operations>
</match>
</Operation>
</Patch>

53
1.4/Source/Mod/CumBase.cs Normal file
View File

@ -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<bool> debug;
public static SettingHandle<bool> cum_on_body;
public static SettingHandle<bool> cum_overlays;
public static SettingHandle<float> cum_on_body_amount;
public static SettingHandle<bool> manual_hero_CleanSelf;
public static SettingHandle<bool> 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);
}
}
}

620
1.4/Source/Mod/CumHelper.cs Normal file
View File

@ -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<key, values> splatchAdjust;//saves x (horizontal) and z (vertical) adjustments of texture positiion for each unique combination of bodyType and bodyPart
public static readonly Dictionary<key_layer, values_layer> 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<key, values>();
//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<key_layer, values_layer>();
//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<BodyPartDef> getAllowedBodyParts()
{
List<BodyPartDef> allowedParts = new List<BodyPartDef>();
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<BodyPartRecord> getAvailableBodyParts(Pawn pawn)
{
//get all non-missing body parts:
IEnumerable<BodyPartRecord> 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<BodyPartDef> filterParts = CumHelper.getAllowedBodyParts();
IEnumerable<BodyPartRecord> 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<Hediff> giverparts;
List<Hediff> pawnparts = pawn.GetGenitalsList();
List<Hediff> 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<rjw.CompHediffBodyPart>();
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<BodyPartRecord> targetParts = new List<BodyPartRecord>();//which to apply cum on
IEnumerable<BodyPartRecord> 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<BodyPartRecord>(out randomPart);
targetParts.Add(randomPart);
break;
case rjw.xxx.rjwSextype.Handjob:
//random part:
availableParts.TryRandomElement<BodyPartRecord>(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<BodyPartRecord>(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<Need_Food>();
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<Need_Food>() != 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
}
}
}
}
}
}
}
}

View File

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

View File

@ -0,0 +1,11 @@
using RimWorld;
using Verse;
namespace rjwcum
{
[DefOf]
public static class JobDefOf
{
public static JobDef CleanSelf;
}
}

View File

@ -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<BodyPartRecord> availableParts = CumHelper.getAvailableBodyParts(pawn);//gets all non missing, valid body parts
IEnumerable<BodyPartRecord> filteredParts = availableParts.Where(x => x.def == spillOverTo);//filters again for valid spill target
if (!filteredParts.EnumerableNullOrEmpty())
{
BodyPartRecord spillPart = null;
spillPart = filteredParts.RandomElement<BodyPartRecord>();//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<int>(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;
}
}
}
}

View File

@ -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<Hediff> hediffs_cum;
Dictionary<string, CumSplatch> splatches;
public override void ExposeData()
{
base.ExposeData();
//Scribe_Values.Look<Dictionary<string, CumSplatch>>(ref splatches, "splatches", new Dictionary<string, CumSplatch>()); - causes errors when loading. for now, just make a new dictionary
splatches = new Dictionary<string, CumSplatch>();//instead of loading, just recreate anew
hediffs_cum = new List<Hediff>();
}
public override void PostMake()
{
base.PostMake();
splatches = new Dictionary<string, CumSplatch>();
}
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<string> removeKeys = new List<string>();
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);
}
}
}
}

View File

@ -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<Toil> MakeNewToils()
{
this.FailOn(delegate
{
List<Hediff> 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;
}
}
}

View File

@ -0,0 +1,66 @@
using Verse;
using HarmonyLib;