code:
<?xml version="1.0" encoding="UTF-8"?>
<Defs>
	<VEF.Abilities.AbilityDef ParentName="VPE_PsycastBase">
		<defName>VPE_EnlargeBreasts</defName>
		enlarge breasts
		<description>Enlarge the targets breasts.</description>
		<iconPath>Things/Motes/GrowBreasts</iconPath>
		<castTime>60</castTime>
		<range>24.9</range>
		<fleckOnTarget>PsycastPsychicEffect</fleckOnTarget>
		<targetMode>Pawn</targetMode>
		<abilityClass>VEF.Abilities.Ability_Blank</abilityClass>
		<castSound>PsycastPsychicPulse</castSound>
		<targetingParameters>
			<canTargetSelf>true</canTargetSelf>
			<canTargetAnimals>true</canTargetAnimals>
			<canTargetMechs>false</canTargetMechs>
			<canTargetHumans>true</canTargetHumans>
			<canTargetBuildings>false</canTargetBuildings>
			<canTargetLocations>true</canTargetLocations>
		</targetingParameters>
		<modExtensions>

				<path>RJW_LewdCaster</path>
				<level>2</level>
				<order>1</order>
				<psyfocusCost>0.01</psyfocusCost>
				<entropyGain>12</entropyGain>
				<showCastBubble>true</showCastBubble>
				<psychic>true</psychic>
				<prerequisites>

VPE_EnlargeAnus

				</prerequisites>

		</modExtensions>
	</VEF.Abilities.AbilityDef>

	<VEF.Abilities.AbilityDef ParentName="VPE_PsycastBase">
		<defName>VPE_ShrinkBreasts</defName>
		shrink breasts
		<description>Shrink the targets breasts.</description>
		<iconPath>Things/Motes/ShrinkBreasts</iconPath>
		<castTime>60</castTime>
		<range>24.9</range>
		<fleckOnTarget>PsycastPsychicEffect</fleckOnTarget>
		<targetMode>Pawn</targetMode>
		<abilityClass>VEF.Abilities.Ability_Blank</abilityClass>
		<castSound>PsycastPsychicPulse</castSound>
		<targetingParameters>
			<canTargetSelf>true</canTargetSelf>
			<canTargetAnimals>true</canTargetAnimals>
			<canTargetMechs>false</canTargetMechs>
			<canTargetHumans>true</canTargetHumans>
			<canTargetBuildings>false</canTargetBuildings>
		</targetingParameters>
		<modExtensions>

				<path>RJW_LewdCaster</path>
				<level>2</level>
				<order>2</order>
				<psyfocusCost>0.01</psyfocusCost>
				<entropyGain>12</entropyGain>
				<showCastBubble>true</showCastBubble>
				<psychic>true</psychic>
				<prerequisites>

VPE_ShrinkAnus

				</prerequisites>

		</modExtensions>
	</VEF.Abilities.AbilityDef>

	<VEF.Abilities.AbilityDef ParentName="VPE_PsycastBase">
		<defName>VPE_EnlargeAnus</defName>
		enlarge anus
		<description>Enlarges the targets anus.</description>
		<iconPath>Things/Motes/GrowAnus</iconPath>
		<castTime>60</castTime>
		<range>24.9</range>
		<fleckOnTarget>PsycastPsychicEffect</fleckOnTarget>
		<targetMode>Pawn</targetMode>
		<abilityClass>VEF.Abilities.Ability_Blank</abilityClass>
		<castSound>PsycastPsychicPulse</castSound>
		<targetingParameters>
			<canTargetSelf>true</canTargetSelf>
			<canTargetAnimals>true</canTargetAnimals>
			<canTargetMechs>false</canTargetMechs>
			<canTargetHumans>true</canTargetHumans>
			<canTargetBuildings>false</canTargetBuildings>
		</targetingParameters>
		<modExtensions>

				<path>RJW_LewdCaster</path>
				<level>1</level>
				<order>1</order>
				<psyfocusCost>0.01</psyfocusCost>
				<entropyGain>12</entropyGain>
				<showCastBubble>true</showCastBubble>
				<psychic>true</psychic>

		</modExtensions>
	</VEF.Abilities.AbilityDef>

	<VEF.Abilities.AbilityDef ParentName="VPE_PsycastBase">
		<defName>VPE_ShrinkAnus</defName>
		shrink anus
		<description>Shrinks the targets anus.</description>
		<iconPath>Things/Motes/ShrinkAnus</iconPath>
		<castTime>60</castTime>
		<range>24.9</range>
		<fleckOnTarget>PsycastPsychicEffect</fleckOnTarget>
		<targetMode>Pawn</targetMode>
		<abilityClass>VEF.Abilities.Ability_Blank</abilityClass>
		<castSound>PsycastPsychicPulse</castSound>
		<targetingParameters>
			<canTargetSelf>true</canTargetSelf>
			<canTargetAnimals>true</canTargetAnimals>
			<canTargetMechs>false</canTargetMechs>
			<canTargetHumans>true</canTargetHumans>
			<canTargetBuildings>false</canTargetBuildings>
		</targetingParameters>
		<modExtensions>

				<path>RJW_LewdCaster</path>
				<level>1</level>
				<order>2</order>
				<psyfocusCost>0.01</psyfocusCost>
				<entropyGain>12</entropyGain>
				<showCastBubble>true</showCastBubble>
				<psychic>true</psychic>

		</modExtensions>
	</VEF.Abilities.AbilityDef>

	<VEF.Abilities.AbilityDef ParentName="VPE_PsycastBase">
		<defName>VPE_EnlargeGenitalia</defName>
		enlarge genitalia
		<description>Enlarge the targets genitalia.</description>
		<iconPath>Things/Motes/GrowGenitalia</iconPath>
		<castTime>60</castTime>
		<range>24.9</range>
		<fleckOnTarget>PsycastPsychicEffect</fleckOnTarget>
		<targetMode>Pawn</targetMode>
		<abilityClass>VEF.Abilities.Ability_Blank</abilityClass>
		<castSound>PsycastPsychicPulse</castSound>
		<targetingParameters>
			<canTargetSelf>true</canTargetSelf>
			<canTargetAnimals>true</canTargetAnimals>
			<canTargetMechs>false</canTargetMechs>
			<canTargetHumans>true</canTargetHumans>
			<canTargetBuildings>false</canTargetBuildings>
		</targetingParameters>
		<modExtensions>

				<path>RJW_LewdCaster</path>
				<level>3</level>
				<order>1</order>
				<psyfocusCost>0.01</psyfocusCost>
				<entropyGain>12</entropyGain>
				<showCastBubble>true</showCastBubble>
				<psychic>true</psychic>
				<prerequisites>

VPE_EnlargeBreasts

				</prerequisites>

		</modExtensions>
	</VEF.Abilities.AbilityDef>

	<VEF.Abilities.AbilityDef ParentName="VPE_PsycastBase">
		<defName>VPE_ShrinkGenitalia</defName>
		shrink genitalia
		<description>Shrinks the targets genitalia.</description>
		<iconPath>Things/Motes/ShrinkGenitalia</iconPath>
		<castTime>60</castTime>
		<range>24.9</range>
		<fleckOnTarget>PsycastPsychicEffect</fleckOnTarget>
		<targetMode>Pawn</targetMode>
		<abilityClass>VEF.Abilities.Ability_Blank</abilityClass>
		<castSound>PsycastPsychicPulse</castSound>
		<targetingParameters>
			<canTargetSelf>true</canTargetSelf>
			<canTargetAnimals>true</canTargetAnimals>
			<canTargetMechs>false</canTargetMechs>
			<canTargetHumans>true</canTargetHumans>
			<canTargetBuildings>false</canTargetBuildings>
		</targetingParameters>
		<modExtensions>

				<path>RJW_LewdCaster</path>
				<level>3</level>
				<order>2</order>
				<psyfocusCost>0.01</psyfocusCost>
				<entropyGain>12</entropyGain>
				<showCastBubble>true</showCastBubble>
				<psychic>true</psychic>
				<prerequisites>

VPE_ShrinkBreasts

				</prerequisites>

		</modExtensions>
	</VEF.Abilities.AbilityDef>

	<VEF.Abilities.AbilityDef ParentName="VPE_PsycastBase">
		<defName>VPE_IncreaseBreastFluid</defName>
		increase breast fluid
		<description>Increase fluid production in target's breast.</description>
		<iconPath>Things/Motes/IncreaseBreastFluid</iconPath>
		<castTime>60</castTime>
		<range>24.9</range>
		<fleckOnTarget>PsycastPsychicEffect</fleckOnTarget>
		<targetMode>Pawn</targetMode>
		<abilityClass>VEF.Abilities.Ability_Blank</abilityClass>
		<castSound>PsycastPsychicPulse</castSound>
		<targetingParameters>
			<canTargetSelf>true</canTargetSelf>
			<canTargetAnimals>true</canTargetAnimals>
			<canTargetMechs>false</canTargetMechs>
			<canTargetHumans>true</canTargetHumans>
			<canTargetBuildings>false</canTargetBuildings>
		</targetingParameters>
		<modExtensions>

				<path>RJW_LewdCaster</path>
				<level>4</level>
				<order>1</order>
				<psyfocusCost>0.01</psyfocusCost>
				<entropyGain>12</entropyGain>
				<showCastBubble>true</showCastBubble>
				<psychic>true</psychic>
				<prerequisites>

VPE_EnlargeBreasts

				</prerequisites>

		</modExtensions>
	</VEF.Abilities.AbilityDef>

	<VEF.Abilities.AbilityDef ParentName="VPE_PsycastBase">
		<defName>VPE_IncreasePenisFluid</defName>
		increase penis fluid
		<description>Increase fluid production in target's penis.</description>
		<iconPath>Things/Motes/IncreasePenisFluid</iconPath>
		<castTime>60</castTime>
		<range>24.9</range>
		<fleckOnTarget>PsycastPsychicEffect</fleckOnTarget>
		<targetMode>Pawn</targetMode>
		<abilityClass>VEF.Abilities.Ability_Blank</abilityClass>
		<castSound>PsycastPsychicPulse</castSound>
		<targetingParameters>
			<canTargetSelf>true</canTargetSelf>
			<canTargetAnimals>true</canTargetAnimals>
			<canTargetMechs>false</canTargetMechs>
			<canTargetHumans>true</canTargetHumans>
			<canTargetBuildings>false</canTargetBuildings>
		</targetingParameters>
		<modExtensions>

				<path>RJW_LewdCaster</path>
				<level>4</level>
				<order>2</order>
				<psyfocusCost>0.01</psyfocusCost>
				<entropyGain>12</entropyGain>
				<showCastBubble>true</showCastBubble>
				<psychic>true</psychic>
				<prerequisites>

