code:
using RimWorld;
using rjw;
using rjw.Modules.Interactions.Helpers;
using rjw.Modules.Interactions.Objects;
using System.Collections.Generic;
using System.Linq;
using Verse;

namespace LicentiaLabs
{
    public static class Licentia
    {
        public static class ThoughtDefs
        {
            public static readonly ThoughtDef GotStretched = DefDatabase<ThoughtDef>.GetNamed("GotStretched");
            public static readonly ThoughtDef GotStretchedKinky = DefDatabase<ThoughtDef>.GetNamed("KinkyGotStretched");
            public static readonly ThoughtDef GotStretchedMasochist = DefDatabase<ThoughtDef>.GetNamed("MasochistGotStretched");
        }

        public static class HediffDefs
        {
            public static readonly HediffDef CervixBruise = HediffDef.Named("CervixBruise");
            public static readonly HediffDef ExtremeProlapse = HediffDef.Named("ExtremeProlapse");
            public static readonly HediffDef Stretched = HediffDef.Named("Stretched");
            public static readonly HediffDef StretchTear = HediffDef.Named("StretchTear");
            public static readonly HediffDef Prolapse = HediffDef.Named("Prolapse");
        }
    }

    public static class LicentiaHelper
    {
        public static readonly IDictionary<SexPartType, BodyPartDef> BodyPartDefBySexPartType = new Dictionary<SexPartType, BodyPartDef>();

        static LicentiaHelper()
        {
            BodyPartDefBySexPartType.Add(SexPartType.Anus, xxx.anusDef);
            BodyPartDefBySexPartType.Add(SexPartType.FemaleBreast, xxx.breastsDef);
            BodyPartDefBySexPartType.Add(SexPartType.FemaleGenital, xxx.genitalsDef);
            BodyPartDefBySexPartType.Add(SexPartType.MaleBreast, xxx.breastsDef);
            BodyPartDefBySexPartType.Add(SexPartType.MaleGenital, xxx.genitalsDef);
        }

        public static bool IsArtificial(Hediff hediff)
        {
            return !(hediff is Hediff_NaturalSexPart);
        }

        public static Hediff TryGetRJWPartHediff(Pawn pawn, SexPartType partType)
        {
            switch (partType)
            {
                case SexPartType.Anus:
                    return TryGetPawnAnusHediff(pawn);

                case SexPartType.FemaleGenital:
                    return TryGetPawnVaginaHediff(pawn);

                case SexPartType.MaleGenital:
                    {
                        var hediffs = TryGetPawnPenisHediff(pawn);
                        if (hediffs.Count > 0)
                        {
                            return hediffs[0];
                        }
                        return null;
                    }
            }

            // default value
            return TryGetPawnBreastHediff(pawn);
        }

        public static List<Hediff> TryGetPawnPenisHediff(Pawn pawn) // used for stretch and futa calculation
        {
            var hediffs = new HashSet<Hediff>();

            var genitalsHediffs = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn));
            if (Genital_Helper.has_penis_fertile(pawn, genitalsHediffs) || Genital_Helper.has_penis_infertile(pawn, genitalsHediffs) || Genital_Helper.has_multipenis(pawn, genitalsHediffs))
            {
                hediffs.AddRange(genitalsHediffs.FindAll((Hediff hediff) => hediff.def.defName.ToLower().Contains("penis")));
                hediffs.AddRange(genitalsHediffs.FindAll((Hediff hediff) => hediff.def.defName.ToLower().Contains("ovipositorf")));
                hediffs.AddRange(genitalsHediffs.FindAll((Hediff hediff) => hediff.def.defName.ToLower().Contains("ovipositorm")));
                hediffs.AddRange(genitalsHediffs.FindAll((Hediff hediff) => hediff.def.defName.ToLower().Contains("tentacle")));
            }
            else if (Genital_Helper.has_ovipositorF(pawn, genitalsHediffs))
            {
                hediffs.AddRange(genitalsHediffs.FindAll((Hediff hediff) => hediff.def.defName.ToLower().Contains("ovipositorf")));
            }

