code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using UnityEngine;
using RimWorld;

namespace Rimworld_Animations {

    public class AnimationSettings : ModSettings {

        public static bool orgasmQuiver, rapeShiver, soundOverride = true, hearts = true, controlGenitalRotation = false, applySemenOnAnimationOrgasm = false, fastAnimForQuickie = false,
            PlayAnimForNonsexualActs = true;
        public static bool offsetTab = false, debugMode = false;
        public static float shiverIntensity = 2f;

        public static Dictionary<string, Vector2> offsets = new Dictionary<string, Vector2>();
        public static Dictionary<string, float> rotation = new Dictionary<string, float>();

        public override void ExposeData() {

            base.ExposeData();

            Scribe_Values.Look(ref debugMode, "RJWAnimations-AnimsDebugMode", false);
            Scribe_Values.Look(ref offsetTab, "RJWAnimations-EnableOffsetTab", false);
            Scribe_Values.Look(ref controlGenitalRotation, "RJWAnimations-controlGenitalRotation", false);
            Scribe_Values.Look(ref orgasmQuiver, "RJWAnimations-orgasmQuiver");
            Scribe_Values.Look(ref fastAnimForQuickie, "RJWAnimations-fastAnimForQuickie");
            Scribe_Values.Look(ref rapeShiver, "RJWAnimations-rapeShiver");
            Scribe_Values.Look(ref hearts, "RJWAnimation-sheartsOnLovin");
            Scribe_Values.Look(ref PlayAnimForNonsexualActs, "RJWAnims-PlayAnimForNonsexualActs");
            Scribe_Values.Look(ref applySemenOnAnimationOrgasm, "RJWAnimations-applySemenOnOrgasm", false);
            Scribe_Values.Look(ref soundOverride, "RJWAnimations-rjwAnimSoundOverride", true);
            Scribe_Values.Look(ref shiverIntensity, "RJWAnimations-shiverIntensity", 2f);
            //todo: save offsetsByDefName

            Scribe_Collections.Look(ref offsets, "RJWAnimations-animationOffsets");
            Scribe_Collections.Look(ref rotation, "RJWAnimations-rotationOffsets");

            //needs to be rewritten
            //probably somewhere in options?

        }

    }

    public class RJW_Animations : Mod {

        public RJW_Animations(ModContentPack content) : base(content) {
            GetSettings<AnimationSettings>();

        }

        public override void DoSettingsWindowContents(Rect inRect) {

            Listing_Standard listingStandard = new Listing_Standard();
            listingStandard.Begin(inRect);

            listingStandard.CheckboxLabeled("Enable Sound Override", ref AnimationSettings.soundOverride);
            listingStandard.CheckboxLabeled("Control Genital Rotation", ref AnimationSettings.controlGenitalRotation);
            listingStandard.CheckboxLabeled("Play Fast Animation for Quickie", ref AnimationSettings.fastAnimForQuickie);
            listingStandard.CheckboxLabeled("Apply Semen on Animation Orgasm", ref AnimationSettings.applySemenOnAnimationOrgasm);

            if(AnimationSettings.applySemenOnAnimationOrgasm) {
                listingStandard.Label("Recommended--turn down \"Cum on body percent\" in RJW settings to about 33%");
            }

            listingStandard.CheckboxLabeled("Enable Orgasm Quiver", ref AnimationSettings.orgasmQuiver);
            listingStandard.CheckboxLabeled("Enable Rape Shiver", ref AnimationSettings.rapeShiver);
            listingStandard.CheckboxLabeled("Enable hearts during lovin'", ref AnimationSettings.hearts);
            listingStandard.CheckboxLabeled("Play animation for nonsexual acts (handholding, makeout)", ref AnimationSettings.PlayAnimForNonsexualActs);
            listingStandard.CheckboxLabeled("Enable Animation Manager Tab", ref AnimationSettings.offsetTab);

            listingStandard.Label("Shiver/Quiver Intensity (default 2): " + AnimationSettings.shiverIntensity);
            AnimationSettings.shiverIntensity = listingStandard.Slider(AnimationSettings.shiverIntensity, 0.0f, 12f);

            listingStandard.CheckboxLabeled("Debug Mode", ref AnimationSettings.debugMode);

            listingStandard.End();
            base.DoSettingsWindowContents(inRect);
        }

        public override void WriteSettings() {
            base.WriteSettings();
            OffsetMainButtonDefOf.OffsetManager.buttonVisible = AnimationSettings.offsetTab;

        }

        public override string SettingsCategory() {
            return "RJW Animation Settings";
        }
    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using rjw.Modules.Interactions.Helpers;
using rjw.Modules.Interactions.Objects;
using UnityEngine;
using Verse;
using Verse.AI;
using rjw.Modules.Interactions.Enums;

namespace Rimworld_Animations {
    public static class AnimationUtility {
        /*
         Note: always make the list in this order:
             Female pawns, animal female pawns, male pawns, animal male pawns
        */
        public static AnimationDef tryFindAnimation(ref List<Pawn> participants, rjw.xxx.rjwSextype sexType = 0, rjw.SexProps sexProps = null) {

            InteractionWithExtension interaction = InteractionHelper.GetWithExtension(sexProps.dictionaryKey);

            if(interaction.HasInteractionTag(InteractionTag.Reverse))
            {
                Pawn buffer = participants[1];
                participants[1] = participants[0];
                participants[0] = buffer;
            }

            participants =
                participants.OrderBy(p => p.jobs.curDriver is rjw.JobDriver_SexBaseInitiator)
                .OrderBy(p => rjw.xxx.can_fuck(p))
                .ToList();

            List<Pawn> localParticipants = new List<Pawn>(participants);

            IEnumerable<AnimationDef> options = DefDatabase<AnimationDef>.AllDefs.Where((AnimationDef x) => {

                if (x.actors.Count != localParticipants.Count) {
                    return false;
                }
                for (int i = 0; i < x.actors.Count; i++) {

                    if (rjw.RJWPreferenceSettings.Malesex == rjw.RJWPreferenceSettings.AllowedSex.Nohomo) {
                        if (rjw.xxx.is_male(localParticipants[i]) && x.actors[i].isFucked) {
                            return false;
                        }
                }
				if (x.actors[i].requiredGender != null && !x.actors[i].requiredGender.Contains(localParticipants[i].gender.ToStringSafe<Gender>()))
				{
					if (AnimationSettings.debugMode)
					{
						Log.Message(string.Concat(new string[]
						{
							x.defName.ToStringSafe<string>(),
							" not selected -- ",
							localParticipants[i].def.defName.ToStringSafe<string>(),
							" ",
							localParticipants[i].Name.ToStringSafe<Name>(),
							" does not match required gender"
						}));
					}
					return false;
				}
                    if ((x.actors[i].blacklistedRaces != null) && x.actors[i].blacklistedRaces.Contains(localParticipants[i].def.defName)) {
                        if(AnimationSettings.debugMode)
                            Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is blacklisted");
                        return false;
                    }

                    if(x.actors[i].defNames.Contains("Human")) {
                        if (!rjw.xxx.is_human(localParticipants[i])) {
                            if (AnimationSettings.debugMode)
                                Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is not human");

                            return false;
                        }

                    }
                    else if (!x.actors[i].bodyDefTypes.Contains(localParticipants[i].RaceProps.body)) {

                        if (!x.actors[i].defNames.Contains(localParticipants[i].def.defName)) {

                            if (rjw.RJWSettings.DevMode) {
                                string animInfo = x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is not ";
                                foreach(String defname in x.actors[i].defNames) {
                                    animInfo += defname + ", ";
                                }
                                if (AnimationSettings.debugMode)
                                    Log.Message(animInfo);
                            }

                            return false;
                        }
                    }
                    //genitals checking

                    if(!GenitalCheckForPawn(x.actors[i].requiredGenitals, localParticipants[i], out string failReason)) {
                        Debug.Log("Didn't select " + x.defName + ", " + localParticipants[i].Name + " " + failReason);
                        return false;
                    }

                    //TESTING ANIMATIONS ONLY REMEMBER TO COMMENT OUT BEFORE PUSH
                    /*
                    if (x.defName != "Cunnilingus")
                        return false;
                    */

                    if (x.actors[i].isFucking && !rjw.xxx.can_fuck(localParticipants[i])) {
                        if (AnimationSettings.debugMode)
                            Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " can't fuck");
                        return false;
                    }

                    if (x.actors[i].isFucked && !rjw.xxx.can_be_fucked(localParticipants[i])) {
                        if (AnimationSettings.debugMode)
                            Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " can't be fucked");
                        return false;
                    }
                }
                return true;
            });
            List<AnimationDef> optionsWithInteractionType = options.ToList().FindAll(x => x.interactionDefTypes != null && x.interactionDefTypes.Contains(sexProps.sexType.ToStringSafe()));
            if (optionsWithInteractionType.Any()) {
                if (AnimationSettings.debugMode)
                    Log.Message("Selecting animation for interaction type " + sexProps.sexType.ToStringSafe() + "...");
                return optionsWithInteractionType.RandomElement();
            }
            List<AnimationDef> optionsWithSexType = options.ToList().FindAll(x => x.sexTypes != null && x.sexTypes.Contains(sexType));
            if (optionsWithSexType.Any()) {
                if (AnimationSettings.debugMode)
                    Log.Message("Selecting animation for rjwSexType " + sexType.ToStringSafe() + "...");
                return optionsWithSexType.RandomElement();
            }