VPE_EnlargeBreasts

				</prerequisites>

		</modExtensions>
	</VEF.Abilities.AbilityDef>

	<VEF.Abilities.AbilityDef ParentName="VPE_PsycastBase">
		<defName>VPE_IncreaseSexFulfillment</defName>
		increase sex fulfillment
		<description>Increases the pawn's sex fulfillment.</description>
		<iconPath>Things/Motes/IncreaseSexFulfillment</iconPath>
		<castTime>60</castTime>
		<range>24.9</range>
		<fleckOnTarget>PsycastPsychicEffect</fleckOnTarget>
		<targetMode>Pawn</targetMode>
		<abilityClass>VEF.Abilities.Ability_Blank</abilityClass>
		<castSound>PsycastPsychicPulse</castSound>
		<targetingParameters>
			<canTargetSelf>true</canTargetSelf>
			<canTargetAnimals>true</canTargetAnimals>
			<canTargetMechs>false</canTargetMechs>
			<canTargetHumans>true</canTargetHumans>
			<canTargetBuildings>false</canTargetBuildings>
		</targetingParameters>
		<modExtensions>

				<path>RJW_LewdCaster</path>
				<level>5</level>
				<order>1</order>
				<psyfocusCost>0.01</psyfocusCost>
				<entropyGain>12</entropyGain>
				<showCastBubble>true</showCastBubble>
				<psychic>true</psychic>
				<prerequisites>

VPE_IncreaseBreastFluid

				</prerequisites>

		</modExtensions>
	</VEF.Abilities.AbilityDef>

	<VEF.Abilities.AbilityDef ParentName="VPE_PsycastBase">
		<defName>VPE_DecreaseSexFulfillment</defName>
		decrease sex fulfillment
		<description>Decreases the pawn's sex fulfillment.</description>
		<iconPath>Things/Motes/DecreaseSexFulfillment</iconPath>
		<castTime>60</castTime>
		<range>24.9</range>
		<fleckOnTarget>PsycastPsychicEffect</fleckOnTarget>
		<targetMode>Pawn</targetMode>
		<abilityClass>VEF.Abilities.Ability_Blank</abilityClass>
		<castSound>PsycastPsychicPulse</castSound>
		<targetingParameters>
			<canTargetSelf>true</canTargetSelf>
			<canTargetAnimals>true</canTargetAnimals>
			<canTargetMechs>false</canTargetMechs>
			<canTargetHumans>true</canTargetHumans>
			<canTargetBuildings>false</canTargetBuildings>
		</targetingParameters>
		<modExtensions>

				<path>RJW_LewdCaster</path>
				<level>5</level>
				<order>2</order>
				<psyfocusCost>0.01</psyfocusCost>
				<entropyGain>12</entropyGain>
				<showCastBubble>true</showCastBubble>
				<psychic>true</psychic>
				<prerequisites>

VPE_ShrinkGenitalia

				</prerequisites>

		</modExtensions>
	</VEF.Abilities.AbilityDef>
</Defs>


code:
<?xml version="1.0" encoding="UTF-8"?>
<Defs>
    <VanillaPsycastsExpanded.PsycasterPathDef>
        <defName>RJW_LewdCaster</defName>
        <label>LewdCaster</label>
        <description>Lewdcasters are capable of altering sexual attributes.</description>
        <background>UI/Backgrounds/EmpathPath</background>
        <altBackground>UI/AlternativeBackgrounds/PathBG_Empath</altBackground>
        <tab>RJW</tab>
        <tooltip>LewdCasters are praised as the bringer of joy in their communities, able to manipulate sexual bodyparts, people from all over the land would do anything to gain their favors.</tooltip>
    </VanillaPsycastsExpanded.PsycasterPathDef>
</Defs>

license: mit


code:
<?xml version="1.0" encoding="utf-8"?>
<ModMetaData>
    <name>Rains RJW Addons</name>
    <author>Ryufais</author>
    <url>https://gitgud.io/Ryufais</url>
    <packageId>Ryuf.Rain.RJWAddons</packageId>
    <supportedVersions>

1.5

1.6

    </supportedVersions>

    <description>
        Just a collection of addons with varying ideas.
    </description>

    <modDependencies>

            <packageId>rim.job.world</packageId>
            <displayName>RimJobWorld</displayName>
            <downloadUrl>https://gitgud.io/Ed86/rjw</downloadUrl>

    </modDependencies>

    <loadAfter>

rim.job.world

VanillaExpanded.VPsycastsE

    </loadAfter>
</ModMetaData>


code:
**__What mod is this:__**
-  Simple psycast mod that allows you to alter sexual bodypart values
-  Compatible with VPE

**Note:** Mod isn't originally mine, so if the original author wishes for me to take it down, i'll be glad to do so.


code:
using RimWorld;
using rjw;
using rjw.Modules.Interactions;
using UnityEngine;
using Verse;
using Verse.Sound;

namespace RainsRJWAddons
{
    public class Comp_PsycastDecreaseAnus : CompAbilityEffect
    {
        private const float MinSeverity = 0.01f;

        private const string EffectMoteText = "Anus--";
        public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
        {
            base.Apply(target, dest);
            if (target.Pawn is not { } pawn || pawn.Dead) return;

            var sexParts = pawn.GetLewdParts()?.Anuses ?? [];

            bool partModified = false;
            foreach (var anuses in sexParts)
            {
                if (anuses.SexPart is Hediff_NaturalSexPart naturalAnus)
                {
                    float currentSeverity = naturalAnus.Severity;
                    float reduction = RainsRJWAddonMod.settings.anusIncrement;
                    float newSeverity = Mathf.Max(currentSeverity - reduction, MinSeverity);

                    naturalAnus.Severity = newSeverity;

                    var comp = anuses.SexPart.GetPartComp();
                    comp.SetSeverity(newSeverity, sync: false);
                    partModified = true;
                }
            }

            if (partModified)
            {
                MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
                DefDatabase<SoundDef>.GetNamed("PsycastPsychicPulse").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
            }
        }

        public override bool CanApplyOn(LocalTargetInfo target, LocalTargetInfo dest) => Valid(target);

        public override bool Valid(LocalTargetInfo target, bool throwMessage = false)
        {
            if (target.Pawn is not { } pawn || pawn.Dead) return false;

            var anuses = pawn.GetLewdParts()?.Anuses;
            if (anuses.EnumerableNullOrEmpty() && throwMessage)
            {
                PartsHelper.SendMessage($"{pawn.Name} does not have Anus!", pawn);
                return false;
            }

            bool onlyArtificial = true;
            bool allMinimum = true;
            foreach (var anus in anuses)
            {
                if (anus.SexPart is Hediff_NaturalSexPart naturalAnus)
                {
                    onlyArtificial = false;

                    float currentSeverity = naturalAnus.Severity;
                    if (currentSeverity > MinSeverity)
                    {
                        allMinimum = false;
                        break;
                    }
                }
            }

            if (onlyArtificial && throwMessage)
            {
                PartsHelper.SendMessage($"Cannot change artificial anus on {pawn.Name}", pawn);
                return false;
            }

            if (allMinimum && throwMessage)
            {
                PartsHelper.SendMessage($"Minimum anus size already reached for {pawn.Name}", pawn);
                return false;
            }

            return true;
        }
    }

    public class CompProperties_PsycastDecreaseAnus : CompProperties_AbilityEffect
    {
        public CompProperties_PsycastDecreaseAnus()
        {
            compClass = typeof(Comp_PsycastDecreaseAnus);
        }
    }
}


code:
using RimWorld;
using rjw;
using rjw.Modules.Interactions;
using UnityEngine;
using Verse;
using Verse.Sound;

namespace RainsRJWAddons
{
    public class Comp_PsycastDecreaseBreast : CompAbilityEffect
    {
        private const float MinSeverity = 0.01f;

        private const string EffectMoteText = "Breasts--";
        public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
        {
            base.Apply(target, dest);

            Pawn pawn = target.Pawn;
            if (pawn is null || pawn.Dead) return;

            var sexParts = pawn.GetLewdParts()?.Breasts ?? [];
            bool partModified = false;
            foreach (var breast in sexParts)
            {
                if (breast.SexPart is Hediff_NaturalSexPart naturalBreast)
                {
                    float currentSeverity = naturalBreast.Severity;
                    float reduction = RainsRJWAddonMod.settings.breastIncrement;
                    float newSeverity = Mathf.Max(currentSeverity - reduction, MinSeverity);

                    naturalBreast.Severity = newSeverity;

                    var comp = breast.SexPart.GetPartComp();
                    comp.SetSeverity(newSeverity, sync: false);
                    partModified = true;
                }
            }

            if (partModified)
            {
                MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
                DefDatabase<SoundDef>.GetNamed("PsycastPsychicPulse").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
            }
        }

        public override bool CanApplyOn(LocalTargetInfo target, LocalTargetInfo dest) => Valid(target);

        public override bool Valid(LocalTargetInfo target, bool throwMessage = false)
        {
            Pawn pawn = target.Pawn;
            if (pawn is null || pawn.Dead) return false;

            var breasts = pawn.GetLewdParts()?.Breasts;
            if (breasts.EnumerableNullOrEmpty() && throwMessage)
            {
                PartsHelper.SendMessage($"{pawn.Name} does not have breasts!", pawn);
                return false;
            }

            bool onlyArtificial = true;
            bool allMinimum = true;
            foreach (var breast in breasts)
            {
                if (breast.SexPart is Hediff_NaturalSexPart naturalBreast)
                {
                    onlyArtificial = false;

                    float currentSeverity = naturalBreast.Severity;
                    if (currentSeverity > MinSeverity)
                    {
                        allMinimum = false;
                        break;
                    }
                }
            }

            if (onlyArtificial && throwMessage)
            {
                PartsHelper.SendMessage($"Cannot change artificial breasts on {pawn.Name}", pawn);
                return false;
            }

            if (allMinimum && throwMessage)
            {
                PartsHelper.SendMessage($"Minimum breast size already reached for {pawn.Name}", pawn);
                return false;
            }

            return true;
        }
    }