            return hediffs.ToList();
        }

        public static Hediff TryGetPawnVaginaHediff(Pawn pawn) // used for stretch and futa calculation
        {
            Hediff hediff = null;
            var pawnparts = Genital_Helper.get_PartsHediffList(pawn, Genital_Helper.get_genitalsBPR(pawn));

            if (Genital_Helper.has_vagina(pawn, pawnparts))
            {
                hediff = pawnparts.FindAll((Hediff hed) => hed.def.defName.ToLower().Contains("vagina")).InRandomOrder().FirstOrDefault();

                if (hediff == null)
                    hediff = pawnparts.FindAll((Hediff hed) => hed.def.defName.ToLower().Contains("ovipositorf")).InRandomOrder().FirstOrDefault();
            }
            else
            {
                hediff = null;
            }

            return hediff;
        }

        public static Hediff TryGetPawnAnusHediff(Pawn pawn)
        {
            BodyPartRecord Part = Genital_Helper.get_anusBPR(pawn);
            if (Part is null)
                return null;

            return pawn.health.hediffSet.hediffs.FindAll((Hediff hed) =>
                    hed.Part == Part &&
                    (hed is Hediff_NaturalSexPart || hed is Hediff_ArtificialSexPart) &&
                    (hed.def.defName.ToLower().Contains("anus"))).InRandomOrder().FirstOrDefault();
        }

        public static Hediff TryGetPawnBreastHediff(Pawn pawn)
        {
            BodyPartRecord Part = Genital_Helper.get_breastsBPR(pawn);
            if (Part is null)
                return null;

            return pawn.health.hediffSet.hediffs.FindAll((Hediff hed) =>
                    hed.Part == Part &&
                    (hed is Hediff_NaturalSexPart || hed is Hediff_ArtificialSexPart) &&
                    (hed.def.defName.ToLower().Contains("breasts") || hed.def.defName.ToLower().Contains("chest"))
                    ).InRandomOrder().FirstOrDefault();
        }

        public static bool GetPenetrator(SexProps props, out Pawn penetrator, out Pawn penetrated)
        {
            penetrator = null;
            penetrated = null;

            bool initiatorHasPenis = true;
            bool partnerHasPenis = true;
            if (TryGetPawnPenisHediff(props.pawn).EnumerableNullOrEmpty())
            {
                initiatorHasPenis = false;
            }
            if (TryGetPawnPenisHediff(props.partner).EnumerableNullOrEmpty())
            {
                partnerHasPenis = false;
            }

            if (!initiatorHasPenis && !partnerHasPenis)
            {
                return false;
            }

            InteractionWithExtension interaction = InteractionHelper.GetWithExtension(props.dictionaryKey);
            bool initiatorUsesPenis;
            bool partnerUsesPenis;
            if (!props.isReceiver)
            {
                initiatorUsesPenis = interaction?.DominantHasTag(GenitalTag.CanPenetrate) ?? false;
                partnerUsesPenis = interaction?.SubmissiveHasTag(GenitalTag.CanPenetrate) ?? false;
            }
            else
            {
                initiatorUsesPenis = interaction?.SubmissiveHasTag(GenitalTag.CanPenetrate) ?? false;
                partnerUsesPenis = interaction?.DominantHasTag(GenitalTag.CanPenetrate) ?? false;
            }

            if (initiatorHasPenis && initiatorUsesPenis)
            {
                penetrator = props.pawn;
                penetrated = props.partner;
                return true;
            }
            else if (partnerHasPenis && partnerUsesPenis)
            {
                penetrator = props.partner;
                penetrated = props.pawn;
                return true;
            }

            return false;
        }

        public static bool AddSexPart(Pawn pawn, rjw.SexPartType partType, float severity = 0)
        {
            if (pawn == null)
            {
                return false;
            }

            if (PawnExtensions.TryAddRacePart(pawn, partType))
            {
                Hediff hdf = TryGetRJWPartHediff(pawn, partType);
                HediffComp_SexPart chdf = hdf.TryGetComp<HediffComp_SexPart>();
                if (chdf != null)
                {
                    chdf.UpdateSeverity(severity);
                }

                return true;
            }

            string defaultHediffName = "GenericBreasts";
            switch (partType)
            {
                case SexPartType.Anus:
                    defaultHediffName = "GenericAnus";
                    break;

                case SexPartType.FemaleGenital:
                    defaultHediffName = "GenericVagina";
                    break;

                case SexPartType.MaleGenital:
                    defaultHediffName = "GenericPenis";
                    break;
            }

            BodyPartRecord bpr = pawn.RaceProps.body.AllParts.Find(item => item.def == BodyPartDefBySexPartType[partType]);
            HediffDef hdd = DefDatabase<HediffDef>.GetNamed(defaultHediffName);
            Hediff hd = HediffMaker.MakeHediff(hdd, pawn, bpr);
            HediffComp_SexPart chd = hd.TryGetComp<HediffComp_SexPart>();

            if (chd == null)
            {
                return false;
            }

            chd.Init(pawn);
            chd.UpdateSeverity(severity);
            pawn.health.AddHediff(hd);

            return true;
        }

        public static T Clamp<T>(T value, T min, T max) where T : System.IComparable<T>
        {
            if (value.CompareTo(min) < 0) return min;
            else if (value.CompareTo(max) > 0) return max;
            return value;
        }
    }
}


code:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{57515AAF-B37A-4912-A910-4C629FEC734F}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>LicentiaLabs</RootNamespace>
    <AssemblyName>LicentiaLabs</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
    <TargetFrameworkProfile />
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>false</DebugSymbols>
    <DebugType>none</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>..\..\..\Assemblies\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    <PlatformTarget>x64</PlatformTarget>
    <Prefer32Bit>false</Prefer32Bit>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>..\..\..\Assemblies\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <Prefer32Bit>false</Prefer32Bit>
    <PlatformTarget>x64</PlatformTarget>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="0Harmony">
      <HintPath>..\..\..\..\Dyspareunia-master\Source\References\0Harmony.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Assembly-CSharp">
      <HintPath>C:\Steam\steamapps\common\RimWorld\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="RJW">
      <HintPath>..\..\..\..\rjw\1.5\Assemblies\RJW.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Runtime.InteropServices.RuntimeInformation" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
    <Reference Include="UnityEngine">
      <HintPath>C:\Steam\steamapps\common\RimWorld\RimWorldWin64_Data\Managed\UnityEngine.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="UnityEngine.CoreModule">
      <HintPath>C:\Steam\steamapps\common\RimWorld\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
      <Private>False</Private>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="LicentiaHelper.cs" />
    <Compile Include="Patch.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Settings.cs" />
    <Compile Include="Stretch.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="packages.config" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

repo_name: ElToro/licentia

license: unknown


code:
using Verse;

namespace LicentiaLabs {

	public class Comp_LicentiaPart : HediffComp {
	}

	public class CompProperties_LicentiaPart : HediffCompProperties {
		public float stretchVariance = 0f;
		public float genitalSize = 0.5f;
		public float cumAmount = -1f;
		public HediffDef oppositeSexOrgan;
		public HediffDef oppositeBreasts;
		public HediffDef extrudedSexOrgan;
		public HediffDef retractedSexOrgan;

		public CompProperties_LicentiaPart()
		{
			compClass = typeof(Comp_LicentiaPart);
		}
	}
}


code:
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("LicentiaLabs")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Hewlett-Packard")]
[assembly: AssemblyProduct("LicentiaLabs")]
[assembly: AssemblyCopyright("Copyright © Hewlett-Packard 2019")]
[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("57515aaf-b37a-4912-a910-4c629fec734f")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]


code:
using UnityEngine;
using Verse;

namespace LicentiaLabs
{
	public class Settings : ModSettings
	{
		public static bool IsAllowMultiplePenetratorsPerOrifice = true;
		public static bool IsStretchingEnabled = true;
		public static bool IsPermanentStretchEnabled = true;
		public static bool IsStretchTextPlayerFactionVisibleOnly = true;

		public static bool IsDamageEnabled = false;
		public static float DamageMultiplier = 1f;
		public static bool IsProlapseEnabled = false;

		public override void ExposeData()
		{
			Scribe_Values.Look(ref IsAllowMultiplePenetratorsPerOrifice, "IsAllowMultiplePenetratorsPerOrifice", true);
			Scribe_Values.Look(ref IsStretchingEnabled, "IsStretchingEnabled", true);
			Scribe_Values.Look(ref IsPermanentStretchEnabled, "IsPermanentStretchEnabled", true);
			Scribe_Values.Look(ref IsStretchTextPlayerFactionVisibleOnly, "IsStretchTextPlayerFactionVisibleOnly", true);

			Scribe_Values.Look(ref IsDamageEnabled, "IsDamageEnabled", false);
			Scribe_Values.Look(ref DamageMultiplier, "DamageMultiplier", 1f);
			Scribe_Values.Look(ref IsProlapseEnabled, "IsProlapseEnabled", false);
			base.ExposeData();
		}

		private static readonly float height_modifier = 100f;

		public static void DoWindowContents(Rect inRect)
		{
			Rect viewRect = new Rect(0f, 0f, inRect.width - 16f, inRect.height + height_modifier);

            Listing_Standard listingStandard = new Listing_Standard
            {
                maxOneColumn = true,
                ColumnWidth = viewRect.width / 2.05f
            };
            listingStandard.Begin(inRect);
			listingStandard.Gap(4f);
			listingStandard.CheckboxLabeled("Enable multiple penetrators per orifice", ref IsAllowMultiplePenetratorsPerOrifice, "Allow orifices to have a chance of being targeted by more than one penetrator at once, if more are available.");
			listingStandard.Gap(4f);
			listingStandard.CheckboxLabeled("Enable stretching effects", ref IsStretchingEnabled, "Allow orifices to be stretched, which affects colonists' moods.");
			listingStandard.Gap(4f);
			listingStandard.CheckboxLabeled("Enable permanent stretching", ref IsPermanentStretchEnabled, "Allow orifices to be permanently stretched by large penetrators (at least 1.5x size of orifice).");
			listingStandard.Gap(4f);
			listingStandard.CheckboxLabeled("Stretch messages for player faction only:", ref IsStretchTextPlayerFactionVisibleOnly, "Only show stretch messages for player faction");

			listingStandard.Gap(40f);
			listingStandard.CheckboxLabeled("Enable orifice damage", ref IsDamageEnabled, "Allow orifices to be damaged by overly large penetrations.");
			listingStandard.Gap(4f);
			listingStandard.Label("Damage multiplier (ranges from 0.1x to 2x): " + DamageMultiplier);
			DamageMultiplier = listingStandard.Slider(DamageMultiplier, 0.1f, 2f);
			listingStandard.Gap(4f);
			listingStandard.CheckboxLabeled("Enable orifice prolapsing", ref IsProlapseEnabled, "Allow orifices to be prolapsed, which, in extreme cases, may require surgery.");
			listingStandard.End();
		}
	}