            /*
            if(optionsWithInitiator.Any()) {
                if (AnimationSettings.debugMode)
                    Log.Message("Selecting animation for initiators...");
            }
            */

            if (options != null && options.Any()) {
                if (AnimationSettings.debugMode)
                    Log.Message("Randomly selecting animation...");
                return options.RandomElement();
            }
            else
                return null;
        }

        public static void RenderPawnHeadMeshInAnimation1(Mesh mesh, Vector3 loc, Quaternion quaternion, Material material, bool drawNow, Pawn pawn) {

            if (pawn == null || pawn.Map != Find.CurrentMap) {
                GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, drawNow);
                return;
            }

            CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();

            if (pawnAnimator == null || !pawnAnimator.isAnimating) {
                GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, drawNow);
            } else {
                Vector3 pawnHeadPosition = pawnAnimator.getPawnHeadPosition();
                pawnHeadPosition.y = loc.y;
                GenDraw.DrawMeshNowOrLater(MeshPool.humanlikeHeadSet.MeshAt(pawnAnimator.headFacing), pawnHeadPosition, Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up), material, true);
            }
        }

        public static void AdjustHead(ref Quaternion quat, ref Rot4 bodyFacing, ref Vector3 pos, ref float angle, Pawn pawn, PawnRenderFlags flags)
        {
            if (flags.FlagSet(PawnRenderFlags.Portrait)) return;

            CompBodyAnimator anim = pawn.TryGetComp<CompBodyAnimator>();
            if (anim.isAnimating)
            {
                bodyFacing = anim.headFacing;
                angle = anim.headAngle;
                quat = Quaternion.AngleAxis(anim.headAngle, Vector3.up);
                pos = anim.getPawnHeadOffset();

            }
        }

        public static void RenderPawnHeadMeshInAnimation(Mesh mesh, Vector3 loc, Quaternion quaternion, Material material, bool portrait, Pawn pawn, float bodySizeFactor = 1) {

            if (pawn == null) {
                GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
                return;
            }

            CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();

            if (pawnAnimator == null || !pawnAnimator.isAnimating || portrait) {
                GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
            }
            else {
                Vector3 pawnHeadPosition = pawnAnimator.getPawnHeadPosition();
                pawnHeadPosition.x *= bodySizeFactor;
                pawnHeadPosition.x *= bodySizeFactor;
                pawnHeadPosition.y = loc.y;
                GenDraw.DrawMeshNowOrLater(mesh, pawnHeadPosition, Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up), material, portrait);
            }
        }

        public static bool GenitalCheckForPawn(List<string> requiredGenitals, Pawn pawn, out string failReason) {

            failReason = null;
            if (requiredGenitals != null) {
                if (requiredGenitals.Contains("Vagina")) {

                    if (!rjw.Genital_Helper.has_vagina(pawn)) {
                        failReason = "missing vagina";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("Penis")) {

                    if (!(rjw.Genital_Helper.has_multipenis(pawn) || rjw.Genital_Helper.has_penis_infertile(pawn) || rjw.Genital_Helper.has_penis_fertile(pawn) || rjw.Genital_Helper.has_ovipositorM(pawn) || rjw.Genital_Helper.has_ovipositorF(pawn))) {
                        failReason = "missing penis";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("Mouth")) {

                    if (!rjw.Genital_Helper.has_mouth(pawn)) {
                        failReason = "missing mouth";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("Anus")) {

                    if (!rjw.Genital_Helper.has_anus(pawn)) {
                        failReason = "missing anus";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("Breasts")) {
                    if (!rjw.Genital_Helper.can_do_breastjob(pawn)) {
                        failReason = "missing breasts";
                        return false;
                    }
                }

                if (requiredGenitals.Contains("NoVagina")) {

                    if (rjw.Genital_Helper.has_vagina(pawn)) {
                        failReason = "has vagina";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("NoPenis")) {

                    if ((rjw.Genital_Helper.has_multipenis(pawn) || rjw.Genital_Helper.has_penis_infertile(pawn) || rjw.Genital_Helper.has_penis_fertile(pawn))) {
                        failReason = "has penis";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("NoMouth")) {

                    if (rjw.Genital_Helper.has_mouth(pawn)) {
                        failReason = "has mouth";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("NoAnus")) {

                    if (rjw.Genital_Helper.has_anus(pawn)) {
                        failReason = "has anus";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("NoBreasts")) {
                    if (rjw.Genital_Helper.can_do_breastjob(pawn)) {
                        failReason = "has breasts";
                        return false;
                    }
                }
            }

            return true;

        }

        public static Rot4 PawnHeadRotInAnimation(Pawn pawn, Rot4 regularPos)
        {
            Debug.Log("Test");

            if(pawn?.TryGetComp<CompBodyAnimator>() != null && pawn.TryGetComp<CompBodyAnimator>().isAnimating)
            {
                return pawn.TryGetComp<CompBodyAnimator>().headFacing;
            }

            return regularPos;
        }
    }
}

repo_name: Valianteffort/rimworld

license: unknown


code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Verse;

namespace Rimworld_Animations
{
    public class PatchOperationAddOrReplace : PatchOperationPathed
    {

        protected string key;
        private XmlContainer value;

        protected override bool ApplyWorker(XmlDocument xml)
        {
            XmlNode valNode = value.node;
            bool result = false;
            IEnumerator enumerator = xml.SelectNodes(xpath).GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    object obj = enumerator.Current;
                    result = true;
                    XmlNode parentNode = obj as XmlNode;
                    XmlNode xmlNode = parentNode.SelectSingleNode(key);
                    if (xmlNode == null)
                    {
                        // Add - Add node if not existing
                        xmlNode = parentNode.OwnerDocument.CreateElement(key);
                        parentNode.AppendChild(xmlNode);
                    }
                    else
                    {
                        // Replace - Clear existing children
                        xmlNode.RemoveAll();
                    }
                    // (Re)add value
                    xmlNode.AppendChild(parentNode.OwnerDocument.ImportNode(valNode.FirstChild, true));
                }
            }
            finally
            {
                IDisposable disposable = enumerator as IDisposable;
                if (disposable != null)
                {
                    disposable.Dispose();
                }
            }
            return result;
        }

    }

}


code:
<?xml version="1.0" encoding="utf-8"?>

<ModMetaData>
	<name>Rimworld-Animations</name>
	<author>C0ffee</author>
  <url>https://gitgud.io/c0ffeeeeeeee/rimworld-animations</url>
	<supportedVersions>

1.1

1.2

1.3

1.4

	</supportedVersions>
	<packageId>c0ffee.rimworld.animations</packageId>
	<modDependencies>

			<packageId>brrainz.harmony</packageId>
			<displayName>Harmony</displayName>
			<steamWorkshopUrl>steam://url/CommunityFilePage/2009463077</steamWorkshopUrl>
			<downloadUrl>https://github.com/pardeike/HarmonyRimWorld/releases/latest</downloadUrl>

			<packageId>UnlimitedHugs.HugsLib</packageId>
			<displayName>HugsLib</displayName>
			<downloadUrl>https://github.com/UnlimitedHugs/RimworldHugsLib/releases/latest</downloadUrl>
			<steamWorkshopUrl>steam://url/CommunityFilePage/818773962</steamWorkshopUrl>

			<packageId>rim.job.world</packageId>
			<displayName>RimJobWorld</displayName>
			<downloadUrl>https://www.loverslab.com/topic/110270-mod-rimjobworld/</downloadUrl>

			<packageId>erdelf.HumanoidAlienRaces</packageId>
			<displayName>Humanoid Alien Races 2.0</displayName>
			<steamWorkshopUrl>https://steamcommunity.com/sharedfiles/filedetails/?id=839005762</steamWorkshopUrl>

	</modDependencies>
	<loadAfter>

UnlimitedHugs.HugsLib

brrainz.harmony

erdelf.humanoidalienraces

nals.facialanimation

com.yayo.yayoAni

	</loadAfter>
	<loadBefore>
	</loadBefore>
	<incompatibleWith>
	</incompatibleWith>
	<description>
    Rimworld Animations! Hurray!

    Questions or bugs?
    Chat with me on the forums: https://www.loverslab.com/topic/140386-rjw-animations/
    Or on the rjw discord: https://discord.gg/CXwHhv8

  </description>
</ModMetaData>


code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Manifest>
  <identifier>Rimworld-Animations</identifier>
  <version>1.3.0</version>
</Manifest>


code:
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>

  <OffsetManager.label>gerenciador de posições</OffsetManager.label>
  <OffsetManager.description>Controla as posições sexuais dos colonos.</OffsetManager.description>

</LanguageData>


code:
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>

  <Dog_Doggystyle.label>estilo cachorrinho</Dog_Doggystyle.label>
  <Horse_Cowgirl.label>vaqueira</Horse_Cowgirl.label>

</LanguageData>


code:
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>

  <Tribadism.label>tribadismo</Tribadism.label>
  <Cunnilingus.label>cunilíngua</Cunnilingus.label>

</LanguageData>


code:
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>

  <Double_Penetration.label>dupla penetração</Double_Penetration.label>

</LanguageData>


code:
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>

  <Doggystyle.label>estilo cachorrinho</Doggystyle.label>
  <Blowjob.label>boquete</Blowjob.label>
  <ReverseStandAndCarry.label>69</ReverseStandAndCarry.label>
  <Cowgirl.label>vaqueira</Cowgirl.label>

</LanguageData>


code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<loadFolders>
	<v1.1>

/

1.1

	</v1.1>
	<v1.2>

/

1.2

Patch_HatsDisplaySelection/1.2

	</v1.2>
	<v1.3>

/

1.3

Patch_HatsDisplaySelection/1.2

Patch_SexToysMasturbation

Patch_SexToysMasturbation/1.3

	</v1.3>
	<v1.4>

/

1.4

Patch_HatsDisplaySelection/1.2

Patch_SexToysMasturbation

Patch_SexToysMasturbation/1.4

	</v1.4>

</loadFolders>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
	<Patch_SexToysMasturbation.SexToyAnimationDef>
		<defName>Masturbation_DildoVaginal</defName>
		dildo masturbation
		<requiredBodyPart>vagina</requiredBodyPart>
		<sounds>true</sounds>
		<sexTypes>

Masturbation

		</sexTypes>
		<actors>

				<defNames>

Human

				</defNames>
				<isFucked>true</isFucked>

		</actors>

		<animationStages>

				<stageName>Masturbating</stageName>
				<isLooping>true</isLooping>
				<playTimeTicks>917</playTimeTicks>
				<stageIndex>0</stageIndex>
				<animationClips>

						<layer>LayingPawn</layer>
						<keyframes>

								<tickDuration>40</tickDuration>
								<bodyAngle>73.01611</bodyAngle>
								<headAngle>40.0739746</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.054543376</bodyOffsetZ>
								<bodyOffsetX>0.112624526</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<soundEffect>Slimy</soundEffect>
								<tickDuration>30</tickDuration>
								<bodyAngle>76.4867554</bodyAngle>
								<headAngle>45.3887634</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.0506898165</bodyOffsetZ>
								<bodyOffsetX>0.08564949</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>30</tickDuration>
								<bodyAngle>78.22131</bodyAngle>
								<headAngle>48.0072327</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.039129138</bodyOffsetZ>
								<bodyOffsetX>0.07794231</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>30</tickDuration>
								<bodyAngle>76.4867554</bodyAngle>
								<headAngle>45.3887634</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.0506898165</bodyOffsetZ>
								<bodyOffsetX>0.08564949</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>1</tickDuration>
								<bodyAngle>73.01611</bodyAngle>
								<headAngle>40.0739746</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.054543376</bodyOffsetZ>
								<bodyOffsetX>0.112624526</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

						</keyframes>

						<keyframes>

								<tickDuration>40</tickDuration>
								<positionX>-0.359264076</positionX>
								<positionZ>-0.00901746</positionZ>
								<rotation>114.011215</rotation>

								<tickDuration>30</tickDuration>
								<positionX>-0.2783391</positionX>
								<positionZ>-0.0514066666</positionZ>
								<rotation>81.16443</rotation>

								<tickDuration>30</tickDuration>
								<positionX>-0.1704393</positionX>
								<positionZ>-0.0668209046</positionZ>
								<rotation>72.8611145</rotation>

								<tickDuration>30</tickDuration>
								<positionX>-0.2783391</positionX>
								<positionZ>-0.0514066666</positionZ>
								<rotation>81.16443</rotation>

								<tickDuration>1</tickDuration>
								<positionX>-0.359264076</positionX>
								<positionZ>-0.00901746</positionZ>
								<rotation>114.011215</rotation>

						</keyframes>

				</animationClips>

				<stageName>GettingIntoPosition</stageName>
				<isLooping>false</isLooping>
				<stageIndex>0</stageIndex>
				<animationClips>

						<layer>LayingPawn</layer>
						<keyframes>

								<tickDuration>50</tickDuration>
								<bodyAngle>73.01611</bodyAngle>
								<headAngle>40.0739746</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.054543376</bodyOffsetZ>
								<bodyOffsetX>0.112624526</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<soundEffect>Slimy</soundEffect>
								<tickDuration>1</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

						</keyframes>

						<keyframes>

								<tickDuration>50</tickDuration>
								<positionX>-0.359264076</positionX>
								<positionZ>-0.00901746</positionZ>
								<rotation>114.011215</rotation>

								<tickDuration>1</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

						</keyframes>

				</animationClips>

				<stageName>FastMasturbation</stageName>
				<isLooping>true</isLooping>
				<stageIndex>0</stageIndex>
				<playTimeTicks>1610</playTimeTicks>
				<animationClips>

						<layer>LayingPawn</layer>
						<keyframes>

								<tickDuration>20</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<soundEffect>Slimy</soundEffect>
								<tickDuration>25</tickDuration>
								<bodyAngle>85.17255</bodyAngle>
								<headAngle>58.0615845</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03527552</bodyOffsetZ>
								<bodyOffsetX>0.0471138731</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>1</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

						</keyframes>

						<keyframes>

								<tickDuration>25</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

								<tickDuration>20</tickDuration>
								<positionX>-0.178146541</positionX>
								<positionZ>-0.01672452</positionZ>
								<rotation>96.95889</rotation>

								<tickDuration>1</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

						</keyframes>

				</animationClips>

				<stageName>VeryFastMasturbation</stageName>
				<isLooping>true</isLooping>
				<stageIndex>0</stageIndex>
				<playTimeTicks>225</playTimeTicks>
				<animationClips>

						<layer>LayingPawn</layer>
						<keyframes>

								<tickDuration>6</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<soundEffect>Slimy</soundEffect>
								<tickDuration>8</tickDuration>
								<bodyAngle>85.17255</bodyAngle>
								<headAngle>58.0615845</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03527552</bodyOffsetZ>
								<bodyOffsetX>0.0471138731</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>1</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

						</keyframes>

						<keyframes>

								<tickDuration>6</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

								<tickDuration>8</tickDuration>
								<positionX>-0.178146541</positionX>
								<positionZ>-0.01672452</positionZ>
								<rotation>96.95889</rotation>

								<tickDuration>1</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

						</keyframes>

				</animationClips>

				<stageName>Orgasm</stageName>
				<isLooping>false</isLooping>
				<stageIndex>0</stageIndex>
				<animationClips>

						<layer>LayingPawn</layer>
						<keyframes>

								<soundEffect>Slimy</soundEffect>
								<tickDuration>6</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<quiver>True</quiver>
								<soundEffect>Cum</soundEffect>
								<tickDuration>80</tickDuration>
								<bodyAngle>85.17255</bodyAngle>
								<headAngle>58.0615845</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03527552</bodyOffsetZ>
								<bodyOffsetX>0.0471138731</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<quiver>True</quiver>
								<soundEffect>Cum</soundEffect>
								<tickDuration>90</tickDuration>
								<bodyAngle>92.15109</bodyAngle>
								<headAngle>96.34238</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.0237147212</bodyOffsetZ>
								<bodyOffsetX>0.0432603136</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<quiver>True</quiver>
								<soundEffect>Cum</soundEffect>
								<tickDuration>70</tickDuration>
								<bodyAngle>92.15109</bodyAngle>
								<headAngle>96.34238</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.0237147212</bodyOffsetZ>
								<bodyOffsetX>0.0432603136</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>70</tickDuration>
								<bodyAngle>92.15109</bodyAngle>
								<headAngle>96.34238</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.0237147212</bodyOffsetZ>
								<bodyOffsetX>0.0432603136</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>1</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

						</keyframes>

						<keyframes>

								<tickDuration>6</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

								<tickDuration>80</tickDuration>
								<positionX>-0.178146541</positionX>
								<positionZ>-0.01672452</positionZ>
								<rotation>96.95889</rotation>

								<tickDuration>90</tickDuration>
								<positionX>-0.178146541</positionX>
								<positionZ>-0.01672452</positionZ>
								<rotation>96.95889</rotation>

								<tickDuration>70</tickDuration>
								<positionX>-0.178146541</positionX>
								<positionZ>-0.01672452</positionZ>
								<rotation>96.95889</rotation>

								<tickDuration>70</tickDuration>
								<positionX>-0.178146541</positionX>
								<positionZ>-0.01672452</positionZ>
								<rotation>96.95889</rotation>

								<tickDuration>1</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

						</keyframes>

				</animationClips>

		</animationStages>
	</Patch_SexToysMasturbation.SexToyAnimationDef>
</Defs>


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>{87763712-0536-4D5F-9EAA-520F15D4F84E}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Patch_SexToysMasturbation</RootNamespace>
    <AssemblyName>Patch_SexToysMasturbation</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>false</DebugSymbols>
    <DebugType>none</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>1.4\Assemblies\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="0Harmony">
      <HintPath>..\..\..\..\..\workshop\content\294100\1127530465\1.3\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="Rimworld-Animations, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\1.3\Assemblies\Rimworld-Animations.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="RJW">
      <HintPath>..\..\rjw\1.4\Assemblies\RJW.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="RJW-ToysAndMasturbation">
      <HintPath>..\..\rjw-toys-and-masturbation\Assemblies\RJW-ToysAndMasturbation.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="Microsoft.CSharp" />
    <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="Properties\AssemblyInfo.cs" />
    <Compile Include="Source\Defs\SexToyAnimationDef.cs" />
    <Compile Include="Source\Harmony\Harmony_PatchAll.cs" />
    <Compile Include="Source\Patches\HarmonyPatch_JobDriver_SexBaseInitiator.cs" />
    <Compile Include="Source\Utilities\AnimSexToyUtility.cs" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="1.3\Assemblies\" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="1.3\Defs\AnimationDefs\Animations_Dildo.xml" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>


code:
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Patch_SexToysMasturbation")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Patch_SexToysMasturbation")]
[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("87763712-0536-4d5f-9eaa-520f15d4f84e")]

// 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_Animations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Patch_SexToysMasturbation
{
    public class SexToyAnimationDef : AnimationDef
    {

        public String requiredBodyPart = null;

    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using HarmonyLib;
using System.Reflection;

namespace Patch_SexToysMasturbation
{

    [StaticConstructorOnStartup]
    public static class Harmony_PatchAll {

        static Harmony_PatchAll() {

            Harmony val = new Harmony("animtoyspatch");
            val.PatchAll(Assembly.GetExecutingAssembly());

        }
    }
}


code:
using HarmonyLib;
using Rimworld_Animations;
using rjw;
using RJW_ToysAndMasturbation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace Patch_SexToysMasturbation
{
    [HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "Start")]
    public class HarmonyPatch_JobDriver_SexBaseInitiator
    {

        public static void Postfix(ref JobDriver_SexBaseInitiator __instance)
        {

            if(__instance is JobDriver_MasturbateWithToy masturbateJobDriver)
            {

                Log.Message("Rerolling animations...");
                Pawn pawn = masturbateJobDriver.pawn;
                Thing sexToy = masturbateJobDriver.dildo;

                RerollAnimationsForSexToy(pawn, sexToy, masturbateJobDriver.Bed);
            }

        }

        public static void RerollAnimationsForSexToy(Pawn pawn, Thing thing, Thing bed)
        {
            CompSexToy sextoy = thing.TryGetComp<CompSexToy>();

            SexToyAnimationDef anim = AnimSexToyUtility.tryFindAnimation(sextoy, pawn);

            if (anim != null)
            {
                Log.Message("Playing anim " + anim.defName);

                if(bed != null)
                {
                    pawn.TryGetComp<CompBodyAnimator>().setAnchor(bed);
                    thing.TryGetComp<CompThingAnimator>().setAnchor(bed);
                }
                else
                {
                    pawn.TryGetComp<CompBodyAnimator>().setAnchor(pawn.Position);
                    thing.TryGetComp<CompThingAnimator>().setAnchor(pawn.Position);
                }

                bool mirror = GenTicks.TicksGame % 2 == 0;

                pawn.TryGetComp<CompBodyAnimator>().StartAnimation(anim, new List<Pawn> { pawn }, 0, mirror);
                thing.TryGetComp<CompThingAnimator>().StartAnimation(anim, pawn, mirror);

                (pawn.jobs.curDriver as JobDriver_Sex).ticks_left = anim.animationTimeTicks;
                (pawn.jobs.curDriver as JobDriver_Sex).sex_ticks = anim.animationTimeTicks;
                (pawn.jobs.curDriver as JobDriver_Sex).duration = anim.animationTimeTicks;
            }
            else
            {
                Log.Message("No animation found");
            }

        }

    }

}


code:
using rjw;
using RJW_ToysAndMasturbation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace Patch_SexToysMasturbation
{
    public class AnimSexToyUtility
    {

        public static SexToyAnimationDef tryFindAnimation(CompSexToy sexToy, Pawn pawn)
        {

            IEnumerable<SexToyAnimationDef> options = DefDatabase<SexToyAnimationDef>.AllDefs.Where((SexToyAnimationDef x) =>
            {

                if(!sexToy.Props.requiredBodyParts.Contains(x.requiredBodyPart))
                {
                    return false;
                }

                if(x.requiredBodyPart == "vagina" && !Genital_Helper.has_vagina(pawn))
                {
                    return false;
                }

                return true;

            });

            if(options != null && options.Any())
            {
                return options.RandomElement();
            }
            else
            {
                return null;
            }

        }

    }
}


code:
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Rimworld-Animations")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Rimworld-Animations")]
[assembly: AssemblyCopyright("Copyright ©  2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

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

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("71b05d71-67b2-4014-82cd-18c20ac0882f")]

// 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:
<?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>{71B05D71-67B2-4014-82CD-18C20AC0882F}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Rimworld_Animations</RootNamespace>
    <AssemblyName>Rimworld-Animations</AssemblyName>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
    <TargetFrameworkProfile />
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>false</DebugSymbols>
    <DebugType>none</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>1.4\Assemblies\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>none</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="0Harmony">
      <HintPath>..\..\..\..\workshop\content\294100\839005762\1.4\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.4\Assemblies\RJW.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="RJW-ToysAndMasturbation">
      <HintPath>..\rjw-toys-and-masturbation\Assemblies\RJW-ToysAndMasturbation.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="Microsoft.CSharp" />
    <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="1.4\Source\Actors\Actor.cs" />
    <Compile Include="1.4\Source\Actors\AlienRaceOffset.cs" />
    <Compile Include="1.4\Source\Actors\BodyTypeOffset.cs" />
    <Compile Include="1.4\Source\Animations\AnimationStage.cs" />
    <Compile Include="1.4\Source\Animations\Clips\BaseAnimationClip.cs" />
    <Compile Include="1.4\Source\Animations\Clips\PawnAnimationClip.cs" />
    <Compile Include="1.4\Source\Animations\Clips\ThingAnimationClip.cs" />
    <Compile Include="1.4\Source\Animations\Keyframes\Keyframe.cs" />
    <Compile Include="1.4\Source\Animations\Keyframes\PawnKeyframe.cs" />
    <Compile Include="1.4\Source\Animations\Keyframes\ThingKeyframe.cs" />
    <Compile Include="1.4\Source\Comps\CompBodyAnimator.cs" />
    <Compile Include="1.4\Source\Comps\CompProperties_BodyAnimator.cs" />
    <Compile Include="1.4\Source\Comps\CompProperties_ThingAnimator.cs" />
    <Compile Include="1.4\Source\Comps\CompThingAnimator.cs" />
    <Compile Include="1.4\Source\Defs\AnimationDef.cs" />
    <Compile Include="1.4\Source\Extensions\PawnWoundDrawerExtension.cs" />
    <Compile Include="1.4\Source\MainTabWindows\MainTabWindow_OffsetConfigure.cs" />
    <Compile Include="1.4\Source\MainTabWindows\OffsetMainButtonDefOf.cs" />
    <Compile Include="1.4\Source\MainTabWindows\WorldComponent_UpdateMainTab.cs" />
    <Compile Include="1.4\Source\Patches\Harmony_PatchAll.cs" />
    <Compile Include="1.4\Source\Patches\OtherModPatches\HarmonyPatch_AlienRace.cs" />
    <Compile Include="1.4\Source\Patches\OtherModPatches\HarmonyPatch_CSL.cs" />
    <Compile Include="1.4\Source\Patches\OtherModPatches\HarmonyPatch_DontShaveYourHead.cs" />
    <Compile Include="1.4\Source\Patches\OtherModPatches\HarmonyPatch_FacialAnimation.cs" />
    <Compile Include="1.4\Source\Patches\OtherModPatches\HarmonyPatch_HatsDisplaySelection.cs" />
    <Compile Include="1.4\Source\Patches\OtherModPatches\HarmonyPatch_ShowHairWithHats.cs" />
    <Compile Include="1.4\Source\Patches\RimworldPatches\HarmonyPatch_HeadHair.cs" />
    <Compile Include="1.4\Source\Patches\RimworldPatches\HarmonyPatch_PawnRenderer.cs" />
    <Compile Include="1.4\Source\Patches\RimworldPatches\HarmonyPatch_PawnRotation.cs" />
    <Compile Include="1.4\Source\Patches\RimworldPatches\HarmonyPatch_Pawn_DrawTracker.cs" />
    <Compile Include="1.4\Source\Patches\RimworldPatches\HarmonyPatch_SetPawnAnimatable.cs" />
    <Compile Include="1.4\Source\Patches\RimworldPatches\HarmonyPatch_Thing.cs" />
    <Compile Include="1.4\Source\Patches\RJWPatches\HarmonyPatch_PlaySexSounds.cs" />
    <Compile Include="1.4\Source\Patches\RJWPatches\HarmonyPatch_SexTick.cs" />
    <Compile Include="1.4\Source\Patches\RJWPatches\HarmonyPatch_WorkGiverSex.cs" />
    <Compile Include="1.4\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_JoinInBed.cs" />
    <Compile Include="1.4\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_SexBaseInitiator.cs" />
    <Compile Include="1.4\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_SexBaseReceiverLoved.cs" />
    <Compile Include="1.4\Source\Settings\AnimationSettings.cs" />
    <Compile Include="1.4\Source\Utilities\AnimationUtility.cs" />
    <Compile Include="1.4\Source\Utilities\PatchOperationAddOrReplace.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="1.1\Assemblies\Rimworld-Animations.dll" />
    <Content Include="1.4\Assemblies\Rimworld-Animations.dll" />
    <Content Include="1.4\Defs\AnimationDefs\Animations_Beast.xml" />
    <Content Include="1.4\Defs\AnimationDefs\Animations_Lesbian.xml" />
    <Content Include="1.4\Defs\AnimationDefs\Animations_Masturbate.xml" />
    <Content Include="1.4\Defs\AnimationDefs\Animations_Multi.xml" />
    <Content Include="1.4\Defs\AnimationDefs\Animations_SexToys.xml" />
    <Content Include="1.4\Defs\AnimationDefs\Animations_vanilla.xml" />
    <Content Include="1.4\Defs\AnimationDefs\Animations_Vanilla2.xml" />
    <Content Include="1.4\Defs\AnimationDefs\TemplateAnimation.xml" />
    <Content Include="1.4\Defs\MainTabDefs\MainButtonDef.xml" />
    <Content Include="1.4\Defs\SoundDefs\Sounds_Sex.xml" />
    <Content Include="1.4\Patches\AnimationPatchHSK.xml" />
    <Content Include="1.4\Patches\AnimationPatch_CompBodyAnimator.xml" />
    <Content Include="1.4\Patches\CompatibilityPatch_FacialAnimation.xml" />
    <Content Include="1.4\Patches\CompatibilityPatch_HCSK.xml" />
    <Content Include="1.4\Patches\CompPatches\AutoCleaner.xml" />
    <Content Include="1.4\Patches\CompPatches\CombatExtended.xml" />
    <Content Include="1.4\Patches\CompPatches\ZombieLand.xml" />
    <Content Include="1.4\Patches\RacePatches\Epona race Renaissance.xml" />
    <Content Include="1.4\Patches\RacePatches\Nyaron.xml" />
    <Content Include="1.4\Sounds\Sex\Clap_1.wav" />
    <Content Include="1.4\Sounds\Sex\Clap_2.wav" />
    <Content Include="1.4\Sounds\Sex\Clap_3.wav" />
    <Content Include="1.4\Sounds\Sex\Clap_4.wav" />
    <Content Include="1.4\Sounds\Sex\Clap_5.wav" />
    <Content Include="1.4\Sounds\Sex\Clap_6.wav" />
    <Content Include="1.4\Sounds\Sex\Clap_7.wav" />
    <Content Include="1.4\Sounds\Sex\Clap_8.wav" />
    <Content Include="1.4\Sounds\Sex\cum.wav" />
    <Content Include="1.4\Sounds\Sex\kucyu04.wav" />
    <Content Include="1.4\Sounds\Sex\Slime\Slimy1.wav" />
    <Content Include="1.4\Sounds\Sex\Slime\Slimy2.wav" />
    <Content Include="1.4\Sounds\Sex\Slime\Slimy3.wav" />
    <Content Include="1.4\Sounds\Sex\Slime\Slimy4.wav" />
    <Content Include="1.4\Sounds\Sex\Slime\Slimy5.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\Suck_1.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\Suck_10.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\suck_2.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\Suck_3.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\Suck_4.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\Suck_5.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\Suck_6.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\Suck_7.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\Suck_8.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\Suck_9.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\Swallow_1.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\Swallow_2.wav" />
    <Content Include="1.4\Sounds\Sex\Suck\Swallow_3.wav" />
    <Content Include="1.4\Textures\UI\MainTab.png" />
    <Content Include="About\About.xml" />
    <Content Include="About\Manifest.xml" />
    <Content Include="Languages\PortugueseBrazilian\DefInjected\MainButtonDef\MainButtonDef.xml" />
    <Content Include="Languages\PortugueseBrazilian\DefInjected\Rimworld_Animations.AnimationDef\Animations_Beast.xml" />
    <Content Include="Languages\PortugueseBrazilian\DefInjected\Rimworld_Animations.AnimationDef\Animations_Lesbian.xml" />
    <Content Include="Languages\PortugueseBrazilian\DefInjected\Rimworld_Animations.AnimationDef\Animations_Multi.xml" />
    <Content Include="Languages\PortugueseBrazilian\DefInjected\Rimworld_Animations.AnimationDef\Animations_vanilla.xml" />
    <Content Include="LoadFolders.xml" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="1.2\" />
  </ItemGroup>
  <ItemGroup>
    <None Include="1.4\Defs\AnimationDefs\AnimationDefs.rar" />
    <None Include="1.4\Patch_HatsDisplaySelection\Patch_HatsDisplaySelection.csproj" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>


code:
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29905.134
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rimworld-Animations", "Rimworld-Animations.csproj", "{71B05D71-67B2-4014-82CD-18C20AC0882F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Patch_HatsDisplaySelection", "Patch_HatsDisplaySelection\Patch_HatsDisplaySelection.csproj", "{BA766964-1716-422D-A09E-29426F8EB9D5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Patch_SexToysMasturbation", "Patch_SexToysMasturbation\Patch_SexToysMasturbation.csproj", "{87763712-0536-4D5F-9EAA-520F15D4F84E}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{71B05D71-67B2-4014-82CD-18C20AC0882F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{71B05D71-67B2-4014-82CD-18C20AC0882F}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{71B05D71-67B2-4014-82CD-18C20AC0882F}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{71B05D71-67B2-4014-82CD-18C20AC0882F}.Release|Any CPU.Build.0 = Release|Any CPU
		{BA766964-1716-422D-A09E-29426F8EB9D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{BA766964-1716-422D-A09E-29426F8EB9D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{BA766964-1716-422D-A09E-29426F8EB9D5}.Release|Any CPU.Build.0 = Release|Any CPU
		{87763712-0536-4D5F-9EAA-520F15D4F84E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{87763712-0536-4D5F-9EAA-520F15D4F84E}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{87763712-0536-4D5F-9EAA-520F15D4F84E}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{87763712-0536-4D5F-9EAA-520F15D4F84E}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {7E01292A-D76F-4CA3-AA64-085B84ECD905}
	EndGlobalSection
EndGlobal


code:
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
 - OS: [e.g. iOS]
 - Version [e.g. 22]
 - Commit: [e.g https://github.com/expanded-mod-team/RL-Expanded-Rewrite/commit/53366c33fe79e4e37d587729ba3f971a3d2ede46]

**Smartphone (please complete the following information):**
 - Device: [e.g. iPhone6]
 - OS: [e.g. iOS8.1]
 - Version [e.g. 22]
 - Commit: [e.g https://github.com/expanded-mod-team/RL-Expanded-Rewrite/commit/53366c33fe79e4e37d587729ba3f971a3d2ede46]

**Additional context**
Add any other context about the problem here.

repo_name: pervy/rogues


code:
---
name: Feature request (Developers)
about: Suggest an specific feature for this project
title: "[DEV FRQ]"
labels: enhancement
assignees: ''

---

<!--- Please ensure your request retains "[USR FRQ] Request:" in it's title.  --->

<!--- All developers suggesting a feature must include a diagram of how this system will integrate --->
![Diagram Alt-Text]()

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe what is needed**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


code:
---
name: Feature request Users
about: Suggest an idea for this project
title: "[USR FRQ] Request: Brief Descriptive Name"
labels: enhancement
assignees: ''

---

<!--- Please ensure your request retains "[USR FRQ] Request:" in it's title.  --->

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


code:
---
name: ToDo
about: A specific task that needs to be accomplished
title: "[TODO] Task: Brief Descriptive Title"
labels: ''
assignees: ''

---

<!--- Please ensure your request retains "[TODO] Task:" in it's title.  --->

<!--- All developers must include a diagram illustrating where they believe a problem occurs.--->
![Diagram Alt-Text](image.png)

**Is your task related to a problem? Please describe.**
A clear and concise description of what the problem is.

**Describe what is needed**
A clear and concise description of what needs to be added/removed/modified or fixed.

**Describe potential approaches you've considered**
A clear and concise description of any alternative solutions you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.


code:
# Gitignore file | DO NOT REMOVE OR EDIT

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

# C extensions
*.so

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

# 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/
.pytest_cache/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/

# Ren'py Projects
project.json
*.rpa
*.rpyc
*.jpeg
*.png
*.bak
/*.exe
/renpy/
/lib/
/game/cache/
/game/gui/
/game/images/
/game/saves/
/game/tl/

# Ren'py Log Files
traceback.txt
log.txt
errors.txt

# UnRen specific ignores
unren
*.bat

# VScode and Testing
.vscode/
*.code-workspace
/document-guide

# Project Specific
/game/_02testsuite/tests/
/game/logging.txt
/game/renpy_log.txt

path: .gitignore


code:
# INIT PRIORITY

Dependencies
    -1000

Common
    -900


code:
python early in trainer:

    attr_max = 1000
    attr_min = 0
    max_modifier = 5
    attributes = {}
    sex_acts = []

    class Attribute(renpy.store.object):
        """
        Represents a character attribute with customizable constraints, modifiers, and bonuses.

        Attributes are numerical values that define various characteristics of characters,
        such as strength, intelligence, or charisma. They can be constrained within specified
        minimum and maximum bounds, modified by factors, and have additional bonuses.

        Attributes:
            value (float): The current value of the attribute.
            minimum (float): The minimum allowed value of the attribute.
            maximum (float): The maximum allowed value of the attribute.
            modifier (float): The modifier factor applied to the attribute.
            bonus (float): Additional bonus added to the attribute value.

        Methods:
            __init__: Initializes an Attribute object.
            __float__: Returns the value of the attribute as a float.
            __repr__: Returns a string representation of the attribute.
            __str__: Returns a string representation of the attribute.
            __add__: Adds a value to the attribute.
            __sub__: Subtracts a value from the attribute.
            __mul__: Multiplies the attribute by a value.
            __truediv__: Divides the attribute by a value.
            __mod__: Computes the modulus of the attribute.
            __pow__: Raises the attribute to a power.
            __iadd__: Adds a value to the attribute in place.
            __isub__: Subtracts a value from the attribute in place.
            __imul__: Multiplies the attribute by a value in place.
            __itruediv__: Divides the attribute by a value in place.
            minimum: Getter and setter for the minimum attribute value.
            maximum: Getter and setter for the maximum attribute value.
            modifier: Getter for the modifier factor applied to the attribute.
            bonus: Getter and setter for the additional bonus added to the attribute value.

        Example:
            # Define an attribute for character strength.
            strength = Attribute(value=10, minimum=0, maximum=100, modifier=1)
        """

        def __init__(self, value=0, **properties):
            """
            Initializes an Attribute object.

            :param value: The initial value of the attribute (default is 0).
            :type value: float
            :param properties: Additional properties for the attribute.
            :type properties: dict
            """
            self._value = value
            self._minimum = properties.pop('min', attr_min)
            self._maximum = properties.pop('max', attr_max)
            self._modifier = min(properties.get('mod', 1), max_modifier)
            self._modifiers = properties.pop('mods', [])
            self.bonus = properties.pop('bonus', 0)

        def constrain(self, value):
            """
            Bind a value between the minimum and maximum.

            :param value: The value to be constrained.
            :type value: float
            :return: The constrained value.
            :rtype: float
            """
            return max(self._minimum, min(value, self._maximum))

        def dual_constrain(self):
            """
            Bind a value using both local and global constraints.

            :param value: The value to be constrained.
            :type value: float
            :return: The constrained value.
            :rtype: float
            """
            return self.constrain(min(self._value, attr_max), attr_min)

        def __float__(self):
            """
            Convert the attribute value to a float.

            :return: The attribute value as a float.
            :rtype: float
            """
            return float(self._dual_constrain(self._value + self.bonus))

        def __repr__(self):
            return "<Character Attribute>"

        # Define all arithmetic operations

        def __add__(self, other):
            return float(self) + other

        def __radd__(self, other):
            return self + other

        def __sub__(self, other):
            return float(self) - other

        def __rsub__(self, other):
            return other - float(self)

        def __mul__(self, other):
            return float(self) * other

        def __rmul__(self, other):
            return self * other

        def __truediv__(self, other):
            return float(self) / other

        def __rtruediv__(self, other):
            return other / float(self)

        def __floordiv__(self, other):
            return float(self) // other

        def __rfloordiv__(self, other):
            return other // float(self)

        def __mod__(self, other):
            return float(self) % other

        def __rmod__(self, other):
            return other % float(self)

        def __pow__(self, other):
            return float(self) ** other

        def __rpow__(self, other):
            return other ** float(self)

        # Define all in-place operations

        def __iadd__(self, other):
            self._value += self._modifier * other
            self._value = self._dual_constrain(self._value)
            return self

        def __isub__(self, other):
            self._value -= self._modifier * other
            self._value = self._dual_constrain(self._value)
            return self

        def __imul__(self, other):
            self._value *= self._modifier * other
            self._value = self._dual_constrain(self._value)
            return self

        def __itruediv__(self, other):
            self._value /= self._modifier * other
            self._value = self._dual_constrain(self._value)
            return self

        # Define comparison operations

        def __eq__(self, other):
            return float(self) == other

        def __ne__(self, other):
            return float(self) != other

        def __lt__(self, other):
            return float(self) < other

        def __le__(self, other):
            return float(self) <= other

        def __gt__(self, other):
            return float(self) > other

        def __ge__(self, other):
            return float(self) >= other

        # Define property getters and setters

        @property
        def minimum(self):
            return self._minimum

        @minimum.setter
        def minimum(self, value):
            self._minimum = value

        @property
        def maximum(self):
            return self._maximum

        @maximum.setter
        def maximum(self, value):
            self._maximum = value

        @property
        def modifier(self):
            return self._modifier

        @modifier.setter
        def modifier(self, value):
            self._modifier = min(value, max_modifier)

        # Define methods for managing modifiers

        def add_modifier(self, modifier):
            """
            Add a modifier to the attribute.

            :param modifier: The modifier to add.
            :type modifier: float
            """
            self._modifiers.append(modifier)

        def remove_modifier(self, modifier):
            """
            Remove a modifier from the attribute.

            :param modifier: The modifier to remove.
            :type modifier: float
            """
            if modifier in self._modifiers:
                self._modifiers.remove(modifier)

    trainer = {}
    traits={}

    class Trait(renpy.store.object):
        """
        Represents a character trait that can modify attributes, apply periodic effects, and be conditionally applied.

        Traits are a way to customize characters by adding specific behaviors, abilities, or characteristics to them.
        They can modify attributes, such as minimum, maximum, modifiers, and bonuses, when applied to a character.
        Additionally, traits can apply periodic effects over time and may have conditions under which they are applied.

        Attributes:
            name (str): The name of the trait.
            description (str): A description of the trait.
            attributes (list of dict): A list of attribute specifications to be modified by the trait.
            application_function (function): A function to be called when the trait is applied to a character.
            remove_function (function): A function to be called when the trait is removed from a character.
            tick_attributes (list of dict): Attribute specifications for periodic effects applied by the trait.
            tick_function (function): A function to be called periodically to apply effects on a character.
            applicable_condition (str): A string evaluating to a boolean condition for trait applicability.

        Methods:
            __init__: Initializes a Trait object.
            __str__: Gets the string representation of the trait.
            is_applicable: Checks if the trait is applicable based on a condition.
            apply: Applies the trait to a character, modifying attributes and applying effects.
            remove: Removes the trait from a character, reverting attribute modifications and effects.
            tick: Applies periodic effects of the trait on a character.

        Example:
            # Define a trait that increases strength and applies a periodic effect.
            strong_trait = Trait(
                name="Strong",
                description="Increases strength and provides a strength boost over time.",
                attributes=[{"attr": "strength", "modifier": 1}],
                tick_attributes=[{"attr": "strength", "modifier": 0.1}],
                applicable_condition="character.level >= 5"
            )
        """

        def __init__(self, name, description=None, attributes=None, **properties):
            """
            Initialize a Trait object.

            :param name: The name of the trait.
            :type name: str
            :param description: A description of the trait (optional).
            :type description: str
            :param attributes: A list of attribute specifications to be modified by the trait (optional).
            :type attributes: list of dict
            :param properties: Additional properties for the trait.
            :type properties: dict
            """
            self.name = name
            self.description = description
            self.attributes = attributes or []
            self.application_function = properties.pop('application_function', None)
            self.remove_function = properties.pop('remove_function', None)
            self.tick_attributes = properties.pop('tick_attributes', [])
            self.tick_function = properties.pop('tick_function', None)
            self.applicable_condition = properties.pop('applicable_condition', None)
            traits[self.name] = self

        def __repr__(self):
            return "<Trait: {!r}>".format(self._name)

        def is_applicable(self):
            """
            Check if the trait is applicable based on the applicable condition.

            :return: True if applicable, False otherwise.
            :rtype: bool
            """
            if self.applicable_condition is not None:
                return renpy.python.py_eval(self.applicable_condition)
            return True

        def apply(self, obj):
            """
            Apply the trait to a character.

            :param obj: The character object to apply the trait to.
            :type obj: TRNCharacter
            """
            if self.is_applicable():
                if self.application_function:
                    self.application_function(obj)
                for attribute_spec in self.attributes:
                    obj.apply_attribute_spec(attribute_spec)

        def remove(self, obj):
            """
            Remove the trait from a character.

            :param obj: The character object to remove the trait from.
            :type obj: TRNCharacter
            """
            if self.remove_function:
                self.remove_function(obj)
            for attribute_spec in self.attributes:
                obj.deapply_attribute_spec(attribute_spec)

        def tick(self, obj):
            """
            Apply periodic effects of the trait on a character.

            :param obj: The character object to apply the effects to.
            :type obj: TRNCharacter
            """
            if self.tick_function:
                self.tick_function(obj)
            for attribute_spec in self.tick_attributes:
                obj.apply_attribute_spec(attribute_spec)

    def execute_trait(parsed_trait):
        '''
            Stores trait in trainer.traits
        '''
        print(parsed_trait)
        name, description, attributes, properties = parsed_trait
        if name not in traits: # check if name in traits
            traits[name] = Trait(name, description, attributes, **properties)
        else:
            print(config.log)
            renpy.log("(%t) is already parsed!" % parsed_trait['name'])
            #config.log(f"{parsed_trait[0]} is already parsed!")
        return

    def parse_trait_attribute(l):
        '''
        Parses an Attribute block of a character trait.
        '''
        attr = {}
        attr['attr'] = l.require(l.name)
        l.require(":")
        l.expect_block("attribute")

        ll = l.subblock_lexer()
        ll.advance()

        while not ll.eob:
            if ll.keyword("bonus"):
                attr['bonus'] = ll.float()
                ll.advance()

            elif ll.keyword("max"):
                attr['max'] = ll.integer()
                ll.advance()

            elif ll.keyword("min"):
                attr['min'] = ll.integer()
                ll.advance()

            elif ll.keyword("modifier"):
                attr['mod'] = ll.float()
                ll.advance()

            elif ll.keyword("decay_modifier"):
                attr['decay_mod'] = ll.float()
                ll.advance()

            else:
                break

        return attr

    def parse_trait(l):
        '''
        Parses a Trainer Character Trait.
        '''
        attributes = []
        tick_attributes = []
        properties = {'applicable_condition' : None, 'application_function':None, 'deapplication_function':None,
                        'tick_function':None}

        # Requires format: `trait foo:`
        name = l.require(l.name)
        l.require(":")
        l.expect_block("trait")

        ll = l.subblock_lexer()
        ll.advance()

        description = ll.string()
        if description is not None :
            ll.expect_eol()
            ll.advance()

        while not ll.eob:
        # Checks all attribute subblocks.
            if ll.keyword('attribute'):
                attributes.append(parse_trait_attribute(ll))
                ll.advance()
            elif ll.keyword('tick_attribute'):
                tick_attributes.append(parse_trait_attribute(ll))
                ll.advance()
            elif ll.keyword('applicable_condition'):
                properties['applicable_condition'] = ll.require(ll.string)
                ll.advance()
            elif ll.keyword('application_function'):
                properties['application_function'] = ll.require(ll.string)
                ll.advance()
            elif ll.keyword('deapplication_function'):
                properties['deapplication_function'] = ll.require(ll.string)
                ll.advance()
            elif ll.keyword('tick_function'):
                properties['tick_function'] = ll.require(ll.string)
                ll.advance()

        if tick_attributes:
            properties['tick_attributes'] = tick_attributes

        return (name, description, attributes, properties)

    renpy.register_statement("trait", parse = parse_trait, execute = execute_trait, block = True)

    renpy.store.Attr = Attribute
    renpy.store.Trait = Trait


code:
""" 01character.rpy
Trainer Character
"""

init -900 python in trainer:

    pure_attributes = [
        'who_prefix',
        'who_suffix',
        'what_prefix',
        'what_suffix',
        'show_function',
        'predict_function',
        'condition',
        'dynamic',
        'image',
        'interact',
        'slow',
        'slow_abortable',
        'afm',
        'ctc',
        'ctc_pause',
        'ctc_timedpause',
        'ctc_position',
        'all_at_once',
        'with_none',
        'callback',
        'type',
        'advance',
        'who_style',
        'what_style',
        'window_style',
        'screen',
        'mode',
        'voice_tag',
        'kind',
        'attributes',
        'traits'
    ]

    @renpy.pure
    class TRNCharacter(renpy.store.ADVCharacter):
        """
        Represents a character in the game, inheriting from ADVCharacter.

        TRNCharacter is a subclass of ADVCharacter specifically designed for characters
        in the game. It extends the basic functionality of ADVCharacter with additional
        attributes, methods, and behaviors.

        Attributes:
            _actual_attributes (set): A set to store actual attribute names.
            _inventory (list): A list to store character's inventory.
            _sex_counter (dict): A dictionary to store sex acts counter.
            _available_sex_acts (list): A list of available sex acts for the character.
            _active (bool): Flag indicating whether the character is active.
            _real_name (str): The real name of the character.
            _last_location (Location): The last location the character was in.
            _location (Location): The current location of the character.
            _nickname (str): The nickname of the character.
            _party (list): A list of characters in the character's party.
            _relationships (list): A list of character relationships.
            _traits (list): A list of character traits.
            _flags (list): A list of flags associated with the character.
            _schedule (dict): A dictionary to store the character's actions as time moves forward

        Methods:
            __setattr__: Overrides setattr to apply changes to attributes.
            __init__: Initializes a TRNCharacter object.
            _apply_trait: Applies a trait to the character.
            _remove_trait: Removes a trait from the character.
            _update_name: Updates the character's displayed name.
            inventory: Property to get the character's inventory.
            sex_counter: Property to get the character's sex counter.
            available_sex_acts: Property to get the available sex acts.
            apply_attribute_modifications: Modifies the character's attributes based on a specification.
            revert_attribute_modifications: Reverts modifications made to the character's attributes.
            on_tick: Runs periodic actions for the character.
            move: Moves the character to a new location.
            add_flag: Adds a flag to the character.
            remove_flag: Removes a flag from the character.
            has_flag: Checks if a flag is present.
            add_item: Adds an item to the character's inventory.
            remove_item: Removes an item from the character's inventory.
            has_item: Checks if the character has an item.
            set_schedule: Changes the schedule to allow a in actions taken over time
            update: Updates the character by applying the schedule's actions
            get_character: Class method to get character data.

        """

        _actual_attributes = set()
        characters = {}

        def __init__(self, name=renpy.character.NotSet, kind=None, **properties):
            """
            Initializes a TRNCharacter object.

            :param name: The name of the character.
            :type name: str
            :param kind: The kind of character.
            :type kind: str
            :param properties: Additional properties for the character.
            :type properties: dict
            """
            self._initializing = True

            self._real_name = name
            self._nickname = properties.pop('nickname', self._real_name)

            self._flags = properties.pop('flags', [])
            self._inventory = properties.pop('inventory', [])
            self._traits = properties.pop('traits', [])

            self._home = properties.pop('home', None)
            self._location = None
            self._last_location = None

            self._active = False
            self._layer = None

            self._following = None
            self._party = []
            self._relationships = properties.pop('relationships', [])

            self._sex_counter = properties.pop('sex_counter', {})
            self._available_sex_acts = properties.pop('available_sex_acts', [])

            self._schedule = properties.pop("schedule", {}) # TODOC

            if not self._sex_counter:
                self._sex_counter = {sex_act: 0 for sex_act in sex_acts}

            if not self._available_sex_acts:
                self._available_sex_acts = sex_acts.copy()

            if kind is None:
                kind = renpy.store.trn

            if "dynamic" not in properties:
                properties["dynamic"] = False

            if 'attributes' in properties:
                for k, v in properties.get('attributes').items():
                    if k.lower() in vars(TRNCharacter()) and k not in attributes and k.capitalize() not in attributes:
                        raise AttributeError("Attribute names should not overwrite non-Attribute TRNCharacter fields")
                    a = Attribute(v)
                    setattr(self, k.lower(), a)
                    self._actual_attributes.add(k.lower())

            for k, v in attributes.items():
                if not hasattr(self, k.lower()):
                    a = Attribute(v)
                    setattr(self, k.lower(), a)
                    self._actual_attributes.add(k.lower())
            TRNCharacter.characters[name] = self
            if self._traits:
                for trait in self._traits:
                    self._apply_trait(trait)

            renpy.store.ADVCharacter.__init__(
                self,
                name,
                kind=None,
                **properties
            )

            delattr(self, "_initializing")

        def __setattr__(self, name, value):
            """Overrides setattr to apply changes to attributes."""
            if name in pure_attributes and not hasattr(self, "_initializing"):
                raise AttributeError(f"Cannot modify inherited attribute '{name}' after initialization")
            elif name in self._actual_attributes and hasattr(self, name):
                a = getattr(self, name)
                a.set_value(value)
            else:
                super().__setattr__(name, value)

        def __repr__(self):
            return "<TRNCharacter: {!r}>".format(self.name)

        @property
        def available_sex_acts(self):
            """Property to get the available sex acts."""
            return self._available_sex_acts

        @property
        def inventory(self):
            """Property to get the character's inventory."""
            return self._inventory

        @property
        def sex_counter(self):
            """Property to get the character's sex counter."""
            return self._sex_counter

        @property
        def schedule(self):
            """Property to get the character's schedule"""
            return self._schedule

        @property
        def location(self):
            """Property to get the character's location"""
            return self._location

        def add_flag(self, flag):
            """Adds a flag to the character."""
            self._flags.append(flag)

        def add_item(self, item):
            """Adds an item to the character's inventory."""
            self._inventory.append(item)

        def apply_attribute_modifications(self, modifications):
            """Modifies the character's attributes based on a specification."""
            for mod in modifications:
                attr_name = mod['attribute']
                if not hasattr(self, attr_name):
                    raise AttributeError(attr_name)

                a = getattr(self, attr_name)

                if 'min' in mod :
                    a.minimum += mod['min']
                if 'max' in mod:
                    a.maximum += mod['max']
                if 'bonus' in mod:
                    a.bonus += mod['bonus']
                if 'modifier' in mod:
                    a.add_modifier(mod['modifier'])
            return

        def _apply_trait(self, trait):
            """Applies a trait to the character."""
            Trait.apply(trait, self)
            return

        def has_flag(self, flag):
            """Checks if a flag is present."""
            return flag in self._flags

        def has_item(self, item):
            """Checks if the character has an item."""
            return item in self._inventory

        def move(self, location):
            """Moves the character to a new location."""
            self._last_location = self._location
            self._location = location
            if (self._last_location is not None):
                self._last_location.remove_person(self) # BUG: Further down the road, will characters always start in a location? If not FIXME
            self._location.add_person(self)
            return

        def remove_flag(self, flag):
            """Removes a flag from the character."""
            self._flags.remove(flag)

        def remove_item(self, item):
            """Removes an item from the character's inventory."""
            self._inventory.remove(item)

        def _remove_trait(self, trait):
            """Removes a trait from the character."""
            Trait.remove(trait, self)
            return

        def _update_name(self):
            """Updates the character's displayed name."""
            return self._nickname if self._nickname != self._real_name else self._real_name

        def revert_attribute_modifications(self, modifications):
            """Reverts modifications made to the character's attributes."""
            for mod in modifications:
                attr_name = mod['attribute']
                if not hasattr(self, attr_name):
                    raise AttributeError(attr_name)

                a = getattr(self, attr_name)

                if 'min' in mod :
                    a.minimum -= mod['min']
                if 'max' in mod:
                    a.maximum -= mod['max']
                if 'bonus' in mod:
                    a.bonus -= mod['bonus']
                if 'modifier' in mod:
                    a.remove_modifier(mod['modifier'])
            return

        def on_tick(self):
            """Runs periodic actions for the character."""
            for trait in self._traits:
                Trait.tick(trait, self)

        def set_schedule(self, schedule):
            """Edits the schedule of the character"""
            self._schedule = schedule

        def update(self):
            """
            RVD: Currently updates the character's location based on the their schedule
            """
            self._schedule.update()

        @classmethod
        def get_character(cls, name):
            """Class method to get character data."""
            try:
                return cls.characters[name]
            except KeyError:
                print(f"The character '{name}' does not exist.")

init -899 python:
    # Default Trainer Character based on ADVCharacter
    trn = trainer.TRNCharacter(
            who_prefix='',
            who_suffix='',
            what_prefix='',
            what_suffix='',

            show_function=renpy.show_display_say,
            predict_function=renpy.predict_show_display_say,

            condition=None,
            dynamic=False,
            image=None,

            interact=True,
            slow=True,
            slow_abortable=True,
            afm=True,
            ctc=None,
            ctc_pause=None,
            ctc_timedpause=None,
            ctc_position="nestled",
            all_at_once=False,
            with_none=None,
            callback=None,
            type='say',
            advance=True,

            who_style='say_label',
            what_style='say_dialogue',
            window_style='say_window',
            screen='say', # say screen that we can change
            mode='say',
            voice_tag=None,

            kind=renpy.store.adv,
            attributes={},
            traits=[])

    renpy.store.TRNCharacter = trainer.TRNCharacter
    renpy.store.trn = trn


code:
"""
    01dynamicentry.rpy
    Adds a dynamic menu system to Ren'Py.
"""

python early in dynamicentry:

    menu_list = [ ]

    class DynamicEntry(renpy.store.object):
        """
        DynamicEntry is a node for the dynamic menu system.
        When spawned, the DynamicEntry is added to its designated parent and tag lists.
        """

        def __init__(self, name=None, parents=None, caption=None, condition=None, actions=None, kind='entry', **properties):
            # Private variables
            self._actions = actions or []
            self._caption = caption or name
            self._condition = condition
            self._kind = kind
            self._name = name

            # Additional properties
            self._children = properties.get("children", [])
            self._hidden = properties.get('hidden', False)
            self._label = properties.get("label", None)
            self._locked = properties.get('locked', False)
            self._menu_list = properties.get("menu_list", menu_list)
            self._text = properties.get("text", name)

            # Assign parents
            if parents:
                for parent in parents:
                    if isinstance(parent, DynamicEntry):
                        parent.add_child(self)
                    else:
                        self._menu_list[parent].add_child(self)

            # Append to menu list
            self._menu_list.append(self)

        # Properties
        @property
        def actions(self):
            return self._actions

        @property
        def caption(self):
            return self._caption

        @property
        def condition(self):
            return self._condition

        @property
        def hidden(self):
            return self._hidden

        @property
        def kind(self):
            return self._kind

        @property
        def label(self):
            return self._label

        @property
        def locked(self):
            return self._locked

        @property
        def name(self):
            return self._name

        @property
        def text(self):
            return self._text

        # Public Methods
        def add_child(self, de):
            """
            Adds a child entry to the DynamicEntry.

            Args:
                de (DynamicEntry): The child entry to add.
            """
            if de not in self._children:
                self._children.append(de)

        def add_entry(self, name=None, parents=None, caption=None, condition=None, actions=None, kind='entry', **properties):
            """
            Adds a new entry to the DynamicEntry.

            Args:
                name (str): The name of the entry.
                parents (list): List of parent entries.
                caption (str): The caption of the entry.
                condition (callable): A function to determine if the entry should be visible.
                actions (list): List of actions associated with the entry.
                kind (str): The type of entry.
                **properties: Additional properties for the entry.
            """
            DynamicEntry(name, parents=[self] if parents is None else parents, caption=caption, condition=condition, actions=actions, kind=kind, **properties)

    renpy.store.menu_list = menu_list
    renpy.store.DynamicEntry = DynamicEntry

init 1 python in trainer:
    '''Removes any duplicated items a creator may have missed.'''
    renpy.store.trainer.menu_set = list(set(renpy.store.dynamicentry.menu_list))

label game_menu(dynamic_entry=None, style='trainer', entry_list=trainer.menu_set):
    # Displays the screen game_menu unless supplied a DynamicMenu.

    $ de = trainer.fetch_entry(dynamic_entry, entry_list)

    if (de is not None and isinstance(de, DynamicEntry)):
        call expression "dynamic_menu" pass (de)

    call screen game_menu(de)
    call expression "game_menu" pass (de)

label dynamic_menu(dynamic_entry=None, entry_list=trainer.menu_set):
    #   Calls proper screen based on entry.

    #   Args:
    #       dynamic_entry: An object of DynamicEntry or list containing DynamicEntry objects.
    #       style: An override for the style of the screen called by this function.

    $ de = trainer.fetch_entry(dynamic_entry, entry_list)

    if (de is None or not isinstance(de, DynamicEntry)):
        call expression "game_menu" pass (de)

    if de.kind is "menu":
        call screen dynamic_choice(de, entry_list)
    elif de.kind is "interaction":
        call screen dynamic_interaction(de, entry_list)
    elif de.kind is "location":
        call screen dynamic_location(de, entry_list)
    elif de.kind is "sex":
        call screen dynamic_sex(de, entry_list)
    else:
        call screen dynamic_choice(de, entry_list)

    jump game_menu