    public class CompProperties_PsycastDecreaseBreast : CompProperties_AbilityEffect
    {
        public CompProperties_PsycastDecreaseBreast()
        {
            compClass = typeof(Comp_PsycastDecreaseBreast);
        }
    }
}


code:
using RimWorld;
using rjw;
using rjw.Modules.Interactions;
using System.Collections.Generic;
using UnityEngine;
using Verse;
using Verse.Sound;

namespace RainsRJWAddons
{
    public class Comp_PsycastDecreaseGenitalia : CompAbilityEffect
    {
        private const float MinSeverity = 0.01f;

        private const string EffectMoteTextV = "Vagina--";

        private const string EffectMoteTextP = "Penis--";
        public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
        {
            base.Apply(target, dest);

            if (target.Pawn is not { } pawn || pawn.Dead) return;

            var sexParts = pawn.GetLewdParts();
            if (sexParts is null) return;

            DecreaseSexPartSeverity(sexParts.Vaginas, RainsRJWAddonMod.settings.vaginaIncrement, EffectMoteTextV, pawn);
            DecreaseSexPartSeverity(sexParts.Penises, RainsRJWAddonMod.settings.penisIncrement, EffectMoteTextP, pawn);
        }

        public void DecreaseSexPartSeverity(List<RJWLewdablePart> sexParts, float reduction, string moteText, Pawn pawn)
        {
            if (sexParts.EnumerableNullOrEmpty()) return;

            bool partModified = false;
            foreach (var part in sexParts)
            {
                if (part.SexPart is Hediff_NaturalSexPart naturalPart)
                {
                    var currentSeverity = naturalPart.Severity;
                    var newSeverity = Mathf.Max(currentSeverity - reduction, MinSeverity);

                    naturalPart.Severity = newSeverity;

                    var comp = part.SexPart.GetPartComp();
                    comp.SetSeverity(newSeverity, sync: false);
                    partModified = true;
                }
            }

            if (partModified)
            {
                MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, moteText.Translate(), 2f);
                DefDatabase<SoundDef>.GetNamed("PsycastPsychicPulse").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
            }
        }

        public override bool CanApplyOn(LocalTargetInfo target, LocalTargetInfo dest) => Valid(target);

        public override bool Valid(LocalTargetInfo target, bool throwMessage = false)
        {

            if (target.Pawn is not { } pawn || pawn.Dead) return false;

            var sexParts = pawn.GetLewdParts();
            if (sexParts is null) return false;

            var vaginas = sexParts.Vaginas;
            var penises = sexParts.Penises;
            if (vaginas.EnumerableNullOrEmpty() && penises.EnumerableNullOrEmpty() && throwMessage)
            {
                PartsHelper.SendMessage($"{pawn.Name} does not have genitals!", pawn);
                return false;
            }

            bool onlyArtificial = true;
            bool allMinimum = true;
            foreach (var vagina in vaginas)
            {
                if (vagina.SexPart is Hediff_NaturalSexPart naturalVagina)
                {
                    onlyArtificial = false;

                    float currentSeverity = naturalVagina.Severity;
                    if (currentSeverity > MinSeverity)
                    {
                        allMinimum = false;
                        break;
                    }
                }
            }

            if (allMinimum)
            {
                foreach (var penis in penises)
                {
                    if (penis.SexPart is Hediff_NaturalSexPart naturalPenis)
                    {
                        onlyArtificial = false;

                        float currentSeverity = naturalPenis.Severity;
                        if (currentSeverity > MinSeverity)
                        {
                            allMinimum = false;
                            break;
                        }
                    }
                }
            }

            if (onlyArtificial && throwMessage)
            {
                PartsHelper.SendMessage($"Cannot change artificial genitals on {pawn.Name}", pawn);
                return false;
            }

            if (allMinimum && throwMessage)
            {
                PartsHelper.SendMessage($"Minimum genital size already reached for {pawn.Name}", pawn);
                return false;
            }

            return true;
        }
    }

    public class CompProperties_PsycastDecreaseGenitalia : CompProperties_AbilityEffect
    {
        public CompProperties_PsycastDecreaseGenitalia()
        {
            compClass = typeof(Comp_PsycastDecreaseGenitalia);
        }
    }
}


code:
using RimWorld;
using rjw;
using Verse;
using Verse.Sound;

namespace RainsRJWAddons
{
    public class Comp_PsycastDecreaseSexFulfillment : CompAbilityEffect
    {
        private const string EffectMoteText = "Sex need--";
        public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
        {
            base.Apply(target, dest);

            if (target.Pawn is not { } pawn || pawn.Dead) return;

            var sexNeed = pawn.needs.TryGetNeed<Need_Sex>();
            if (sexNeed is null) return;

            var reduction = RainsRJWAddonMod.settings.sexNeedIncrement;
            sexNeed.CurLevel -= reduction;
            MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
            DefDatabase<SoundDef>.GetNamed("PsycastPsychicPulse").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));

        }

        public override bool CanApplyOn(LocalTargetInfo target, LocalTargetInfo dest) => Valid(target);

        public override bool Valid(LocalTargetInfo target, bool throwMessage = false)
        {

            if (target.Pawn is not { } pawn || pawn.Dead) return false;

            bool hasSexNeed = pawn.RaceHasSexNeed();
            if (!hasSexNeed && throwMessage)
            {
                PartsHelper.SendMessage($"{pawn.Name} does not have sex need!", pawn);
                return false;
            }

            return hasSexNeed;
        }
    }

    public class CompProperties_PsycastDecreaseSexFulfillment : CompProperties_AbilityEffect
    {
        public CompProperties_PsycastDecreaseSexFulfillment()
        {
            compClass = typeof(Comp_PsycastDecreaseSexFulfillment);
        }
    }
}


code:
using RimWorld;
using rjw;
using rjw.Modules.Interactions;
using UnityEngine;
using Verse;
using Verse.Sound;

namespace RainsRJWAddons
{
    public class Comp_PsycastIncreaseAnus : CompAbilityEffect
    {
        private const float MaxSeverity = 3.0f;

        private const string EffectMoteText = "Anus++";
        public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
        {
            base.Apply(target, dest);

            if (target.Pawn is not { } pawn || pawn.Dead) return;

            var sexParts = pawn.GetLewdParts()?.Anuses ?? [];

            bool partModified = false;
            foreach (var anuses in sexParts)
            {
                if (anuses.SexPart is Hediff_NaturalSexPart naturalAnus)
                {
                    float currentSeverity = naturalAnus.Severity;
                    float boost = RainsRJWAddonMod.settings.anusIncrement;
                    float newSeverity = Mathf.Min(currentSeverity + boost, MaxSeverity);

                    naturalAnus.Severity = newSeverity;

                    var comp = anuses.SexPart.GetPartComp();
                    comp.SetSeverity(newSeverity, sync: false);
                    partModified = true;
                }
            }

            if (partModified)
            {
                MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
                DefDatabase<SoundDef>.GetNamed("PsycastPsychicPulse").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
            }
        }

        public override bool CanApplyOn(LocalTargetInfo target, LocalTargetInfo dest) => Valid(target);

        public override bool Valid(LocalTargetInfo target, bool throwMessage = false)
        {
            if (target.Pawn is not { } pawn || pawn.Dead) return false;

            var anuses = pawn.GetLewdParts()?.Anuses;
            if (anuses.EnumerableNullOrEmpty() && throwMessage)
            {
                PartsHelper.SendMessage("Pawn does not have anus!", pawn);
                return false;
            }

            bool onlyArtificial = true;
            bool allMaxed = true;
            foreach (var anus in anuses)
            {
                if (anus.SexPart is Hediff_NaturalSexPart naturalAnus)
                {
                    onlyArtificial = false;

                    float currentSeverity = naturalAnus.Severity;
                    if (currentSeverity < MaxSeverity)
                    {
                        allMaxed = false;
                        break;
                    }
                }
            }

            if (onlyArtificial && throwMessage)
            {
                PartsHelper.SendMessage("Cannot change artificial anus!", pawn);
                return false;
            }

            if (allMaxed && throwMessage)
            {
                PartsHelper.SendMessage("Maximum anus size already reached!", pawn);
                return false;
            }

            return true;
        }
    }

    public class CompProperties_PsycastIncreaseAnus : CompProperties_AbilityEffect
    {
        public CompProperties_PsycastIncreaseAnus()
        {
            compClass = typeof(Comp_PsycastIncreaseAnus);
        }
    }
}


code:
using RimWorld;
using rjw;
using rjw.Modules.Interactions;
using UnityEngine;
using Verse;
using Verse.Sound;

namespace RainsRJWAddons
{
    public class Comp_PsycastIncreaseBreast : CompAbilityEffect
    {
        private const float MaxSeverity = 3.0f;

        private const string EffectMoteText = "Breasts++";
        public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
        {
            base.Apply(target, dest);

            if (target.Pawn is not { } pawn || pawn.Dead) return;

            var sexParts = pawn.GetLewdParts()?.Breasts ?? [];

            bool partModified = false;
            foreach (var breast in sexParts)
            {
                if (breast.SexPart is Hediff_NaturalSexPart naturalBreast)
                {
                    float currentSeverity = naturalBreast.Severity;
                    float boost = RainsRJWAddonMod.settings.breastIncrement;
                    float newSeverity = Mathf.Min(currentSeverity + boost, MaxSeverity);

                    naturalBreast.Severity = newSeverity;

                    var comp = breast.SexPart.GetPartComp();
                    comp.SetSeverity(newSeverity, sync: false);
                    partModified = true;
                }
            }

            if (partModified)
            {
                MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
                DefDatabase<SoundDef>.GetNamed("PsycastPsychicPulse").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
            }
        }

        public override bool CanApplyOn(LocalTargetInfo target, LocalTargetInfo dest) => Valid(target);