	public class LicentiaLabs : Mod
	{
		private readonly Settings settings;

		public LicentiaLabs(ModContentPack content) : base(content)
		{
			settings = GetSettings<Settings>();
		}

		public override string SettingsCategory()
		{
			return "RJW Licentia Labs";
		}

		public override void DoSettingsWindowContents(Rect inRect)
		{
			Settings.DoWindowContents(inRect);
		}
	}
}


code:
using HarmonyLib;
using RimWorld;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Verse;

namespace LicentiaLabs
{
    public static class StretchHelper
    {
        public static void Stretch(SexProps props)
        {
            if (!LicentiaHelper.GetPenetrator(props, out Pawn penetrator, out Pawn recipient))
            {
                // If there is not penetrator involved, then there will be no inflation.
                return;
            }

            if (props.pawn != recipient)
            {
                // RJW calls SatisfyPersonal 2 times with props.pawn pointing to different participant.
                return;
            }

            if (IsElasticised(recipient))
            {
                // Elasticised recipients cannot be stretched.
                return;
            }

            // Collect some information about the act beforehand, namely the dimensions of the hediffs being
            // used for penetrating and for being penetrated.
            // Collect penetrator dimensions.
            var adjustedPenetratorDimensions = GetAdjustedHediffDimensions(penetrator, LicentiaHelper.TryGetPawnPenisHediff(penetrator));
            if (adjustedPenetratorDimensions.EnumerableNullOrEmpty())
            {
                return;
            }

            // Collect orifice dimensions.
            var pawnOrifices = new List<Hediff>();
            if (props.sexType == xxx.rjwSextype.DoublePenetration || props.sexType == xxx.rjwSextype.Vaginal)
            {
                pawnOrifices.Add(LicentiaHelper.TryGetPawnVaginaHediff(recipient));
            }
            if (props.sexType == xxx.rjwSextype.DoublePenetration || props.sexType == xxx.rjwSextype.Anal)
            {
                pawnOrifices.Add(LicentiaHelper.TryGetPawnAnusHediff(recipient));
            }
            if (pawnOrifices.EnumerableNullOrEmpty())
            {
                return;
            }
            var adjustedOrificeDimensions = GetAdjustedHediffDimensions(recipient, pawnOrifices, float.MaxValue / 2);
            if (adjustedOrificeDimensions.EnumerableNullOrEmpty())
            {
                return;
            }

            // Map penetrators (not their dimensions, just the penetrator Hediffs themselves) to orifices.
            var targetMappings = MapOrificesToPenetrators(adjustedPenetratorDimensions.Keys, pawnOrifices);

            // Determine what the new severity increase should be.
            foreach (var mapping in targetMappings)
            {
                var orifice = mapping.Key;
                if (LicentiaHelper.IsArtificial(orifice))
                {
                    // Artificial orifices cannot be stretched.
                    continue;
                }

                // Collect the penetrator dimensions.
                var penetrators = mapping.Value;
                var penetratorDimensions = CalculateTotalPenetratorDimensions(penetrators, adjustedPenetratorDimensions);
                var orificeDimensions = adjustedOrificeDimensions.TryGetValue(orifice);

                var isStretched = DoesStretchingOccur(penetratorDimensions, orificeDimensions, props.isRape, out var isPermanentlyStretched);
                if (!isStretched.Item1 && !isStretched.Item2)
                {
                    // Orifice is larger than penetrators in all dimensions.
                    continue;
                }

                // Find the larger of the permanent severities for the orifice to add to the real
                // orifice severity.
                var lengthPermSeverity = StretchOrifice(recipient, isPermanentlyStretched.Item1, isStretched.Item1, orifice, penetratorDimensions.Item1, orificeDimensions.Item1, out float lengthDamage);
                var girthPermSeverity = StretchOrifice(recipient, isPermanentlyStretched.Item2, isStretched.Item2, orifice, penetratorDimensions.Item2, orificeDimensions.Item2, out float girthDamage);
                var greaterPermSeverity = Math.Max(lengthPermSeverity, girthPermSeverity);
                if (greaterPermSeverity > 0f)
                {
                   float permTotal = orifice.Severity += greaterPermSeverity;
                   HediffComp_SexPart hediffComp = orifice.TryGetComp<HediffComp_SexPart>();
                   hediffComp.UpdateSeverity(permTotal);
                }

                GiveStretchThoughts(recipient);

                if (DamageHelper.IsPawnInvulnerable(recipient) || orifice.Part == null)
                {
                    continue;
                }

                if (Settings.IsDamageEnabled)
                {
                    if (lengthDamage > 0f)
                    {
                        if (Settings.IsDamageEnabled)
                        {
                            if (orifice.def.defName.Contains("Vagina"))
                            {
                                DamageHelper.ApplyDamage(recipient, orifice.Part, Licentia.HediffDefs.CervixBruise, lengthDamage);
                            }
                            else
                            {
                                DamageHelper.ApplyDamage(recipient, orifice.Part, Licentia.HediffDefs.StretchTear, lengthDamage);
                            }
                        }
                    }
                    if (girthDamage > 0f)
                    {
                        DamageHelper.ApplyDamage(recipient, orifice.Part, Licentia.HediffDefs.StretchTear, girthDamage);
                    }
                }

                float higherDamage = Math.Max(lengthDamage, girthDamage);
                if (Settings.IsProlapseEnabled && higherDamage > 0f)
                {
                    // When length damage is below 1.5f, apply a regular prolapse. If over 1.5f, apply an extreme
                    // prolapse. Severity will scale linearly from 0f to 1f for regular prolapse severity, while
                    // extreme prolapse severity will be fixed to 1.0f.
                    if (higherDamage <= 1.5f)
                    {
                        DamageHelper.ApplyDamage(recipient, orifice.Part, Licentia.HediffDefs.Prolapse, higherDamage * 2f / 3f);
                    }
                    else if (higherDamage <= 2.5f)
                    {
                        // To lessen the number of extreme prolapses, the threshold for damage can go up to 2.5f
                        // but the receipient Pawn will receive a regular prolapse instead at max severity.
                        DamageHelper.ApplyDamage(recipient, orifice.Part, Licentia.HediffDefs.Prolapse, 1f);
                    }
                    else
                    {
                        DamageHelper.ApplyDamage(recipient, orifice.Part, Licentia.HediffDefs.ExtremeProlapse, 1f);
                    }
                }
            }
        }