        public override bool Valid(LocalTargetInfo target, bool throwMessage = false)
        {

            if (target.Pawn is not { } pawn || pawn.Dead) return false;

            var breasts = pawn.GetLewdParts()?.Breasts;
            if (breasts.EnumerableNullOrEmpty() && throwMessage)
            {
                PartsHelper.SendMessage("Pawn does not have breasts!", pawn);
                return false;
            }

            bool onlyArtificial = true;
            bool allMaxed = true;
            foreach (var breast in breasts)
            {
                if (breast.SexPart is Hediff_NaturalSexPart naturalBreast)
                {
                    onlyArtificial = false;

                    float currentSeverity = naturalBreast.Severity;
                    if (currentSeverity < MaxSeverity)
                    {
                        allMaxed = false;
                        break;
                    }
                }
            }

            if (onlyArtificial && throwMessage)
            {
                PartsHelper.SendMessage("Cannot change artificial breasts!", pawn);
                return false;
            }

            if (allMaxed && throwMessage)
            {
                PartsHelper.SendMessage("Maximum breast size already reached!", pawn);
                return false;
            }

            return true;
        }
    }

    public class CompProperties_PsycastIncreaseBreast : CompProperties_AbilityEffect
    {
        public CompProperties_PsycastIncreaseBreast() =>
            compClass = typeof(Comp_PsycastIncreaseBreast);

    }
}


code:
using RimWorld;
using rjw;
using rjw.Modules.Interactions;
using UnityEngine;
using Verse;
using Verse.Sound;

namespace RainsRJWAddons
{
    public class Comp_PsycastIncreaseBreastFluid : CompAbilityEffect
    {
        private const float MaxSeverity = 100.0f;

        private const string EffectMoteText = "Fluid++";
        public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
        {
            base.Apply(target, dest);

            if (target.Pawn is not { } pawn || pawn.Dead) return;

            var sexParts = pawn.GetLewdParts()?.Breasts ?? [];

            bool partModified = false;
            foreach (var breast in sexParts)
            {
                var comp = breast.SexPart.GetPartComp();
                if (comp is not null)
                {
                    var fluid = comp.Fluid;
                    if (fluid is null) continue;

                    var fluidMultiplier = comp.partFluidMultiplier;
                    float boost = RainsRJWAddonMod.settings.fluidIncrement;
                    comp.partFluidMultiplier = Mathf.Min(fluidMultiplier + boost, MaxSeverity);
                    partModified = true;
                }
            }

            if (partModified)
            {
                MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
                DefDatabase<SoundDef>.GetNamed("PsycastPsychicPulse").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
            }
        }

        public override bool CanApplyOn(LocalTargetInfo target, LocalTargetInfo dest) => Valid(target);

        public override bool Valid(LocalTargetInfo target, bool throwMessage = false)
        {

            if (target.Pawn is not { } pawn || pawn.Dead) return false;

            var breasts = pawn.GetLewdParts()?.Breasts;
            if (breasts.EnumerableNullOrEmpty())
            {
                if (throwMessage) PartsHelper.SendMessage($"{pawn.Name} does not have breasts!", pawn);
                return false;
            }

            bool allMaxed = true;
            foreach (var breast in breasts)
            {
                var comp = breast.SexPart.GetPartComp();
                if (comp is not null)
                {
                    var fluid = comp.Fluid;
                    if (fluid is null) continue;

                    var fluidMultiplier = comp.partFluidMultiplier;
                    if (fluidMultiplier < MaxSeverity)
                    {
                        allMaxed = false;
                        break;
                    }
                }
            }

            if (allMaxed && throwMessage)
            {
                PartsHelper.SendMessage($"Maximum fluid amount reached for {pawn.Name}", pawn);
                return false;
            }

            return true;
        }

    }

    public class CompProperties_PsycastIncreaseBreastFluid : CompProperties_AbilityEffect
    {
        public CompProperties_PsycastIncreaseBreastFluid()
        {
            compClass = typeof(Comp_PsycastIncreaseBreastFluid);
        }
    }
}


code:
using RimWorld;
using rjw;
using rjw.Modules.Interactions;
using System.Collections.Generic;
using UnityEngine;
using Verse;
using Verse.Sound;

namespace RainsRJWAddons
{
    public class Comp_PsycastIncreaseGenitalia : CompAbilityEffect
    {
        private const float MaxSeverity = 3.0f;

        private const string EffectMoteTextV = "Vagina++";

        private const string EffectMoteTextP = "Penis++";
        public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
        {
            base.Apply(target, dest);

            if (target.Pawn is not { } pawn || pawn.Dead) return;

            var sexParts = pawn.GetLewdParts();
            if (sexParts is null) return;

            IncreaseSexPartSeverity(sexParts.Vaginas, RainsRJWAddonMod.settings.vaginaIncrement, EffectMoteTextV, pawn);
            IncreaseSexPartSeverity(sexParts.Penises, RainsRJWAddonMod.settings.penisIncrement, EffectMoteTextP, pawn);
        }

        public void IncreaseSexPartSeverity(List<RJWLewdablePart> sexParts, float boost, string moteText, Pawn pawn)
        {
            if (sexParts.EnumerableNullOrEmpty()) return;

            bool partModified = false;
            foreach (var part in sexParts)
            {
                if (part.SexPart is Hediff_NaturalSexPart naturalPart)
                {
                    var currentSeverity = naturalPart.Severity;
                    var newSeverity = Mathf.Min(currentSeverity + boost, MaxSeverity);

                    naturalPart.Severity = newSeverity;

                    var comp = part.SexPart.GetPartComp();
                    comp.SetSeverity(newSeverity, sync: false);
                    partModified = true;
                }
            }

            if (partModified)
            {
                MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, moteText.Translate(), 2f);
                DefDatabase<SoundDef>.GetNamed("PsycastPsychicPulse").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
            }

        }

        public override bool CanApplyOn(LocalTargetInfo target, LocalTargetInfo dest) => Valid(target);

        public override bool Valid(LocalTargetInfo target, bool throwMessage = false)
        {
            if (target.Pawn is not { } pawn || pawn.Dead) return false;

            var sexParts = pawn.GetLewdParts();
            if (sexParts is null) return false;

            var vaginas = sexParts.Vaginas;
            var penises = sexParts.Penises;
            if (vaginas.EnumerableNullOrEmpty() && penises.EnumerableNullOrEmpty() && throwMessage)
            {
                PartsHelper.SendMessage("pawn does not have genitals!", pawn);
                return false;
            }

            bool onlyArtificial = true;
            bool allMaxed = true;
            foreach (var vagina in vaginas)
            {
                if (vagina.SexPart is Hediff_NaturalSexPart naturalVagina)
                {
                    onlyArtificial = false;

                    float currentSeverity = naturalVagina.Severity;
                    if (currentSeverity < MaxSeverity)
                    {
                        allMaxed = false;
                        break;
                    }
                }
            }

            if (allMaxed)
            {
                foreach (var penis in penises)
                {
                    if (penis.SexPart is Hediff_NaturalSexPart naturalPenises) // Fix: Access the `SexPart` property of `RJWLewdablePart`
                    {
                        onlyArtificial = false;

                        float currentSeverity = naturalPenises.Severity;
                        if (currentSeverity < MaxSeverity)
                        {
                            allMaxed = false;
                            break;
                        }
                    }
                }
            }

            if (onlyArtificial && throwMessage)
            {
                PartsHelper.SendMessage("Cannot change artificial genitals!", pawn);
                return false;
            }

            if (allMaxed && throwMessage)
            {
                PartsHelper.SendMessage("Maximum genital size already reached!", pawn);
                return false;
            }

            return true;
        }
    }

    public class CompProperties_PsycastIncreaseGenitalia : CompProperties_AbilityEffect
    {
        public CompProperties_PsycastIncreaseGenitalia()
        {
            compClass = typeof(Comp_PsycastIncreaseGenitalia);
        }
    }
}


code:
using RimWorld;
using rjw;
using rjw.Modules.Interactions;
using UnityEngine;
using Verse;
using Verse.Sound;

namespace RainsRJWAddons
{
    class Comp_PsycastIncreasePenisFluid : CompAbilityEffect
    {
        private const float MaxSeverity = 100.0f;

        private const string EffectMoteText = "Fluid++";
        public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
        {
            base.Apply(target, dest);

            if (target.Pawn is not { } pawn || pawn.Dead) return;

            var sexParts = pawn.GetLewdParts()?.Penises ?? [];

            bool partModified = false;
            foreach (var penis in sexParts)
            {
                var comp = penis.SexPart.GetPartComp();
                if (comp is not null)
                {
                    var fluid = comp.Fluid;
                    if (fluid is null) continue;

                    float boost = RainsRJWAddonMod.settings.fluidIncrement;
                    var fluidMultiplier = comp.partFluidMultiplier;
                    comp.partFluidMultiplier = Mathf.Min(fluidMultiplier + boost, MaxSeverity);
                    partModified = true;
                }
            }

            if (partModified)
            {
                MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
                DefDatabase<SoundDef>.GetNamed("PsycastPsychicPulse").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
            }
        }

        public override bool CanApplyOn(LocalTargetInfo target, LocalTargetInfo dest) => Valid(target);

        public override bool Valid(LocalTargetInfo target, bool throwMessage = false)
        {

            if (target.Pawn is not { } pawn || pawn.Dead) return false;

            var penises = pawn.GetLewdParts()?.Penises;
            if (penises.EnumerableNullOrEmpty())
            {
                if (throwMessage) PartsHelper.SendMessage($"{pawn.Name} does not have penis!", pawn);
                return false;
            }

            bool allMaxed = true;
            foreach (var penis in penises)
            {
                var comp = penis.SexPart.GetPartComp();
                if (comp is not null)
                {
                    var fluid = comp.Fluid;
                    if (fluid is null) continue;

                    var fluidMultiplier = comp.partFluidMultiplier;
                    if (fluidMultiplier < MaxSeverity)
                    {
                        allMaxed = false;
                        break;
                    }
                }
            }

            if (allMaxed && throwMessage)
            {
                PartsHelper.SendMessage($"Maximum fluid amount reached for {pawn.Name}", pawn);
                return false;
            }

            return true;
        }
    }

    public class CompProperties_PsycastIncreasePenisFluid : CompProperties_AbilityEffect
    {
        public CompProperties_PsycastIncreasePenisFluid()
        {
            compClass = typeof(Comp_PsycastIncreasePenisFluid);
        }
    }
}


code:
using RimWorld;
using rjw;
using Verse;
using Verse.Sound;

namespace RainsRJWAddons
{
    public class Comp_PsycastIncreaseSexFulfillment : CompAbilityEffect
    {
        private const string EffectMoteText = "Sex need++";
        public override void Apply(LocalTargetInfo target, LocalTargetInfo dest)
        {
            base.Apply(target, dest);

            if (target.Pawn is not { } pawn || pawn.Dead) return;

            var sexNeed = pawn.needs.TryGetNeed<Need_Sex>();
            if (sexNeed is null) return;

            var boost = RainsRJWAddonMod.settings.sexNeedIncrement;
            sexNeed.CurLevel += boost;
            MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
            DefDatabase<SoundDef>.GetNamed("PsycastPsychicPulse").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
        }

        public override bool CanApplyOn(LocalTargetInfo target, LocalTargetInfo dest) => Valid(target);

        public override bool Valid(LocalTargetInfo target, bool throwMessage = false)
        {

            if (target.Pawn is not { } pawn || pawn.Dead) return false;

            bool hasSexNeed = pawn.RaceHasSexNeed();
            if (!hasSexNeed && throwMessage)
            {
                PartsHelper.SendMessage($"{pawn.Name} does not have sex need!", pawn);
                return false;
            }

            return hasSexNeed;
        }
    }

    public class CompProperties_PsycastIncreaseSexFulfillment : CompProperties_AbilityEffect
    {
        public CompProperties_PsycastIncreaseSexFulfillment()
        {
            compClass = typeof(Comp_PsycastIncreaseSexFulfillment);
        }
    }
}


code:
using System.Reflection;
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("RainsRJWAddons")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("RainsRJWAddons")]
[assembly: AssemblyCopyright("Copyright ©  2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7259745d-73f8-4097-b641-13b27bf7140f")]

// 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 RimWorld;
using Verse;

namespace RainsRJWAddons
{
    public class PartsHelper : CompAbilityEffect
    {
        public static void SendMessage(string text, Pawn pawn) =>
            Messages.Message(text, pawn, MessageTypeDefOf.RejectInput, false);
    }

}


code:
using UnityEngine;
using Verse;

namespace RainsRJWAddons
{
    public class RainsRJWAddonMod : Mod
    {
        public static Settings settings;

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

        public override void DoSettingsWindowContents(Rect inRect)
        {
            Listing_Standard list = new Listing_Standard();
            list.Begin(inRect);
            list.Label("BreastIncrement".Translate() + ": " + settings.breastIncrement.ToStringByStyle(ToStringStyle.FloatMaxTwo), -1, "BreastIncrementDesc".Translate());
            settings.breastIncrement = list.Slider(settings.breastIncrement, 0f, 1f);
            list.Label("VaginaIncrement".Translate() + ": " + settings.vaginaIncrement.ToStringByStyle(ToStringStyle.FloatMaxTwo), -1, "VaginaIncrementDesc".Translate());
            settings.vaginaIncrement = list.Slider(settings.vaginaIncrement, 0f, 1f);
            list.Label("PenisIncrement".Translate() + ": " + settings.penisIncrement.ToStringByStyle(ToStringStyle.FloatMaxTwo), -1, "PenisIncrementDesc".Translate());
            settings.penisIncrement = list.Slider(settings.penisIncrement, 0f, 1f);
            list.Label("SexNeedIncrement".Translate() + ": " + settings.sexNeedIncrement.ToStringByStyle(ToStringStyle.FloatMaxTwo), -1, "SexNeedIncrementDesc".Translate());
            settings.sexNeedIncrement = list.Slider(settings.sexNeedIncrement, 0f, 1f);
            list.Label("AnusIncrement".Translate() + ": " + settings.anusIncrement.ToStringByStyle(ToStringStyle.FloatMaxTwo), -1, "anusIncrementDesc".Translate());
            settings.anusIncrement = list.Slider(settings.anusIncrement, 0f, 1f);
            list.Label("fluidIncrement".Translate() + ": " + settings.fluidIncrement.ToStringByStyle(ToStringStyle.FloatMaxTwo), -1, "fluidIncrementDesc".Translate());
            settings.fluidIncrement = list.Slider(settings.fluidIncrement, 0f, 1f);
            list.End();
            base.DoSettingsWindowContents(inRect);
        }

        public override string SettingsCategory()
        {
            return "Rains RJW Addon";
        }
    }
}


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>{7259745D-73F8-4097-B641-13B27BF7140F}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>RainsRJWAddons</RootNamespace>
    <AssemblyName>RainsRJWAddons</AssemblyName>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
    <LangVersion>preview</LangVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
    <TargetFrameworkProfile />
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>portable</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>..\1.6\Assemblies\</OutputPath>
    <DefineConstants>
    </DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>3</WarningLevel>
    <DebugSymbols>true</DebugSymbols>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="0Harmony, Version=2.3.3.0, Culture=neutral, processorArchitecture=MSIL">
      <HintPath>..\..\2934420800\Assemblies\0Harmony.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Assembly-CSharp">
      <HintPath>..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="RJW">
      <HintPath>..\..\rjw\1.6\Assemblies\RJW.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
    <Reference Include="UnityEngine">
      <HintPath>..\..\..\RimWorldWin64_Data\Managed\UnityEngine.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="UnityEngine.CoreModule">
      <HintPath>..\..\..\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
      <Private>False</Private>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Comps\Comp_PsycastIncreasePenisFluid.cs" />
    <Compile Include="Comps\Comp_PsycastIncreaseBreastFluid.cs" />
    <Compile Include="Comps\Comp_PsycastDecreaseBreast.cs" />
    <Compile Include="Comps\Comp_PsycastDecreaseAnus.cs" />
    <Compile Include="Comps\Comp_PsycastDecreaseGenitalia.cs" />
    <Compile Include="Comps\Comp_PsycastIncreaseAnus.cs" />
    <Compile Include="Comps\Comp_PsycastDecreaseSexFulfillment.cs" />
    <Compile Include="Comps\Comp_PsycastIncreaseSexFulfillment.cs" />
    <Compile Include="Comps\Comp_PsycastIncreaseGenitalia.cs" />
    <Compile Include="Comps\Comp_PsycastIncreaseBreast.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="PsycastHelper.cs" />
    <Compile Include="RainsRJWAddonMod.cs" />
    <Compile Include="Settings.cs" />
    <Compile Include="Startup.cs" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>