        /// <summary>
        /// Checks to see if the given <c>Pawn</c> is a valid stretch target.
        /// </summary>
        /// <param name="stretchee">the <c>Pawn</c> to be checked</param>
        /// <returns><c>true</c> if the <paramref name="stretchee"/> has used an elasticise serum recently,
        /// <c>false</c> otherwise.</returns>
        public static bool IsElasticised(Pawn stretchee)
        {
            return false;
        }

        /// <summary>
        /// Calculates the lengths and girths of each genital <c>Hediff</c> specified in the <paramref name="hediffs"/>
        /// list. It is assumed that each <c>Hediff</c> belongs to the provided <paramref name="pawn"/>.
        /// If lengths and girths are not available, then <c>Hediff</c> severity will be used as the
        /// substitute.
        /// </summary>
        /// <param name="pawn">the <c>Pawn</c> that is assumed to own all of the <c>Hediff</c>s</param>
        /// <param name="hediffs">the <c>Hediff</c>s containing the <c>Hediff</c>s to be calculated</param>
        /// <param name="defaultValue">the value to be used if no dimension value could be obtained</param>
        /// <returns>A Dictionary of <c>Hediff</c>s to tuples of the <c>Hediff</c>s' lengths and girths in
        /// that order.</returns>
        public static Dictionary<Hediff, (float, float)> GetAdjustedHediffDimensions(Pawn pawn, IEnumerable<Hediff> hediffs, float defaultValue = float.MinValue)
        {
            Dictionary<Hediff, (float, float)> hediffDimensions = new Dictionary<Hediff, (float, float)>();

            // Theoretically, a Pawn could have both enlargement and diminishment effects active at the same time.
            var adjustmentValue = 0f;

            // Calculate the length and girth of each Hediff.
            foreach (var hediff in hediffs)
            {
                // Some HediffComps have varying stretch amounts.
                var stretchVariance = hediff.def.CompProps<CompProperties_LicentiaPart>()?.stretchVariance;
                if (stretchVariance.HasValue)
                {
                    adjustmentValue += Rand.Range(0, (float)stretchVariance) / 2f;
                }

                // Try to use length and girth if at all possible, and use Severity only as a fallback.
                HediffComp_SexPart rjwHediff = hediff.TryGetComp<HediffComp_SexPart>();
                if (rjwHediff != null)
                {
                    if (!PartSizeCalculator.TryGetLength(hediff, out float length)) length = defaultValue == float.MinValue ? hediff.Severity : defaultValue;
                    length += adjustmentValue;
                    if (!PartSizeCalculator.TryGetGirth(hediff, out float girth)) girth = defaultValue == float.MinValue ? hediff.Severity : defaultValue;
                    girth += adjustmentValue;
                    if (length < 0 || girth < 0)
                    {
                        continue;
                    }
                    hediffDimensions.Add(hediff, (length, girth));
                }
                else
                {
                    if (hediff.Severity < 0)
                    {
                        continue;
                    }
                    hediffDimensions.Add(hediff, (hediff.Severity, hediff.Severity));
                }
            }

            return hediffDimensions;
        }