code:
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.13.35806.99 d17.13
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RainsRJWAddons", "RainsRJWAddons.csproj", "{7259745D-73F8-4097-B641-13B27BF7140F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RainsVPE", "..\RainsVPE\RainsVPE.csproj", "{BEB31732-57A1-4A02-8AA9-AD9F69C602A1}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{7259745D-73F8-4097-B641-13B27BF7140F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{7259745D-73F8-4097-B641-13B27BF7140F}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{7259745D-73F8-4097-B641-13B27BF7140F}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{7259745D-73F8-4097-B641-13B27BF7140F}.Release|Any CPU.Build.0 = Release|Any CPU
		{BEB31732-57A1-4A02-8AA9-AD9F69C602A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{BEB31732-57A1-4A02-8AA9-AD9F69C602A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{BEB31732-57A1-4A02-8AA9-AD9F69C602A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{BEB31732-57A1-4A02-8AA9-AD9F69C602A1}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {5D4C575E-35BA-423E-B6FC-B759D4A6D246}
	EndGlobalSection
EndGlobal


code:
using Verse;

namespace RainsRJWAddons
{
    public class Settings : ModSettings
    {
        public float penisIncrement = 0.1f;
        public float vaginaIncrement = 0.1f;
        public float breastIncrement = 0.1f;
        public float anusIncrement = 0.1f;
        public float sexNeedIncrement = 0.05f;
        public float fluidIncrement = 0.1f;

        public override void ExposeData()
        {
            Scribe_Values.Look(ref penisIncrement, "penisIncrement", 0.1f);
            Scribe_Values.Look(ref vaginaIncrement, "vaginaIncrement", 0.1f);
            Scribe_Values.Look(ref breastIncrement, "breastIncrement", 0.1f);
            Scribe_Values.Look(ref anusIncrement, "anusIncrement", 0.1f);
            Scribe_Values.Look(ref sexNeedIncrement, "sexNeedIncrement", 0.05f);
            Scribe_Values.Look(ref fluidIncrement, "fluidIncrement", 0.1f);
            base.ExposeData();
        }
    }
}


code:
using HarmonyLib;
using System.Reflection;
using Verse;

namespace RainsRJWAddons
{
    [StaticConstructorOnStartup]
    public static class Startup
    {
        static Startup()
        {
            Harmony harmony = new Harmony("rrjwa");
            harmony.PatchAll(Assembly.GetExecutingAssembly());
        }
    }
}


code:
using RainsRJWAddons;
using RimWorld;
using RimWorld.Planet;
using rjw;
using rjw.Modules.Interactions;
using UnityEngine;
using Verse;
using Ability = VEF.Abilities.Ability;

namespace RainsVPE
{
    class VPE_PsycastDecreaseAnus : VEF.Abilities.AbilityExtension_AbilityMod
    {
        private const float MinSeverity = 0.01f;

        private const string EffectMoteText = "Anus--";

        public override void Cast(GlobalTargetInfo[] targets, Ability ability)
        {
            base.Cast(targets, ability);

            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {
                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var sexParts = pawn.GetLewdParts()?.Anuses ?? [];

                bool anyPartModified = false;
                foreach (var anuses in sexParts)
                {
                    if (anuses.SexPart is Hediff_NaturalSexPart naturalAnus)
                    {
                        float currentSeverity = naturalAnus.Severity;
                        float reduction = RainsRJWAddonMod.settings.anusIncrement;
                        float newSeverity = Mathf.Max(currentSeverity - reduction, MinSeverity);

                        naturalAnus.Severity = newSeverity;

                        var Comp = anuses.SexPart.GetPartComp();
                        Comp.SetSeverity(newSeverity, sync: false);
                        anyPartModified = true;
                    }
                }

                if (anyPartModified)
                {
                    MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
                }
            }
        }

        public override bool Valid(GlobalTargetInfo[] targets, Ability ability, bool throwMessage = false)
        {
            bool anyValid = false;
            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var anuses = pawn.GetLewdParts()?.Anuses;
                if (anuses.EnumerableNullOrEmpty() && throwMessage)
                {
                    PartsHelper.SendMessage($"{pawn.Name} does not have anus!", pawn);
                    continue;
                }

                bool onlyArtificial = true;
                bool allMinimum = true;
                foreach (var anus in anuses)
                {
                    if (anus.SexPart is Hediff_NaturalSexPart naturalAnus)
                    {
                        onlyArtificial = false;

                        float currentSeverity = naturalAnus.Severity;
                        if (currentSeverity > MinSeverity)
                        {
                            allMinimum = false;
                            break;
                        }
                    }
                }

                if (onlyArtificial && throwMessage)
                {
                    PartsHelper.SendMessage($"Cannot change artificial anus on {pawn.Name}", pawn);
                    continue;
                }

                if (allMinimum && throwMessage)
                {
                    PartsHelper.SendMessage($"Minimum anus size already reached for {pawn.Name}", pawn);
                    continue;
                }

                anyValid = true;
            }

            return anyValid;
        }
    }
}


code:
using RainsRJWAddons;
using RimWorld;
using RimWorld.Planet;
using rjw;
using rjw.Modules.Interactions;
using UnityEngine;
using Verse;
using Ability = VEF.Abilities.Ability;

namespace RainsVPE
{
    class VPE_PsycastDecreaseBreast : VEF.Abilities.AbilityExtension_AbilityMod
    {
        private const float MinSeverity = 0.01f;

        private const string EffectMoteText = "Breasts--";
        public override void Cast(GlobalTargetInfo[] targets, Ability ability)
        {
            base.Cast(targets, ability);

            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var sexParts = pawn.GetLewdParts()?.Breasts ?? [];

                bool anyPartModified = false;
                foreach (var breast in sexParts)
                {
                    if (breast.SexPart is Hediff_NaturalSexPart naturalBreast)
                    {
                        float currentSeverity = naturalBreast.Severity;
                        float reduction = RainsRJWAddonMod.settings.breastIncrement;
                        float newSeverity = Mathf.Max(currentSeverity - reduction, MinSeverity);

                        naturalBreast.Severity = newSeverity;

                        var comp = breast.SexPart.GetPartComp();
                        comp.SetSeverity(newSeverity, sync: false);
                        anyPartModified = true;
                    }
                }

                if (anyPartModified)
                {
                    MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
                }
            }

        }

        public override bool Valid(GlobalTargetInfo[] targets, Ability ability, bool throwMessage = false)
        {
            bool anyValid = false;
            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var breasts = pawn.GetLewdParts()?.Breasts;
                if (breasts.EnumerableNullOrEmpty() && throwMessage)
                {
                    PartsHelper.SendMessage($"{pawn.Name} does not have breasts!", pawn);
                    continue;
                }

                bool onlyArtificial = true;
                bool allMinimum = true;
                foreach (var breast in breasts)
                {
                    if (breast.SexPart is Hediff_NaturalSexPart naturalBreast)
                    {
                        onlyArtificial = false;

                        float currentSeverity = naturalBreast.Severity;
                        if (currentSeverity > MinSeverity)
                        {
                            allMinimum = false;
                            break;
                        }
                    }
                }

                if (onlyArtificial && throwMessage)
                {
                    PartsHelper.SendMessage($"Cannot change artificial breasts on {pawn.Name}", pawn);
                    continue;
                }

                if (allMinimum && throwMessage)
                {
                    PartsHelper.SendMessage($"Minimum breast size already reached for {pawn.Name}", pawn);
                    continue;
                }

                anyValid = true;
            }
            return anyValid;
        }
    }
}


code:
using RainsRJWAddons;
using RimWorld;
using RimWorld.Planet;
using rjw;
using rjw.Modules.Interactions;
using System.Collections.Generic;
using UnityEngine;
using Verse;
using Ability = VEF.Abilities.Ability;

namespace RainsVPE
{
    class VPE_PsycastDecreaseGenitalia : VEF.Abilities.AbilityExtension_AbilityMod
    {
        private const float MinSeverity = 0.01f;

        private const string EffectMoteTextV = "Vagina--";

        private const string EffectMoteTextP = "Penis--";

        public override void Cast(GlobalTargetInfo[] targets, Ability ability)
        {
            base.Cast(targets, ability);

            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var sexParts = pawn.GetLewdParts();
                if (sexParts is null) continue;

                DecreaseSexPartSeverity(sexParts.Vaginas, false, RainsRJWAddonMod.settings.vaginaIncrement, EffectMoteTextV, pawn);
                DecreaseSexPartSeverity(sexParts.Penises, false, RainsRJWAddonMod.settings.penisIncrement, EffectMoteTextP, pawn);
            }
        }

        public void DecreaseSexPartSeverity(List<RJWLewdablePart> sexParts, bool partModified, float reduction, string moteText, Pawn pawn)
        {
            if (sexParts.EnumerableNullOrEmpty()) return;

            foreach (var part in sexParts)
            {
                if (part.SexPart is Hediff_NaturalSexPart naturalPart)
                {
                    var currentSeverity = naturalPart.Severity;
                    var newSeverity = Mathf.Max(currentSeverity - reduction, MinSeverity);

                    naturalPart.Severity = newSeverity;

                    var comp = part.SexPart.GetPartComp();
                    comp.SetSeverity(newSeverity, sync: false);
                    partModified = true;
                }
            }

            if (partModified)
            {
                MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, moteText.Translate(), 2f);
            }

        }

        public override bool Valid(GlobalTargetInfo[] targets, Ability ability, bool throwMessage = false)
        {
            bool anyValid = false;
            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var sexParts = pawn.GetLewdParts();
                if (sexParts is null) continue;

                var vaginas = sexParts.Vaginas;
                var penises = sexParts.Penises;
                if (vaginas.EnumerableNullOrEmpty() && penises.EnumerableNullOrEmpty() && throwMessage)
                {
                    PartsHelper.SendMessage($"{pawn.Name} does not have genitals!", pawn);
                    continue;
                }

                bool onlyArtificial = true;
                bool allMinimum = true;
                foreach (var vagina in vaginas)
                {
                    if (vagina.SexPart is Hediff_NaturalSexPart naturalVagina)
                    {
                        onlyArtificial = false;

                        float currentSeverity = naturalVagina.Severity;
                        if (currentSeverity > MinSeverity)
                        {
                            allMinimum = false;
                            break;
                        }
                    }
                }

                if (allMinimum)
                {
                    foreach (var penis in penises)
                    {
                        if (penis.SexPart is Hediff_NaturalSexPart naturalPenis)
                        {
                            onlyArtificial = false;

                            float currentSeverity = naturalPenis.Severity;
                            if (currentSeverity > MinSeverity)
                            {
                                allMinimum = false;
                                break;
                            }
                        }
                    }
                }

                if (onlyArtificial && throwMessage)
                {
                    PartsHelper.SendMessage($"Cannot change artificial genitals on {pawn.Name}", pawn);
                    continue;
                }

                if (allMinimum && throwMessage)
                {
                    PartsHelper.SendMessage($"Minimum genital size already reached for {pawn.Name}", pawn);
                    continue;
                }

                anyValid = true;
            }

            return anyValid;
        }
    }
}


code:
using RainsRJWAddons;
using RimWorld;
using RimWorld.Planet;
using rjw;
using Verse;
using Ability = VEF.Abilities.Ability;

namespace RainsVPE
{
    class VPE_PsycastDecreaseSexFulfillment : VEF.Abilities.AbilityExtension_AbilityMod
    {
        private const string EffectMoteText = "Sex need--";
        public override void Cast(GlobalTargetInfo[] targets, Ability ability)
        {
            base.Cast(targets, ability);
            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var sexNeed = pawn.needs.TryGetNeed<Need_Sex>();
                if (sexNeed is null) continue;

                var reduction = RainsRJWAddonMod.settings.sexNeedIncrement;
                sexNeed.CurLevel -= reduction;
                MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
            }
        }

        public override bool Valid(GlobalTargetInfo[] targets, Ability ability, bool throwMessage = false)
        {
            bool anyValid = false;
            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                bool hasSexNeed = pawn.RaceHasSexNeed();
                if (!hasSexNeed && throwMessage)
                {
                    PartsHelper.SendMessage($"{pawn.Name} does not have sex need!", pawn);
                    continue;
                }

                anyValid = true;
            }

            return anyValid;
        }
    }
}


code:
using RainsRJWAddons;
using RimWorld;
using RimWorld.Planet;
using rjw;
using rjw.Modules.Interactions;
using UnityEngine;
using Verse;
using Ability = VEF.Abilities.Ability;

namespace RainsVPE
{
    class VPE_PsycastIncreaseAnus : VEF.Abilities.AbilityExtension_AbilityMod
    {
        private const float MaxSeverity = 3.0f;

        private const string EffectMoteText = "Anus++";

        public override void Cast(GlobalTargetInfo[] targets, Ability ability)
        {
            base.Cast(targets, ability);

            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var sexParts = pawn.GetLewdParts()?.Anuses ?? [];

                bool anyPartModified = false;
                foreach (var anuses in sexParts)
                {
                    if (anuses.SexPart is Hediff_NaturalSexPart naturalAnus)
                    {
                        float currentSeverity = naturalAnus.Severity;
                        float boost = RainsRJWAddonMod.settings.anusIncrement;
                        float newSeverity = Mathf.Min(currentSeverity + boost, MaxSeverity);

                        naturalAnus.Severity = newSeverity;

                        var comp = anuses.SexPart.GetPartComp();
                        comp.SetSeverity(newSeverity, sync: false);
                        anyPartModified = true;
                    }
                }

                if (anyPartModified)
                {
                    MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
                }
            }
        }

        public override bool Valid(GlobalTargetInfo[] targets, Ability ability, bool throwMessage = false)
        {
            bool anyValid = false;
            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var anuses = pawn.GetLewdParts()?.Anuses;
                if (anuses.EnumerableNullOrEmpty() && throwMessage)
                {
                    PartsHelper.SendMessage($"{pawn.Name} does not have anus!", pawn);
                    continue;
                }

                bool onlyArtificial = true;
                bool allMaxed = true;
                foreach (var anus in anuses)
                {
                    if (anus.SexPart is Hediff_NaturalSexPart naturalAnus)
                    {
                        onlyArtificial = false;

                        float currentSeverity = naturalAnus.Severity;
                        if (currentSeverity < MaxSeverity)
                        {
                            allMaxed = false;
                            break;
                        }
                    }
                }

                if (onlyArtificial && throwMessage)
                {
                    PartsHelper.SendMessage($"Cannot change artificial anus on {pawn.Name}", pawn);
                    continue;
                }

                if (allMaxed && throwMessage)
                {
                    PartsHelper.SendMessage($"Maximum anus size already reached for {pawn.Name}", pawn);
                    continue;
                }

                anyValid = true;
            }

            return anyValid;
        }
    }
}


code:
using RainsRJWAddons;
using RimWorld;
using RimWorld.Planet;
using rjw;
using rjw.Modules.Interactions;
using UnityEngine;
using Verse;
using Ability = VEF.Abilities.Ability;

namespace RainsVPE
{
    public class VPE_PsycastIncreaseBreast : VEF.Abilities.AbilityExtension_AbilityMod
    {
        private const float MaxSeverity = 3.0f;

        private const string EffectMoteText = "Breasts++";
        public override void Cast(GlobalTargetInfo[] targets, Ability ability)
        {

            base.Cast(targets, ability);

            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var sexParts = pawn.GetLewdParts()?.Breasts ?? [];

                bool anyPartModified = false;
                foreach (var breast in sexParts)
                {
                    if (breast.SexPart is Hediff_NaturalSexPart naturalBreast)
                    {
                        float currentSeverity = naturalBreast.Severity;
                        float boost = RainsRJWAddonMod.settings.breastIncrement;
                        float newSeverity = Mathf.Min(currentSeverity + boost, MaxSeverity);

                        naturalBreast.Severity = newSeverity;

                        var comp = breast.SexPart.GetPartComp();
                        comp.SetSeverity(newSeverity, sync: false);
                        anyPartModified = true;
                    }
                }

                if (anyPartModified)
                {
                    MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
                }
            }

        }

        public override bool Valid(GlobalTargetInfo[] targets, Ability ability, bool throwMessage = false)
        {
            bool anyValid = false;
            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var breasts = pawn.GetLewdParts()?.Breasts;
                if (breasts.EnumerableNullOrEmpty() && throwMessage)
                {
                    PartsHelper.SendMessage($"{pawn.Name} does not have breasts!", pawn);
                    continue;
                }

                bool onlyArtificial = true;
                bool allMaxed = true;
                foreach (var breast in breasts)
                {
                    if (breast.SexPart is Hediff_NaturalSexPart naturalBreast)
                    {
                        onlyArtificial = false;

                        float currentSeverity = naturalBreast.Severity;
                        if (currentSeverity < MaxSeverity)
                        {
                            allMaxed = false;
                            break;
                        }
                    }
                }

                if (onlyArtificial && throwMessage)
                {
                    PartsHelper.SendMessage($"Cannot change artificial breasts on {pawn.Name}", pawn);
                    continue;
                }

                if (allMaxed && throwMessage)
                {
                    PartsHelper.SendMessage($"Maximum breast size already reached for {pawn.Name}", pawn);
                    continue;
                }

                anyValid = true;
            }

            return anyValid;
        }
    }
}


code:
using RainsRJWAddons;
using RimWorld;
using RimWorld.Planet;
using rjw;
using rjw.Modules.Interactions;
using UnityEngine;
using Verse;
using Ability = VEF.Abilities.Ability;

namespace RainsVPE
{
    class VPE_PsycastIncreaseBreastFluid : VEF.Abilities.AbilityExtension_AbilityMod
    {
        private const float MaxSeverity = 100.0f;

        private const string EffectMoteText = "Fluid++";
        public override void Cast(GlobalTargetInfo[] targets, Ability ability)
        {
            base.Cast(targets, ability);

            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var sexParts = pawn.GetLewdParts()?.Breasts ?? [];

                bool anyPartModified = false;
                foreach (var breast in sexParts)
                {
                    var comp = breast.SexPart.GetPartComp();
                    if (comp is not null)
                    {
                        var fluid = comp.Fluid;
                        if (fluid is null) continue;

                        var fluidMultiplier = comp.partFluidMultiplier;
                        float boost = RainsRJWAddonMod.settings.fluidIncrement;
                        comp.partFluidMultiplier = Mathf.Min(fluidMultiplier + boost, MaxSeverity);
                        anyPartModified = true;
                    }
                }

                if (anyPartModified)
                {
                    MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
                }
            }
        }

        public override bool Valid(GlobalTargetInfo[] targets, Ability ability, bool throwMessage = false)
        {
            bool anyValid = false;
            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var breasts = pawn.GetLewdParts()?.Breasts;
                if (breasts.EnumerableNullOrEmpty() && throwMessage)
                {
                    PartsHelper.SendMessage($"{pawn.Name} does not have breasts!", pawn);
                    continue;
                }

                bool allMaxed = true;
                foreach (var breast in breasts)
                {
                    var comp = breast.SexPart.GetPartComp();
                    if (comp is not null)
                    {
                        var fluid = comp.Fluid;
                        if (fluid is null) continue;

                        var fluidMultiplier = comp.partFluidMultiplier;
                        if (fluidMultiplier < MaxSeverity)
                        {
                            allMaxed = false;
                            break;
                        }
                    }

                }

                if (allMaxed && throwMessage)
                {
                    PartsHelper.SendMessage($"Maximum fluid amount reached for {pawn.Name}", pawn);
                    continue;
                }

                anyValid = true;
            }

            return anyValid;
        }
    }
}


code:
using RainsRJWAddons;
using RimWorld;
using RimWorld.Planet;
using rjw;
using rjw.Modules.Interactions;
using System.Collections.Generic;
using UnityEngine;
using Verse;
using Ability = VEF.Abilities.Ability;

namespace RainsVPE
{
    class VPE_PsycastIncreaseGenitalia : VEF.Abilities.AbilityExtension_AbilityMod
    {
        private const float MaxSeverity = 3.0f;

        private const string EffectMoteTextV = "Vagina++";

        private const string EffectMoteTextP = "Penis++";

        public override void Cast(GlobalTargetInfo[] targets, Ability ability)
        {
            base.Cast(targets, ability);

            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var sexParts = pawn.GetLewdParts();
                if (sexParts is null) continue;

                IncreaseSexPartSeverity(sexParts.Vaginas, false, RainsRJWAddonMod.settings.vaginaIncrement, EffectMoteTextV, pawn);
                IncreaseSexPartSeverity(sexParts.Penises, false, RainsRJWAddonMod.settings.penisIncrement, EffectMoteTextP, pawn);
            }
        }

        public void IncreaseSexPartSeverity(List<RJWLewdablePart> sexParts, bool partModified, float boost, string moteText, Pawn pawn)
        {
            if (sexParts.EnumerableNullOrEmpty()) return;

            foreach (var part in sexParts)
            {
                if (part.SexPart is Hediff_NaturalSexPart naturalPart)
                {
                    var currentSeverity = naturalPart.Severity;
                    var newSeverity = Mathf.Min(currentSeverity + boost, MaxSeverity);

                    naturalPart.Severity = newSeverity;

                    var comp = part.SexPart.GetPartComp();
                    comp.SetSeverity(newSeverity, sync: false);
                    partModified = true;
                }
            }

            if (partModified)
            {
                MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, moteText.Translate(), 2f);
            }

        }

        public override bool Valid(GlobalTargetInfo[] targets, Ability ability, bool throwMessage = false)
        {
            bool anyValid = false;
            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var sexParts = pawn.GetLewdParts();
                if (sexParts is null) continue;

                var vaginas = sexParts.Vaginas;
                var penises = sexParts.Penises;
                if (vaginas.EnumerableNullOrEmpty() && penises.EnumerableNullOrEmpty() && throwMessage)
                {
                    PartsHelper.SendMessage($"{pawn.Name} does not have genitals!", pawn);
                    continue;
                }

                bool onlyArtificial = true;
                bool allMaxed = true;
                foreach (var vagina in vaginas)
                {
                    if (vagina.SexPart is Hediff_NaturalSexPart naturalVagina)
                    {
                        onlyArtificial = false;

                        float currentSeverity = naturalVagina.Severity;
                        if (currentSeverity < MaxSeverity)
                        {
                            allMaxed = false;
                            break;
                        }
                    }
                }

                if (allMaxed)
                {
                    foreach (var penis in penises)
                    {
                        if (penis.SexPart is Hediff_NaturalSexPart naturalPenis)
                        {
                            onlyArtificial = false;

                            float currentSeverity = naturalPenis.Severity;
                            if (currentSeverity < MaxSeverity)
                            {
                                allMaxed = false;
                                break;
                            }
                        }
                    }
                }
                if (onlyArtificial && throwMessage)
                {
                    PartsHelper.SendMessage($"Cannot change artificial genitals on {pawn.Name}", pawn);
                    continue;
                }

                if (allMaxed && throwMessage)
                {
                    PartsHelper.SendMessage($"Maximum genital size already reached for {pawn.Name}", pawn);
                    continue;
                }

                anyValid = true;
            }

            return anyValid;
        }
    }
}


code:
using RainsRJWAddons;
using RimWorld;
using RimWorld.Planet;
using rjw;
using rjw.Modules.Interactions;
using UnityEngine;
using Verse;
using Ability = VEF.Abilities.Ability;

namespace RainsVPE
{
    class VPE_PsycastIncreasePenisFluid : VEF.Abilities.AbilityExtension_AbilityMod
    {
        private const float MaxSeverity = 100.0f;

        private const string EffectMoteText = "Fluid++";

        public override void Cast(GlobalTargetInfo[] targets, Ability ability)
        {
            base.Cast(targets, ability);

            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var sexParts = pawn.GetLewdParts()?.Penises ?? [];

                bool anyPartModified = false;
                foreach (var penis in sexParts)
                {
                    var comp = penis.SexPart.GetPartComp();
                    if (comp is not null)
                    {
                        var fluid = comp.Fluid;
                        if (fluid is null) continue;

                        var fluidMultiplier = comp.partFluidMultiplier;
                        float boost = RainsRJWAddonMod.settings.fluidIncrement;
                        comp.partFluidMultiplier = Mathf.Min(fluidMultiplier + boost, MaxSeverity);
                        anyPartModified = true;
                    }
                }

                if (anyPartModified)
                {
                    MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
                }
            }
        }