        /// <summary>
        /// Tries to pair penetrators with orifices. If there are more penetrators than there are orifices,
        /// then the targets will be doubled up and later may have a chance of multiple penetrators targeting
        /// the same orifice, increasing effective girth when calculated for stretching.
        /// </summary>
        /// <param name="penetrators">the penetrator <c>Hediff</c>s</param>
        /// <param name="orifices">the orifice <c>Hediff</c>s; count must be greater than 0</param>
        /// <returns>A Dictionary mapping orifice <c>Hediff</c>s to one or more penetrator <c>Hediff</c>s</returns>
        public static Dictionary<Hediff, List<Hediff>> MapOrificesToPenetrators(IEnumerable<Hediff> penetrators, IList<Hediff> orifices)
        {
            var penetratorList = penetrators.ToList();
            penetratorList.Shuffle();

            var output = new Dictionary<Hediff, List<Hediff>>();
            var nOrifices = orifices.Count();
            for (int ii = 0; ii < penetratorList.Count(); ++ii)
            {
                var orifice = orifices[ii % nOrifices];
                if (!output.TryGetValue(orifice, out List<Hediff> mappedPenetrators))
                {
                    mappedPenetrators = new List<Hediff>();
                    output.Add(orifice, mappedPenetrators);
                }
                var penetrator = penetratorList[ii];
                mappedPenetrators.Add(penetrator);

                if (ii == nOrifices - 1 && !Settings.IsAllowMultiplePenetratorsPerOrifice)
                {
                    break;
                }
            }
            return output;
        }

        /// <summary>
        /// Determines what the final total penetrator dimensions will be. The maximum penetrator length
        /// and some form of summation of girths will be used, meaning that the overall penetrator dimensions
        /// will be that of some aggregate penetrator roughly resembling all of the penetrators used.
        /// </summary>
        /// <param name="penetrators">the <c>Hediff</c>s performing the penetrating</param>
        /// <param name="penetratorDimensions">the Dictionary that will serve as a lookup for dimensions
        /// of each of the <paramref name="penetrators"/></param>
        /// <returns>A tuple of the aggregate penetrator's length and girth in that order.</returns>
        public static (float, float) CalculateTotalPenetratorDimensions(IEnumerable<Hediff> penetrators, Dictionary<Hediff, (float, float)> penetratorDimensions)
        {
            var length = 0f;
            var totalGirth = 0f;
            foreach (var penetrator in penetrators)
            {
                var dimensions = penetratorDimensions.TryGetValue(penetrator);
                if (length == 0f || totalGirth == 0f || true/*Rand.Range(0f, 1f) > 0.5f*/)
                {
                    length = Math.Max(length, dimensions.Item1);
                    totalGirth += dimensions.Item2;
                }
            }
            return (length, totalGirth);
        }

        /// <summary>
        /// Determines based on preset thresholds whether the orifice being penetrated should be stretched
        /// or not.
        /// </summary>
        /// <param name="penetrator">The aggregate length and girth of the penetrator</param>
        /// <param name="orifice">The length and girth of the receiving orifice</param>
        /// <param name="isRape">lowers the threshold for stretching effects from simple comparison to
        /// modifying the penetrating dimensions to 125% their actual dimensions to simulate the damaging
        /// effects of non-consensual intercourse.</param>
        /// <param name="isPermanentlyStretched">Contains whether the length or girth are valid stretching
        /// candidates</param>
        /// <returns>Whether there was any stretching at all in either length or girth directions.</returns>
        public static (bool, bool) DoesStretchingOccur((float, float) penetrator, (float, float) orifice, bool isRape, out (bool, bool) isPermanentlyStretched)
        {
            // Check for stretching length-wise.
            bool isLengthStretched = penetrator.Item1 * (isRape ? 1.25f : 1f) > orifice.Item1;
            bool isLengthStretchedPerm = Settings.IsPermanentStretchEnabled && penetrator.Item1 * (isRape ? 1.25f : 1f) > orifice.Item1 * 1.5;

            // Check for stretching girth-wise.
            bool isGirthStretched = penetrator.Item2 * (isRape ? 1.25f : 1f) > orifice.Item2;
            bool isGirthStretchedPerm = Settings.IsPermanentStretchEnabled && penetrator.Item2 * (isRape ? 1.25f : 1f) > orifice.Item2 * 1.5;

            isPermanentlyStretched = (isLengthStretchedPerm, isGirthStretchedPerm);
            return (isLengthStretched, isGirthStretched);
        }