        public override bool Valid(GlobalTargetInfo[] targets, Ability ability, bool throwMessage = false)
        {
            bool anyValid = false;
            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var penises = pawn.GetLewdParts()?.Penises;
                if (penises.EnumerableNullOrEmpty() && throwMessage)
                {
                    PartsHelper.SendMessage($"{pawn.Name} does not have penis!", pawn);
                    continue;
                }

                bool allMaxed = true;
                foreach (var penis in penises)
                {
                    var comp = penis.SexPart.GetPartComp();
                    if (comp is not null)
                    {
                        var fluid = comp.Fluid;
                        if (fluid is null) continue;

                        var fluidMultiplier = comp.partFluidMultiplier;
                        if (fluidMultiplier < MaxSeverity)
                        {
                            allMaxed = false;
                            break;
                        }

                    }
                }

                if (allMaxed && throwMessage)
                {
                    PartsHelper.SendMessage($"Maximum fluid amount reached for {pawn.Name}", pawn);
                    continue;
                }

                anyValid = true;
            }

            return anyValid;
        }
    }
}


code:
using RainsRJWAddons;
using RimWorld;
using RimWorld.Planet;
using rjw;
using Verse;
using Ability = VEF.Abilities.Ability;

namespace RainsVPE
{
    class VPE_PsycastIncreaseSexFulfillment : VEF.Abilities.AbilityExtension_AbilityMod
    {

        private const string EffectMoteText = "Sex need++";
        public override void Cast(GlobalTargetInfo[] targets, Ability ability)
        {
            base.Cast(targets, ability);
            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                var sexNeed = pawn.needs.TryGetNeed<Need_Sex>();
                if (sexNeed is null) continue;

                var boost = RainsRJWAddonMod.settings.sexNeedIncrement;
                sexNeed.CurLevel += boost;
                MoteMaker.ThrowText(pawn.DrawPos, pawn.Map, EffectMoteText.Translate(), 2f);
            }
        }

        public override bool Valid(GlobalTargetInfo[] targets, Ability ability, bool throwMessage = false)
        {
            bool anyValid = false;
            foreach (GlobalTargetInfo globalTargetInfo in targets)
            {

                if (globalTargetInfo.Pawn is not { } pawn || pawn.Dead) continue;

                bool hasSexNeed = pawn.RaceHasSexNeed();
                if (!hasSexNeed && throwMessage)
                {
                    PartsHelper.SendMessage($"{pawn.Name} does not have sex need!", pawn);
                    continue;
                }

                anyValid = true;
            }

            return anyValid;
        }
    }
}


code:
using System.Reflection;
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("RainsVPE")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("RainsVPE")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[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("beb31732-57a1-4a02-8aa9-ad9f69c602a1")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]


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>{BEB31732-57A1-4A02-8AA9-AD9F69C602A1}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>RainsVPE</RootNamespace>
    <AssemblyName>RainsVPE</AssemblyName>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
    <LangVersion>preview</LangVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>..\1.6\Mods\PsycastExpanded\Assemblies\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>portable</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>..\1.6\Mods\PsycastExpanded\Assemblies\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <DebugSymbols>true</DebugSymbols>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="Assembly-CSharp">
      <HintPath>..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="RJW">
      <HintPath>..\..\rjw\1.6\Assemblies\RJW.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Xml" />
    <Reference Include="UnityEngine">
      <HintPath>..\..\..\RimWorldWin64_Data\Managed\UnityEngine.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="UnityEngine.CoreModule">
      <HintPath>..\..\..\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="VanillaPsycastsExpanded">
      <HintPath>..\..\2842502659\1.6\Assemblies\VanillaPsycastsExpanded.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="VEF">
      <HintPath>..\..\2023507013\1.6\Assemblies\VEF.dll</HintPath>
      <Private>False</Private>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Comps\VPE_PsycastDecreaseSexFulfillment.cs" />
    <Compile Include="Comps\VPE_PsycastIncreaseSexFulfillment.cs" />
    <Compile Include="Comps\VPE_PsycastIncreasePenisFluid.cs" />
    <Compile Include="Comps\VPE_PsycastIncreaseBreastFluid.cs" />
    <Compile Include="Comps\VPE_PsycastDecreaseGenitalia.cs" />
    <Compile Include="Comps\VPE_PsycastIncreaseGenitalia.cs" />
    <Compile Include="Comps\VPE_PsycastDecreaseAnus.cs" />
    <Compile Include="Comps\VPE_PsycastIncreaseAnus.cs" />
    <Compile Include="Comps\VPE_PsycastDecreaseBreast.cs" />
    <Compile Include="Comps\VPE_PsycastIncreaseBreast.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\RainsRJWAddons\RainsRJWAddons.csproj">
      <Project>{7259745d-73f8-4097-b641-13b27bf7140f}</Project>
      <Name>RainsRJWAddons</Name>
      <Private>False</Private>
    </ProjectReference>
  </ItemGroup>
  <ItemGroup />
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>


code:
<loadFolders>
	<v1.5>

/

1.5

1.5/Mods/PsycastExpanded

	</v1.5>
	<v1.6>

/

1.6

1.6/Mods/PsycastExpanded

	</v1.6>
</loadFolders>


code:
*.bat	text eol=crlf
*.tw	text eol=lf
*.sh	text eol=lf
*.py	text eol=lf
*.txt	text eol=lf
compile	text eol=lf
compile-git	text eol=lf
sanityCheck	text eol=lf

repo_name: Anony/fc

path: .gitattributes


code:
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
*.swp
*.swo

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# IPython Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv
venv/
ENV/

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject

# Start.tw
src/config/start.tw

path: .gitignore


code:
further development:
-specialized slave schools
-fortifications
-more levels for militia edict (further militarize society)
-conquering other arcologies?

Events:
-famous criminal escapes to the arcology, followed by another arcology police force

Bugs:
-sometimes troop counts breaks
-sometimes rebel numbers have fractionary parts


code:
# How to vector art

Please read the whole document before starting to work.
Some aspects are not obvious right away.

Note: This does not actually describe how to be an artist.

## TL;DR

    killall inkscape
    artTools/vector_layer_split.py artTools/vector_source.svg tw src/art/vector/layers/
    compile

## 1. Be an artist

Make changes to the vector_source.svg.
Inkscape was thoroughly tested.
Adobe Illustrator might work decently, too.

## 2. Respect the structure

While editing, keep the Layers in mind.

* In Inkscape, Layers are special "Inkscape groups".
* In Illustrator, Layers are groups with user-definded IDs.
* All Layers should have an ID that is globally unique
  (not just within their subtree).

* Please use anonymous groups only for the ease of editing. Remove all "helper" groups before finally saving the file.
* Anonymous groups can be used for continous scaling (of e.g. boobs).

* Every asset that should go into a separate file needs to be in a labelled group
  (even if that is a group with only one shape).
* There are some globally available styles defined as CSS classes (e.g. skin, hair).
  Use them if your asset should be changed upon display.
  Do not set the style directly in the object.

## 3. Normalise the document (before committing)

**THIS IS IMPORTANT**

The various editors out there (Inkscape, Illustrator) may use different variants of formatting the SVG XML.
Use

    python3 normalize_svg.py vector_source.svg

before committing to normalise the format so git will not freak out about the changed indentation.

If you use Inkscape, please use in Edit → Settings → Input/Output → SVG-Output → Path Data → Optimized. This is the default.
In case your Editor uses another path data style which cannot be changed, please contact the other artists and developers via the issue tracker to find a new common ground.

What it does:
* Formats the SVG XML according to Pythons lxml module, regardless of editor.
* Adobe Illustrator uses group IDs as layer labels.
  Inkscape however uses layer labels and a separate, auto-generated group ID.
  normalize_svg.py overwrites the group ID with the Inkscape layer label
  so they are synchronised with Inkscape layer labels.
* Inkscape copies the global style definition into the object *every time*
  the object is edited. If an object references a CSS class AND at the same time
  has a local style, the local style is removed
  so global dynamic styling is possible later on.

Note: Behaviour of Adobe Illustrator is untested.

## 4. Split the layers

For NoX original, deepurk extensions, execute

    python3 vector_layer_split.py vector_source_ndmain.svg tw ../src/art/vector/layers/

For faraen revamped art (based on NoX original)

	python3 vector_revamp_layer_split.py vector_revamp_source.svg tw ../src/art/vector_revamp/layers/

. This application reads all groups in `vector_source.svg`.
Each group is stored in a separate file in the target directory `/src/art/vector/layers/`.
The group ID sets the file name. Therefore, the group ID **must not** contain spaces or any other weird characters.

Also consider:
* A group with ID ending in _ is ignored. Use Layers ending in _ to keep overview.
* A group with ID starting with "g" (Inkscape) or "XMLID" (Illustrator) is also ignored.
* Existing files are overwritten **without enquiry**.
* The target directory is not emptied. If a file is no longer needed, you should remove it manually.
* This procedure removes global definitions. This means, SVG filters are currently not supported.

Available output formats are `svg` and `tw`.
`svg` output exists for debug reasons.
`tw` embeds the SVG data into Twine files, but removes the global style definitions so they can be set during display.

## 5. Edit the code

`/src/art/` contains Twine code which shows the assets in the story.
There are many helpful comments in `/src/art/artWidgets.tw`.
The code also generates the previously removed global style definitions on the fly and per display.

## 6. Compile the story

Use the provided compile script (Windows batch or shell) for compiling.

## 7. Advanced usage

You can define multiple CSS classes to one element, e.g. "skin torso". When procedurally generating CSS, you can then set a global "skin" style, and a different one for "torso".

You can put variable text into the image using single quotes, e.g. "'+_tattooText+'". The single quotes *break* the quoting in Twine so the content of the Twine variable `_tattooText` is shown upon display. You can even align text on paths.

An anonymous group can be used for dynamic transformation. However, finding the correct parameters for the affine transformation matrix can be cumbersome. Use any method you see fit. See `src/art/vector/Boob.tw` for one example of the result.