        /// <summary>
        /// Adds a "Stretched" <c>Hediff</c> to the orifice currently being stretched. Also returns the
        /// severity increase for the orifice if permanent stretching has taken place and is enabled. One
        /// of the two parameters <paramref name="isPermanentlyStretched"/> or <paramref name="isStretched"/>
        /// must be true for this function to have any effect.
        /// </summary>
        /// <param name="penetratedPawn">the <c>Pawn</c> receiving the penetration</param>
        /// <param name="isPermanentlyStretched">if true, then permanent stretching is to be performed</param>
        /// <param name="isStretched">if true, then temporary stretching is to be performed</param>
        /// <param name="orifice">the orifice <c>Hediff</c> being stretched</param>
        /// <param name="penetratorDimension">the current dimension being checked (length or girth) for
        /// the penetrator</param>
        /// <param name="orificeDimension">the current dimension being checked (length or girth) for
        /// the receiving orifice</param>
        /// <param name="uniqueDamage">the damage to be uniquely received by the penetrated orifice; scales
        /// from 0f to 2f when the adjusted dimension ratio between penetrator and orifice was >150%.</param>
        /// <returns></returns>
        public static float StretchOrifice(Pawn penetratedPawn, bool isPermanentlyStretched, bool isStretched, Hediff orifice, float penetratorDimension, float orificeDimension, out float uniqueDamage)
        {
            float severityIncrease = 0f;
            Hediff hediff = null;
            if (isPermanentlyStretched)
            {
                // Attempt to increase the severity of the stretched orifice, as that is the only way that the
                // orifice can increase in size (and prevent stretching again).
                // TODO: Could use an algorithm for directly converting dimension differences into severity.
                var ratio = Math.Min(penetratorDimension / orificeDimension / 1.5f, 0.12f) * Rand.Range(0.85f, 1.15f); // The maximum an orifice can stretch by is 10%-14% its current severity.
                GenderHelper.ChangeSex(penetratedPawn, () =>
                {
                    var stretchAmount = orifice.Severity * ratio;
                    severityIncrease = LicentiaHelper.Clamp(stretchAmount, 0.05f, 0.25f); // Clamp the actual severity increase to prevent rapid changes.
                });

                // Stretching severity will scale from 25% (lasting one day) to 100% (lasting 4 days) depending
                // on how badly the orifice was stretched.
                hediff = HediffMaker.MakeHediff(Licentia.HediffDefs.Stretched, penetratedPawn);
                hediff.Severity = 0.25f + LicentiaHelper.Clamp(0.75f * (ratio / 0.12f), 0.25f, 0.75f);
                penetratedPawn.health.AddHediff(hediff, orifice.Part);

                if (!Settings.IsStretchTextPlayerFactionVisibleOnly || (penetratedPawn.Faction?.IsPlayer ?? false))
                {
                    Messages.Message("LL_OrificeStretched".Translate(penetratedPawn, orifice.def.defName.ToLower()), penetratedPawn, MessageTypeDefOf.SilentInput);
                }
            }
            else if (isStretched)
            {
                // If any stretching occurred at all, there will be a minimum of 25% severity for stretching. Mild
                // stretching will last one day.
                hediff = HediffMaker.MakeHediff(Licentia.HediffDefs.Stretched, penetratedPawn);
                hediff.Severity = 0.25f;
                penetratedPawn.health.AddHediff(hediff, orifice.Part);
            }
            else
            {
                // For some reason, this function was called without any stretching taking place.
                uniqueDamage = 0f;
                return 0f;
            }

            // Stretch severity ranges between 0.25f and 1.0f, which means the severity of the damage can
            // range between 0f (regular stretching does not cause tears) and 2.0f, which is 25% of the orifice
            // HP for a >150% stretch.
            // Formula here is a linear relationship: y = 2.6666...x + 0.6666...
            uniqueDamage = Settings.DamageMultiplier * ((2f + 2f / 3f) * hediff?.Severity ?? 0f) - (2f / 3f);
            return severityIncrease;
        }

        /// <summary>
        /// Gives the <paramref name="stretchee"/> a thought about having been stretched. For <c>Pawn</c>s
        /// that enjoy pain or the act of stretching itself, a positive thought will be gained. Otherwise,
        /// the <c>Pawn</c> will gain a negative thought.
        /// </summary>
        /// <param name="stretchee">the <c>Pawn</c> that was stretched and will be receiving a thought</param>
        public static void GiveStretchThoughts(Pawn stretchee)
        {
            if (xxx.is_animal(stretchee))
            {
                return;
            }

            string pawn_quirks = CompRJW.Comp(stretchee).quirks.ToString();
            if (pawn_quirks.Contains("Impregnation fetish") ||
                pawn_quirks.Contains("Teratophile") ||
                pawn_quirks.Contains("Incubator") ||
                pawn_quirks.Contains("Breeder") ||
                xxx.is_zoophile(stretchee))
            {
                stretchee.needs.mood.thoughts.memories.TryGainMemory(Licentia.ThoughtDefs.GotStretchedKinky);
            }
            else if (xxx.is_masochist(stretchee))
            {
                stretchee.needs.mood.thoughts.memories.TryGainMemory(Licentia.ThoughtDefs.GotStretchedMasochist);
            }
            else
            {
                stretchee.needs.mood.thoughts.memories.TryGainMemory(Licentia.ThoughtDefs.GotStretched);
            }
        }
    }

    public static class DamageHelper
    {
        public static bool IsPawnInvulnerable(Pawn pawn)
        {
            return false;
        }

        public static void ApplyDamage(Pawn pawn, BodyPartRecord part, HediffDef damageType, float damage)
        {
            if (part == null)
            {
                return;
            }

            var hediff = HediffMaker.MakeHediff(damageType, pawn);
            hediff.Severity = damage;
            pawn.health.AddHediff(hediff, part);
        }
    }

    [StaticConstructorOnStartup]
    internal static class First
    {
        static First()
        {
            var har = new Harmony("LL");
            har.PatchAll(Assembly.GetExecutingAssembly());
        }
    }

    [HarmonyPatch(typeof(SexUtility))]
    [HarmonyPatch("SatisfyPersonal")]
    [HarmonyPatch(new Type[] { typeof(SexProps), typeof(float) })]
    public static class StretchPatch
    {
        [HarmonyPostfix]
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Parameter is provided by RJW through Harmony.")]
        public static void SatisfyPersonal(SexProps props, float satisfaction)
        {
            if (props.isCoreLovin || !Settings.IsStretchingEnabled || props.pawn?.Dead == true || props.partner == null || props.partner.Dead == true)
            {
                return;
            }

            StretchHelper.Stretch(props);
        }
    }
}


code:
<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Lib.Harmony" version="2.0.2" targetFramework="net472" />
</packages>


code:
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs     diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln       merge=binary
#*.csproj    merge=binary
#*.vbproj    merge=binary
#*.vcxproj   merge=binary
#*.vcproj    merge=binary
#*.dbproj    merge=binary
#*.fsproj    merge=binary
#*.lsproj    merge=binary
#*.wixproj   merge=binary
#*.modelproj merge=binary
#*.sqlproj   merge=binary
#*.wwaproj   merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg   binary
#*.png   binary
#*.gif   binary

###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc   diff=astextplain
#*.DOC   diff=astextplain
#*.docx  diff=astextplain
#*.DOCX  diff=astextplain
#*.dot   diff=astextplain
#*.DOT   diff=astextplain
#*.pdf   diff=astextplain
#*.PDF   diff=astextplain
#*.rtf   diff=astextplain
#*.RTF   diff=astextplain

path: .gitattributes


code:
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore

# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/

# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# Visual Studio 2017 auto generated files
Generated\ Files/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# Benchmark Results
BenchmarkDotNet.Artifacts/

# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/

# StyleCop
StyleCopReport.xml

# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# Visual Studio Trace Files
*.e2e

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json

# Visual Studio code coverage results
*.coverage
*.coveragexml

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs

# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak

# SQL Server files
*.mdf
*.ldf
*.ndf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- Backup*.rdl

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# JetBrains Rider
.idea/
*.sln.iml

# CodeRush personal settings
.cr/personal

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config

# Tabs Studio
*.tss

# Telerik's JustMock configuration file
*.jmconfig

# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs

# OpenCover UI analysis results
OpenCover/

# Azure Stream Analytics local run output
ASALocalRun/

# MSBuild Binary and Structured Log
*.binlog

# NVidia Nsight GPU debugger configuration file
*.nvuser

# MFractors (Xamarin productivity tool) working folder
.mfractor/

# Local History for Visual Studio
.localhistory/

# BeatPulse healthcheck temp database
healthchecksdb
/Source/Patches/PawnAnimationPatches/HarmonyPatch_Pawn_DrawTracker.cs
/Source/Patches/PawnAnimationPatches/HarmonyPatch_PawnRotation.cs
/Source/Patches/PawnAnimationPatches/HarmonyPatch_PawnRenderer.cs
/Source/Patches/OtherModPatches/HarmonyPatch_ShowHairWithHats.cs
/Source/Patches/OtherModPatches/HarmonyPatch_FacialAnimation.cs
/Source/Patches/OtherModPatches/HarmonyPatch_DontShaveYourHead.cs
/Source/Patches/OtherModPatches/HarmonyPatch_CSL.cs
/Source/Patches/OtherModPatches/HarmonyPatch_AlienRace.cs
/Source/Patches/ThingAnimationPatches/HarmonyPatch_ThingDrawAt.cs
/Defs/AnimationDefs/Animations_SexToys.xml

path: .gitignore