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

namespace Rimworld_Animations
{
    public class TexPathVariantsDef : Def
    {

        public List<string> variants;

    }
}


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

namespace Rimworld_Animations
{
    public class PawnRenderSubWorker_ChangeOffset : PawnRenderSubWorker
    {

        public override void TransformOffset(PawnRenderNode node, PawnDrawParms parms, ref Vector3 offset, ref Vector3 pivot)
        {
            if (parms.Portrait)
                return;

            if (node.AnimationWorker is AnimationWorker_KeyframesExtended
                && node.tree.pawn.TryGetComp<CompExtendedAnimator>(out CompExtendedAnimator extendedAnimator))
            {
                Vector3? pawnOffset = extendedAnimator.Offset;
                if (pawnOffset != null)
                {
                    offset += (Vector3)pawnOffset;
                }

            }
        }

        public override void TransformRotation(PawnRenderNode node, PawnDrawParms parms, ref Quaternion rotation)
        {
            if (node.AnimationWorker is AnimationWorker_KeyframesExtended
                && node.tree.pawn.TryGetComp<CompExtendedAnimator>(out CompExtendedAnimator extendedAnimator))
            {
                int? pawnRotation = extendedAnimator.Rotation;
                if (pawnRotation != null)
                {
                    Quaternion additionalRotation = Quaternion.AngleAxis((int)pawnRotation, Vector3.up);
                    rotation *= additionalRotation;
                }

            }

            base.TransformRotation(node, parms, ref rotation);
        }

    }
}

repo_name: Perseusi/rimworld

license: unknown


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

namespace Rimworld_Animations
{
    public class PawnRenderSubWorker_HideWhenAnimating : PawnRenderSubWorker
    {

        public override void EditMaterial(PawnRenderNode node, PawnDrawParms parms, ref Material material)
        {
            if (node.tree.pawn.def != ThingDefOf.Human) return;

            if (node.tree.rootNode.AnimationWorker is AnimationWorker_KeyframesExtended
                || node.tree.rootNode.children.Any(x => x.AnimationWorker is AnimationWorker_KeyframesExtended))
            {
                material.color = Color.clear;
                material.shader = ShaderTypeDefOf.Transparent.Shader;
            }
        }

        public override void TransformLayer(PawnRenderNode node, PawnDrawParms parms, ref float layer)
        {

            base.TransformLayer(node, parms, ref layer);

            if (node.tree.pawn.def != ThingDefOf.Human) return;
            layer -= 1000;
        }

    }

}


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 {

        public static void StartAnimation(List<Pawn> participants)
        {
            participants[0].Drawer.renderer.SetAnimation(AnimationDefOf.TestAnimation1);
            participants[1].Drawer.renderer.SetAnimation(AnimationDefOf.TestAnimation2);
        }

        //startgroupanimator with anchor
        //don't anchor to self if anchor is self
        public static void StartGroupAnimation(List<Pawn> participants, GroupAnimationDef groupAnimationDef, int reorder, Thing anchor)
        {

            int seed = GenTicks.TicksGame;

            for (int i = 0; i < participants.Count; i++)
            {

                groupAnimationDef.GetOffset(i, participants[i], out Vector3? position, out int? rotation, reorder);

                if (anchor is Pawn pawn && pawn == participants[i])
                {

                    List<AnimationDef> allAnimationsForPawn = groupAnimationDef.GetAllAnimationsForActor(i, seed, reorder);
                    participants[i].TryGetComp<CompExtendedAnimator>().PlayGroupAnimation(allAnimationsForPawn, position, rotation);
                }
                else
                {
                    //each participant gets their own unique extendedanimatoranchor, important for scribe_deep saving
                    List<AnimationDef> allAnimationsForPawn = groupAnimationDef.GetAllAnimationsForActor(i, seed, reorder);
                    BaseExtendedAnimatorAnchor animatorAnchor = new ExtendedAnimatorAnchor_Thing(anchor);

                    if (RJWAnimationSettings.debugMode)
                    {
                        Log.Message("Now playing animation: " + groupAnimationDef.defName + " Actor Shift: " + reorder);
                    }

                    participants[i].TryGetComp<CompExtendedAnimator>().PlayGroupAnimation(allAnimationsForPawn, position, rotation, animatorAnchor);
                }
            }
        }

        //startgroupanimation without anchor; just play where standing
        public static void StartGroupAnimation(List<Pawn> participants, GroupAnimationDef groupAnimationDef, int reorder)
        {
            int seed = GenTicks.TicksGame;

            for (int i = 0; i < participants.Count; i++)
            {
                List<AnimationDef> allAnimationsForPawn = groupAnimationDef.GetAllAnimationsForActor(i, seed, reorder);
                groupAnimationDef.GetOffset(i, participants[i], out Vector3? position, out int? rotation, reorder);
                participants[i].TryGetComp<CompExtendedAnimator>().PlayGroupAnimation(allAnimationsForPawn, position, rotation);
            }
        }

        public static void StopGroupAnimation(List<Pawn> participants)
        {
            foreach(Pawn pawn in participants)
            {
                pawn.TryGetComp<CompExtendedAnimator>()?.StopAnimating();
            }
        }

        public static void StopGroupAnimation(Pawn participant)
        {
            participant.TryGetComp<CompExtendedAnimator>()?.StopAnimating();
        }

        public static GroupAnimationDef FindGroupAnimation(List<Pawn> participants, out int reorder)
        {
            // go through each context in each GroupAnimationDef
            // if you find one where it returns canAnimationBeUsed (and reorders),
            // return that animation

            //find all, reorder randomly, then find max priority context
            DefDatabase<GroupAnimationDef>.AllDefsListForReading
                .FindAll((GroupAnimationDef x) => x.canAnimationBeUsed(participants))
                .OrderBy(_ => Rand.Int)
                .TryMaxBy((GroupAnimationDef x) => x.Priority(participants), out GroupAnimationDef result);

            reorder = result != null ? result.Reorder(participants) : 0;
            return result;

        }

        public static int GetAnimationLength(Pawn pawn)
        {
            return pawn.TryGetComp<CompExtendedAnimator>().AnimationLength;
        }

    }
}


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

namespace Rimworld_Animations
{
    public class VoiceDef : Def
    {

        public List<ThingDef> races = new List<ThingDef>();
        public Gender gender = Gender.None;
        public List<TraitDef> traits = new List<TraitDef>();
        public bool takesPriority = false;
        public float randomChanceFactor = 1;

        public Dictionary<VoiceTagDef, SoundDef> sounds = new Dictionary<VoiceTagDef, SoundDef>();

        public bool VoiceFitsPawn(Pawn pawn)
        {

            //doesn't match any of the races
            if (!races.Exists(x => x == pawn.def)) return false;

            //doesn't match gender
            if (gender != Gender.None && pawn.gender != gender) return false;

            //if traits list is not empty, and pawn doesn't have any of the designated traits, doesn't match
            if (!traits.Empty() && !traits.Any(trait => pawn.story.traits.HasTrait(trait))) return false;

            return true;

        }

    }
}


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

namespace Rimworld_Animations
{
    [DefOf]
    public static class VoiceDefOf
    {
        static VoiceDefOf()
        {
            DefOfHelper.EnsureInitializedInCtor(typeof(VoiceDefOf));
        }

        public static VoiceDef Voice_HumanMale;
        public static VoiceDef Voice_HumanFemale;
    }
}


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

namespace Rimworld_Animations
{
    public class VoiceTagDef : Def
    {

        public float probability = 1;

    }
}


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

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

1.5

	</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>

	</modDependencies>
	<loadAfter>

UnlimitedHugs.HugsLib

brrainz.harmony

erdelf.humanoidalienraces

nals.facialanimation

com.yayo.yayoAni

	</loadAfter>
	<loadBefore>
	</loadBefore>
	<incompatibleWith>
	</incompatibleWith>
	<description>
    Rimworld Animations 2.0! 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>2.0.0</version>
</Manifest>


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

	<!-- Mod menu -->
	<RimAnim_ModSettings>RJW Animation Settings</RimAnim_ModSettings>
	<RimAnim_SoundOverride>启用声音覆盖</RimAnim_SoundOverride>
	<RimAnim_GenitalRotation>控制生殖器旋转</RimAnim_GenitalRotation>
	<RimAnim_OrgasmQuiver>启用高潮颤抖</RimAnim_OrgasmQuiver>
	<RimAnim_RapeShiver>启用强奸颤动</RimAnim_RapeShiver>
	<RimAnim_HeartsDuringLovin>性爱时启用心形</RimAnim_HeartsDuringLovin>
	<RimAnim_PlayNonsexual>播放非性行为的动画 (牵手、亲热)</RimAnim_PlayNonsexual>
	<RimAnim_AnimManagerTab>启用动画管理器选项卡</RimAnim_AnimManagerTab>
	<RimAnim_ShiverIntensity>颤抖/颤动强度 (默认为 2): </RimAnim_ShiverIntensity>
	<RimAnim_Voices>在动画期间播放语音</RimAnim_Voices>
	<RimAnim_HumanlikeVoicesDefault>如果没有找到外星种族的声音，则默认播放人类的声音</RimAnim_HumanlikeVoicesDefault>
	<RimAnim_DebugMode>调试模式</RimAnim_DebugMode>
	<RimAnim_FloatRangeRenderTree>调整渲染树偏移表单的浮动范围:</RimAnim_FloatRangeRenderTree>

	<!-- Main Tab Window -->
	<RimAnims_AnimManager>Animation Manager</RimAnims_AnimManager>
	<RimAnims_Warning>警告--一般情况下，你不想更改人类偏移值，只想更改外星种族偏移值或动物偏移值</RimAnims_Warning>
	<RimAnims_CopyToClipboard>将偏移值复制到剪贴板</RimAnims_CopyToClipboard>
	<RimAnims_ShareSettings>将偏移值粘贴到 OffsetDef 中，或在 Discord 中共享</RimAnims_ShareSettings>

</LanguageData>


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

	<!-- Mod menu -->
	<RimAnim_ModSettings>RJW Animation Settings</RimAnim_ModSettings>
	<RimAnim_SoundOverride>啟用聲音覆蓋</RimAnim_SoundOverride>
	<RimAnim_GenitalRotation>控制生殖器旋轉</RimAnim_GenitalRotation>
	<RimAnim_OrgasmQuiver>啟用高潮顫抖</RimAnim_OrgasmQuiver>
	<RimAnim_RapeShiver>啟用強姦顫動</RimAnim_RapeShiver>
	<RimAnim_HeartsDuringLovin>性愛時啟用心形</RimAnim_HeartsDuringLovin>
	<RimAnim_PlayNonsexual>播放非性行為的動畫 (牽手、親熱)</RimAnim_PlayNonsexual>
	<RimAnim_AnimManagerTab>啟用動畫管理器選項卡</RimAnim_AnimManagerTab>
	<RimAnim_ShiverIntensity>顫抖/顫動強度 (預設為 2): </RimAnim_ShiverIntensity>
	<RimAnim_Voices>在動畫期間播放語音</RimAnim_Voices>
	<RimAnim_HumanlikeVoicesDefault>如果沒有找到外星種族的聲音，則默認播放人類的聲音</RimAnim_HumanlikeVoicesDefault>
	<RimAnim_DebugMode>除錯模式</RimAnim_DebugMode>
	<RimAnim_FloatRangeRenderTree>調整渲染樹偏移表單的浮動範圍:</RimAnim_FloatRangeRenderTree>

	<!-- Main Tab Window -->
	<RimAnims_AnimManager>Animation Manager</RimAnims_AnimManager>
	<RimAnims_Warning>警告--一般情況下，你不想更改人類偏移值，只想更改外星種族偏移值或動物偏移值</RimAnims_Warning>
	<RimAnims_CopyToClipboard>將偏移值複製到剪貼板</RimAnims_CopyToClipboard>
	<RimAnims_ShareSettings>將偏移值黏貼到 OffsetDef 中，或在 Discord 中共享</RimAnims_ShareSettings>

</LanguageData>


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

	<!-- Mod menu -->
	<RimAnim_ModSettings>RJW Animation Settings</RimAnim_ModSettings>
	<RimAnim_SoundOverride>Enable Sound Override</RimAnim_SoundOverride>
	<RimAnim_GenitalRotation>Control Genital Rotation</RimAnim_GenitalRotation>
	<RimAnim_OrgasmQuiver>Enable Orgasm Quiver</RimAnim_OrgasmQuiver>
	<RimAnim_RapeShiver>Enable Rape Shiver</RimAnim_RapeShiver>
	<RimAnim_HeartsDuringLovin>Enable hearts during lovin'</RimAnim_HeartsDuringLovin>
	<RimAnim_PlayNonsexual>Play animation for nonsexual acts (handholding, makeout)</RimAnim_PlayNonsexual>
	<RimAnim_AnimManagerTab>Enable Animation Manager Tab</RimAnim_AnimManagerTab>
	<RimAnim_ShiverIntensity>Shiver/Quiver Intensity (default 2): </RimAnim_ShiverIntensity>
	<RimAnim_Voices>Play voices during animations</RimAnim_Voices>
	<RimAnim_HumanlikeVoicesDefault>Play human voices by default, when none found for alien race</RimAnim_HumanlikeVoicesDefault>
	<RimAnim_DebugMode>Debug Mode</RimAnim_DebugMode>
	<RimAnim_FloatRangeRenderTree>Float range for Debug Render Tree offset menu: </RimAnim_FloatRangeRenderTree>

	<!-- Main Tab Window -->
	<RimAnims_AnimManager>Animation Manager</RimAnims_AnimManager>
	<RimAnims_Warning>Warning--You generally don't want to change human offsets, only alien offsets or animals</RimAnims_Warning>
	<RimAnims_CopyToClipboard>Copy Offset to Clipboard</RimAnims_CopyToClipboard>
	<RimAnims_ShareSettings>Paste offset values in OffsetDef, or share in Discord</RimAnims_ShareSettings>

</LanguageData>


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_HumanoidAlienRaces/1.4

Patch_HatsDisplaySelection/1.2

Patch_SexToysMasturbation

Patch_SexToysMasturbation/1.4

	</v1.4>
	<v1.5>

/

1.5

Patch_HumanoidAlienRaces/1.5

Patch_SexToysMasturbation/1.5

Patch_FacialAnimation/1.5

Patch_NoRimNudeWorld/1.5

Patch_NoRimNudeWorldZoo/1.5

	</v1.5>

</loadFolders>


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>{11DC70AF-FF23-4D4D-A4E5-6453664B1A12}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Milkable_Colonists_Animations</RootNamespace>
    <AssemblyName>Milkable-Colonists-Animations</AssemblyName>
    <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\Debug\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>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\839005762\1.5\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="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>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Source\Patches\Harmony_PatchAll.cs" />
  </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("Milkable-Colonists-Animations")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Milkable-Colonists-Animations")]
[assembly: AssemblyCopyright("Copyright ©  2024")]
[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("11dc70af-ff23-4d4d-a4e5-6453664b1a12")]

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

namespace MC_Animations {

    [StaticConstructorOnStartup]
    public static class Harmony_PatchAll {

        static Harmony_PatchAll() {

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

        }
    }
}


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

	<Operation Class="PatchOperationSequence">
		<success>Always</success>
		<operations>

				<xpath>/Defs/PawnRenderTreeDef[defName="Humanlike"]/root/children/li[debugLabel="Head"]/subworkerClasses</xpath>
				<success>Always</success>
				<nomatch Class="PatchOperationAdd">
					<xpath>/Defs/PawnRenderTreeDef[defName="Humanlike"]/root/children/li[debugLabel="Head"]</xpath>
					<value>
						<subworkerClasses />
					</value>
				</nomatch>

				<xpath>/Defs/PawnRenderTreeDef[defName="Humanlike"]/root/children/li[debugLabel="Head"]/subworkerClasses</xpath>
				<value>

Rimworld_Animations.PawnRenderSubWorker_HideWhenAnimating

				</value>

		</operations>
	</Operation>

</Patch>


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

namespace Rimworld_Animations {

	[StaticConstructorOnStartup]
	public static class HarmonyPatch_AlienRace
	{
		static HarmonyPatch_AlienRace()
		{
			(new Harmony("rjwanim")).Patch(AccessTools.Method(AccessTools.TypeByName("AlienRace.HarmonyPatches"), "DrawAddons"),
							prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_AlienRace), "Prefix_AnimateHeadAddons")));
		}

		/* todo: replace jank prefix with this
		public static void Prefix_DrawAddonsFinalHook(ref Pawn pawn, ref AlienPartGenerator.BodyAddon addon, ref Rot4 rot, ref Graphic graphic, ref Vector3 offsetVector, ref float angle, ref Material mat)
        {
			CompBodyAnimator animator = pawn.TryGetComp<CompBodyAnimator>();

			if (animator == null || !animator.isAnimating)
            {
				return;
            }

			if(addon.alignWithHead || addon.drawnInBed)
            {
				rot = animator.headFacing;
				angle = animator.headAngle;
				offsetVector += animator.deltaPos + animator.bodyAngle * animator.headBob;

			}
			else
            {
				rot = animator.bodyFacing;
				angle = animator.bodyAngle;
				offsetVector += animator.deltaPos;
            }

        }
		*/
		public static bool Prefix_AnimateHeadAddons(PawnRenderFlags renderFlags, Vector3 vector, Vector3 headOffset, Pawn pawn, Quaternion quat, Rot4 rotation)
		{

			if (renderFlags.FlagSet(PawnRenderFlags.Portrait) || !CompBodyAnimator.IsAnimating(pawn)) return true;
			if (!(pawn.def is ThingDef_AlienRace alienProps) || renderFlags.FlagSet(PawnRenderFlags.Invisible)) return false;

			List<AlienPartGenerator.BodyAddon> addons = alienProps.alienRace.generalSettings.alienPartGenerator.bodyAddons;
			AlienPartGenerator.AlienComp comp = pawn.GetComp<AlienPartGenerator.AlienComp>();
			CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();

			bool flag = renderFlags.FlagSet(PawnRenderFlags.Portrait);
			bool flag2 = renderFlags.FlagSet(PawnRenderFlags.Invisible);

			for (int i = 0; i < addons.Count; i++)
			{
				AlienPartGenerator.BodyAddon ba = addons[index: i];

				if (!ba.CanDrawAddon(pawn: pawn)) continue;

				bool forceDrawForBody = false;
				if (alienProps.defName.Contains("Orassan") && ba.path.ToLower().Contains("tail"))
                {
					forceDrawForBody = true;
                }
				AlienPartGenerator.RotationOffset offset = ba.defaultOffsets.GetOffset((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing);
				Vector3 a = (offset != null) ? offset.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, pawn.story.headType) : Vector3.zero;
				AlienPartGenerator.RotationOffset offset2 = ba.offsets.GetOffset((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing);
				Vector3 vector2 = a + ((offset2 != null) ? offset2.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, pawn.story.headType) : Vector3.zero);
				vector2.y = (ba.inFrontOfBody ? (0.3f + vector2.y) : (-0.3f - vector2.y));
				float num = ba.angle;
				if (((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing) == Rot4.North)
				{
					if (ba.layerInvert)
					{
						vector2.y = 0f - vector2.y;
					}
					num = 0f;
				}
				if (((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing) == Rot4.East)
				{
					num = 0f - num;
					vector2.x = 0f - vector2.x;
				}
				Graphic addonGraphic = comp.addonGraphics[i];

				addonGraphic.drawSize = ((flag && ba.drawSizePortrait != Vector2.zero) ? ba.drawSizePortrait : ba.drawSize) * (ba.scaleWithPawnDrawsize ? (ba.alignWithHead ? ((flag ? comp.customPortraitHeadDrawSize : comp.customHeadDrawSize) * (ModsConfig.BiotechActive ? (pawn.ageTracker.CurLifeStage.headSizeFactor ?? 1.5f) : 1.5f)) : ((flag ? comp.customPortraitDrawSize : comp.customDrawSize) * (ModsConfig.BiotechActive ? pawn.ageTracker.CurLifeStage.bodySizeFactor : 1f) * 1.5f)) : (Vector2.one * 1.5f));

				if ((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead)
				{

					Quaternion addonRotation = Quaternion.AngleAxis(pawnAnimator.headAngle < 0 ? 360 - (360 % pawnAnimator.headAngle) : pawnAnimator.headAngle, Vector3.up);

					GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: pawnAnimator.headFacing), loc: vector + (ba.alignWithHead ? headOffset : headOffset - addonRotation * pawn.Drawer.renderer.BaseHeadOffsetAt(pawnAnimator.headFacing)) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f),
					quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * addonRotation, mat: addonGraphic.MatAt(rot: pawnAnimator.headFacing), renderFlags.FlagSet(PawnRenderFlags.DrawNow));

				}

				else
				{
					Quaternion addonRotation;
					if (AnimationSettings.controlGenitalRotation && ba.path.ToLower().Contains("penis"))
                    {
						addonRotation = Quaternion.AngleAxis(pawnAnimator.genitalAngle, Vector3.up);
					}
					else
                    {
						addonRotation = Quaternion.AngleAxis(pawnAnimator.bodyAngle, Vector3.up);
					}

					if (AnimationSettings.controlGenitalRotation && pawnAnimator.controlGenitalAngle && ba?.hediffGraphics != null && ba.hediffGraphics.Count != 0 && ba.hediffGraphics[0]?.path != null && (ba.hediffGraphics[0].path.Contains("Penis") || ba.hediffGraphics[0].path.Contains("penis")))
					{
						GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: rotation), loc: vector + (ba.alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f),
						quat: Quaternion.AngleAxis(angle: pawnAnimator.genitalAngle, axis: Vector3.up), mat: addonGraphic.MatAt(rot: rotation), renderFlags.FlagSet(PawnRenderFlags.DrawNow));
					}

					else
                    {
						GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: rotation), loc: vector + (ba.alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f),
						quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * addonRotation, mat: addonGraphic.MatAt(rot: rotation), renderFlags.FlagSet(PawnRenderFlags.DrawNow));
					}

				}

			}

			return false;
		}
	}

	/*

    [HarmonyPatch(typeof(AlienRace.HarmonyPatches), "DrawAddons")]
    public static class HarmonyPatch_AlienRace {

		public static void RenderHeadAddonInAnimation(Mesh mesh, Vector3 loc, Quaternion quat, Material mat, bool drawNow, Graphic graphic, AlienPartGenerator.BodyAddon bodyAddon, Vector3 v, Vector3 headOffset, Pawn pawn, PawnRenderFlags renderFlags, Vector3 vector, Rot4 rotation)
        {

			CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();
			AlienPartGenerator.AlienComp comp = pawn.GetComp<AlienPartGenerator.AlienComp>();

			if (pawnAnimator != null && pawnAnimator.isAnimating)
            {

				if((bodyAddon.drawnInBed || bodyAddon.alignWithHead))
                {

					AlienPartGenerator.RotationOffset offset = bodyAddon.defaultOffsets.GetOffset(rotation);
					Vector3 a = (offset != null) ? offset.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, comp.crownType) : Vector3.zero;
					AlienPartGenerator.RotationOffset offset2 = bodyAddon.offsets.GetOffset(rotation);
					Vector3 vector2 = a + ((offset2 != null) ? offset2.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, comp.crownType) : Vector3.zero);
					vector2.y = (bodyAddon.inFrontOfBody ? (0.3f + vector2.y) : (-0.3f - vector2.y));
					float num = bodyAddon.angle;
					if (rotation == Rot4.North)
					{
						if (bodyAddon.layerInvert)
						{
							vector2.y = -vector2.y;
						}
						num = 0f;
					}
					if (rotation == Rot4.East)
					{
						num = -num;
						vector2.x = -vector2.x;
					}

					vector = vector + Quaternion.AngleAxis(pawnAnimator.bodyAngle, Vector3.up) * pawn.Drawer.renderer.BaseHeadOffsetAt(pawnAnimator.bodyFacing) - pawnAnimator.getPawnHeadOffset(); //convert vector into pseudo body pos for head
					quat = Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up);
					loc = vector + (bodyAddon.alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(Mathf.Acos(Quaternion.Dot(Quaternion.identity, quat)) * 2f * 57.29578f);
					mat = graphic.MatAt(rot: pawnAnimator.headFacing);
				}
				else
                {

					AlienPartGenerator.RotationOffset offset = bodyAddon.defaultOffsets.GetOffset(rotation);
					Vector3 a = (offset != null) ? offset.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, comp.crownType) : Vector3.zero;
					AlienPartGenerator.RotationOffset offset2 = bodyAddon.offsets.GetOffset(rotation);
					Vector3 vector2 = a + ((offset2 != null) ? offset2.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, comp.crownType) : Vector3.zero);
					vector2.y = (bodyAddon.inFrontOfBody ? (0.3f + vector2.y) : (-0.3f - vector2.y));
					float num = bodyAddon.angle;
					if (rotation == Rot4.North)
					{
						if (bodyAddon.layerInvert)
						{
							vector2.y = -vector2.y;
						}
						num = 0f;
					}
					if (rotation == Rot4.East)
					{
						num = -num;
						vector2.x = -vector2.x;
					}
					quat = Quaternion.AngleAxis(pawnAnimator.bodyAngle, Vector3.up);
					loc = vector + (bodyAddon.alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(Mathf.Acos(Quaternion.Dot(Quaternion.identity, quat)) * 2f * 57.29578f);

				}

			}
			GenDraw.DrawMeshNowOrLater(mesh, loc, quat, mat, drawNow);

			/*
			if (pawnAnimator != null && !renderFlags.FlagSet(PawnRenderFlags.Portrait) && pawnAnimator.isAnimating && (bodyAddon.drawnInBed || bodyAddon.alignWithHead))
            {

				if ((pawn.def as ThingDef_AlienRace).defName == "Alien_Orassan")
                {
					orassan = true;

					if(bodyAddon.path.Contains("closed"))
                    {
						return;
                    }

					if (bodyAddon.bodyPart.Contains("ear"))

					{
						orassan = true;

						orassanv = new Vector3(0, 0, 0.23f);
						if (pawnAnimator.headFacing == Rot4.North)
						{
							orassanv.z -= 0.1f;
							orassanv.y += 1f;

							if(bodyAddon.bodyPart.Contains("left"))
                            {
								orassanv.x += 0.03f;
                            } else
                            {
								orassanv.x -= 0.03f;
							}

						}
						else if (pawnAnimator.headFacing == Rot4.East)
						{
							orassanv.x -= 0.1f;
						}
						else if (pawnAnimator.headFacing == Rot4.West)
						{
							orassanv.x = 0.1f;
						}
						else
                        {
							orassanv.z -= 0.1f;
							orassanv.y += 1f;

							if (bodyAddon.bodyPart.Contains("right"))
							{
								orassanv.x += 0.05f;
							}
							else
							{
								orassanv.x -= 0.05f;
							}
						}
						orassanv = orassanv.RotatedBy(pawnAnimator.headAngle);
					}
				}

			GenDraw.DrawMeshNowOrLater(mesh: graphic.MeshAt(rot: headRotInAnimation), loc: loc + orassanv + (bodyAddon.alignWithHead ? headOffset : Vector3.zero),// + v.RotatedBy(Mathf.Acos(Quaternion.Dot(Quaternion.identity, quat)) * 2f * 57.29578f),
					quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * headQuatInAnimation, mat: graphic.MatAt(rot: pawnAnimator.headFacing), drawNow: drawNow);;
			}

			else
            {

			}

		}

		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
        {
			List<CodeInstruction> ins = instructions.ToList();
			for (int i = 0; i < ins.Count; i++)
            {

				Type[] type = new Type[] { typeof(Mesh), typeof(Vector3), typeof(Quaternion), typeof(Material), typeof(bool) };

				if (ins[i].OperandIs(AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater", type)))
                {

					yield return new CodeInstruction(OpCodes.Ldloc, (object)7); //graphic
					yield return new CodeInstruction(OpCodes.Ldloc, (object)4); //bodyAddon
					yield return new CodeInstruction(OpCodes.Ldloc, (object)5); //offsetVector/AddonOffset (v)
					yield return new CodeInstruction(OpCodes.Ldarg, (object)2); //headOffset
					yield return new CodeInstruction(OpCodes.Ldarg, (object)3); //pawn
					yield return new CodeInstruction(OpCodes.Ldarg, (object)0); //renderflags
					yield return new CodeInstruction(OpCodes.Ldarg, (object)1); //vector
					yield return new CodeInstruction(OpCodes.Ldarg, (object)5); //rotation

					yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(HarmonyPatch_AlienRace), "RenderHeadAddonInAnimation"));

                }

				else
                {
					yield return ins[i];
				}
            }
        }

		public static bool Prefix(PawnRenderFlags renderFlags, ref Vector3 vector, ref Vector3 headOffset, Pawn pawn, ref Quaternion quat, ref Rot4 rotation)
		{
			if(pawn == null)
            {
				return true;
			}

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

			if(anim == null)
            {
				return true;
            }

			if (anim != null && !renderFlags.FlagSet(PawnRenderFlags.Portrait) && anim.isAnimating)
			{
				//quat = Quaternion.AngleAxis(anim.bodyAngle, Vector3.up);
			}

			return true;

		}
	}

	*/

}


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>{C76F3790-9AC0-4827-ACD5-84174238954F}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Patch_HumanoidAlienRaces</RootNamespace>
    <AssemblyName>Patch_HumanoidAlienRaces</AssemblyName>
    <TargetFrameworkVersion>v4.8</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.5\Assemblies\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <GenerateSerializationAssemblies>Off</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\839005762\1.4\Assemblies\0Harmony.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="AlienRace">
      <HintPath>..\..\..\..\..\workshop\content\294100\839005762\1.5\Assemblies\AlienRace.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">
      <HintPath>..\1.4\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="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.5\Source\Patches\Harmony_PatchAll.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="1.5\Assemblies\Patch_HumanoidAlienRaces.dll" />
  </ItemGroup>
  <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_HumanoidAlienRaces")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Patch_HumanoidAlienRaces")]
[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("c76f3790-9ac0-4827-acd5-84174238954f")]

// 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" ?>
<Defs>
	<!-- placeholder for when RimNude isn't installed -->
	<PawnRenderNodeTagDef>
		<defName>RimNude_Penis</defName>
	</PawnRenderNodeTagDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
	<!-- placeholder for when RimNude isn't installed -->
	<PawnRenderNodeTagDef>
		<defName>AnimalPenis</defName>
	</PawnRenderNodeTagDef>
</Defs>


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.5\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="AlienRace">
      <HintPath>..\..\..\..\workshop\content\294100\839005762\1.4\Assemblies\AlienRace.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.5\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>
    <Reference Include="UnityEngine.IMGUIModule">
      <HintPath>..\..\RimWorldWin64_Data\Managed\UnityEngine.IMGUIModule.dll</HintPath>
      <Private>False</Private>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="1.5\Source\Actors\Actor.cs" />
    <Compile Include="1.5\Source\Actors\AlienRaceOffset.cs" />
    <Compile Include="1.5\Source\Animations\AnimationOffsets\AnimationOffsetDef.cs" />
    <Compile Include="1.5\Source\Animations\AnimationOffsets\Offsets\AnimationOffset_AgeRange.cs" />
    <Compile Include="1.5\Source\Animations\AnimationOffsets\Offsets\AnimationOffset_BodyType.cs" />
    <Compile Include="1.5\Source\Animations\AnimationOffsets\Offsets\AnimationOffset_BodyTypeGendered.cs" />
    <Compile Include="1.5\Source\Animations\AnimationOffsets\Offsets\AnimationOffset_Single.cs" />
    <Compile Include="1.5\Source\Animations\AnimationOffsets\Offsets\BaseAnimationOffset.cs" />
    <Compile Include="1.5\Source\Animations\AnimationOffsets\Offsets\BodyTypeOffset.cs" />
    <Compile Include="1.5\Source\Animations\AnimationProps\AnimationPropDef.cs" />
    <Compile Include="1.5\Source\Animations\AnimationWorkers\AnimationWorker_KeyframesExtended.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\BasePawnTest.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\PawnTest_Hediff.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\PawnTest_Never.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\PawnTest_Humanlike.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\PawnTest_PrisonerOfColony.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\PawnTest_SlaveOfColony.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\PawnTest_Always.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\PawnTest_JobDef.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\PawnTest_Multi.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\PawnTest_Race.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\PawnTest_RJWCanBeFucked.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\PawnTest_RJWCanFuck.cs" />
    <Compile Include="1.5\Source\Animations\PawnTests\PawnTest_Trait.cs" />
    <Compile Include="1.5\Source\Comps\CompExtendedAnimator.cs" />
    <Compile Include="1.5\Source\Comps\CompProperties_ExtendedAnimator.cs" />
    <Compile Include="1.5\Source\Comps\CompProperties_ThingAnimator.cs" />
    <Compile Include="1.5\Source\Comps\CompThingAnimator.cs" />
    <Compile Include="1.5\Source\Comps\ExtendedAnimatorAnchor\BaseExtendedAnimatorAnchor.cs" />
    <Compile Include="1.5\Source\Comps\ExtendedAnimatorAnchor\ExtendedAnimatorAnchor_Thing.cs" />
    <Compile Include="1.5\Source\Comps\ExtendedAnimatorAnchor\ExtendedAnimatorAnchor_Vector3.cs" />
    <Compile Include="1.5\Source\Defs\AnimationDefOf.cs" />
    <Compile Include="1.5\Source\Animations\GroupAnimations\GroupAnimationContexts\BaseGroupAnimationContext.cs" />
    <Compile Include="1.5\Source\Animations\GroupAnimations\GroupAnimationContexts\GroupAnimationContext_RJWSex.cs" />
    <Compile Include="1.5\Source\Animations\GroupAnimations\GroupAnimationDef.cs" />
    <Compile Include="1.5\Source\Animations\GroupAnimations\GroupAnimationStages\AnimationStage.cs" />
    <Compile Include="1.5\Source\Animations\GroupAnimations\GroupAnimationStages\AnimationStage_Branch.cs" />
    <Compile Include="1.5\Source\Animations\GroupAnimations\GroupAnimationStages\AnimationStage_LoopRandomSelectChance.cs" />
    <Compile Include="1.5\Source\Animations\GroupAnimations\GroupAnimationStages\AnimationStage_TicksDuration.cs" />
    <Compile Include="1.5\Source\Animations\Keyframes\ExtendedKeyframe.cs" />
    <Compile Include="1.5\Source\MainTabWindows\MainTabWindow_OffsetConfigure.cs" />
    <Compile Include="1.5\Source\MainTabWindows\OffsetMainButtonDefOf.cs" />
    <Compile Include="1.5\Source\MainTabWindows\WorldComponent_UpdateMainTab.cs" />
    <Compile Include="1.5\Source\Patches\Harmony_PatchAll.cs" />
    <Compile Include="1.5\Source\Patches\RimworldPatches\HarmonyPatch_Dialog_DebugRenderTree.cs" />
    <Compile Include="1.5\Source\Patches\RimworldPatches\HarmonyPatch_PawnRenderer.cs" />
    <Compile Include="1.5\Source\Patches\RimworldPatches\HarmonyPatch_PawnRenderNode.cs" />
    <Compile Include="1.5\Source\Patches\RimworldPatches\HarmonyPatch_PawnRenderNodeWorker.cs" />
    <Compile Include="1.5\Source\Patches\RimworldPatches\HarmonyPatch_PawnRenderTree.cs" />
    <Compile Include="1.5\Source\Patches\RimworldPatches\HarmonyPatch_Pawn_DrawTracker.cs" />
    <Compile Include="1.5\Source\Patches\RimworldPatches\HarmonyPatch_Thing.cs" />
    <Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_Masturbate.cs" />
    <Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\JobDriver_Sex\HarmonyPatch_Animate.cs" />
    <Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\JobDriver_Sex\HarmonyPatch_PlaySexSounds.cs" />
    <Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\JobDriver_Sex\HarmonyPatch_SexTick.cs" />
    <Compile Include="1.5\Source\Patches\RJWPatches\HarmonyPatch_WorkGiverSex.cs" />
    <Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_JoinInBed.cs" />
    <Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_Sex.cs" />
    <Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_SexBaseInitiator.cs" />
    <Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\SexBaseReceivers\HarmonyPatch_JobDriver_SexBaseReceiverRaped.cs" />
    <Compile Include="1.5\Source\Patches\RJWPatches\JobDrivers\SexBaseReceivers\HarmonyPatch_JobDriver_SexBaseReceiverLoved.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\GraphicBodyTypeVariants\PawnRenderNodeProperties_GraphicBodyTypeVariants.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\GraphicBodyTypeVariants\PawnRenderNodeWorker_GraphicBodyTypeVariants.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\GraphicBodyTypeVariants\PawnRenderNode_GraphicBodyTypeVariants.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\GraphicHediffSeverityVariants\PawnRenderNodeProperties_GraphicHediffSeverityVariants.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\GraphicHediffSeverityVariants\PawnRenderNodeWorker_GraphicHediffSeverityVariants.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\GraphicHediffSeverityVariants\PawnRenderNode_GraphicHediffSeverityVariants.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\GraphicHediffVariants\PawnRenderNodeProperties_GraphicHediffVariants.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\GraphicHediffVariants\PawnRenderNodeWorker_GraphicHediffVariants.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\GraphicHediffVariants\PawnRenderNode_GraphicHediffVariants.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\GraphicVariants\PawnRenderNodeProperties_GraphicVariants.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\GraphicVariants\PawnRenderNodeWorker_GraphicVariants.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\GraphicVariants\PawnRenderNode_GraphicVariants.cs" />
    <Compile Include="1.5\Source\PawnRenderNode\TexPathVariants.cs" />
    <Compile Include="1.5\Source\RenderSubWorkers\PawnRenderSubWorker_ChangeOffset.cs" />
    <Compile Include="1.5\Source\Patches\RJWPatches\RJWAnimationSettings.cs" />
    <Compile Include="1.5\Source\RenderSubWorkers\PawnRenderSubWorker_HideWhenAnimating.cs" />
    <Compile Include="1.5\Source\Utilities\AnimationUtility.cs" />
    <Compile Include="1.5\Source\Voices\VoiceDef.cs" />
    <Compile Include="1.5\Source\Voices\VoiceDefOf.cs" />
    <Compile Include="1.5\Source\Voices\VoiceTagDef.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="1.5\Assemblies\0MultiplayerAPI.dll" />
    <Content Include="1.5\Assemblies\Rimworld-Animations.dll" />
    <Content Include="1.5\Assemblies\RJW.dll" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Beast\DogBeast\GroupAnimation_DogBeast.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Beast\DogBeast\Stage1.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Beast\DogBeast\Stage2.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Beast\DogBeast\Stage3_Variant1.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Beast\DogBeast\Stage3_Variant2_FollowupWithVariant1.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Beast\DogBeast\Stage3_Variant3.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Beast\DogBeast\Stage4.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Beast\DogBeast\Stage5_Variant1.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Beast\DogBeast\Stage5_Variant2.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Beast\DogBeast\Stage5_Variant3.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Beast\DogBeast\Stage6.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\AnimationPropDef_Cum.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\GroupAnimation_Blowjob.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\OffsetDef_Blowjob.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\Stage1.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\Stage2a.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\Stage2b.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\Stage2c.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\Stage3.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\Stage4a.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\Stage4b.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\Stage4c.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\Stage5.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\Stage6a.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Blowjob\Stage6b.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cowgirl\AnimationPropDef_Cowgirl_Xray.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cowgirl\Cowgirl_Stage1.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cowgirl\Cowgirl_Stage2_1.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cowgirl\Cowgirl_Stage2_2.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cowgirl\Cowgirl_Stage2_3.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cowgirl\Cowgirl_Stage3_XRay.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cowgirl\Cowgirl_Stage4.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cowgirl\Cowgirl_Stage_1_5.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cowgirl\GroupAnimation_Cowgirl.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cowgirl\OffsetDef_Cowgirl.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\AnimationPropDef_Saliva.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\GroupAnimation_Cunnilingus.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\OffsetDef_Cunnilingus.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\Stage1.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\Stage2a.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\Stage2b.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\Stage2c.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\Stage3_LoopOnce.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\Stage4a.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\Stage4b.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\Stage4c.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\Stage5.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cunnilingus\Stage6.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\GroupAnimation_DP.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\OffsetDef_DP.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\Stage1.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\Stage2a.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\Stage2b.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\Stage2c.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\Stage2d.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\Stage3.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\Stage4.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\Stage5a.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\Stage5b.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\Stage5c.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\DoublePenetration\Stage5_Base.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Missionary\GroupAnimation_Missionary.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Missionary\OffsetDef_Missionary.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Missionary\Stage1.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Missionary\Stage2.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Missionary\Stage2b.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Missionary\Stage2c.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Missionary\Stage3_DontLoop.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Missionary\Stage4.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Missionary\Stage5.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Missionary\Stage6.xml" />
    <Content Include="1.5\Defs\AnimationPropDefs\AnimationPropDef_Hand.xml" />
    <Content Include="1.5\Defs\OffsetDefs\OffsetDef_Placeholder.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\1 Intro\Doggystyle Intro1.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\1 Intro\Doggystyle Intro2.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\1 Intro\Doggystyle Intro3.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\1 Intro\Doggystyle1a.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\2 Main\Doggystyle1b.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\2 Main\Doggystyle2.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\2 Main\Doggystyle3.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\2 Main\Doggystyle4.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\2 Main\Doggystyle5.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\2 Main\Doggystyle6.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\2 Main\Doggystyle7.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\3 Ending\Doggystyle8.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\3 Ending\Doggystyle8a.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\3 Ending\Doggystyle8b.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\3 Ending\Doggystyle8c.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\3 Ending\Doggystyle8d.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\3 Ending\Doggystyle8e.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\Misc\AnimationPropDef_BEV.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\Misc\GroupAnimation_Doggystyle_Condom_BEV.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\Misc\OffsetDef_Doggystyle_BEV.xml" />
    <Content Include="1.5\Defs\TestDoNotPush\Experimental\Misc\TexPathVariants_Doggystyle_BEV.xml" />
    <Content Include="1.5\Defs\TexPathVariantsDefs\TexPathVariants_XrayPenis_Human.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\TestAnimation\TestAnimation1.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\TestAnimation\TestAnimation2.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\TestAnimation\TestAnimation3.xml" />
    <Content Include="1.5\Defs\AnimationPropDefs\AnimationPropDef_Knees.xml" />
    <Content Include="1.5\Defs\AnimationPropDefs\AnimationPropDef_Xray_Inside.xml" />
    <Content Include="1.5\Defs\AnimationPropDefs\AnimationPropDef_Banana.xml" />
    <Content Include="1.5\Defs\AnimationPropDefs\AnimationPropDef_Xray_Penis.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\TestAnimation\TestGroupAnimation1.xml" />
    <Content Include="1.5\Defs\MainTabDefs\MainButtonDef.xml" />
    <Content Include="1.5\Defs\OffsetDefs\OffsetDef_GroinToAppropriateHeight.xml" />
    <Content Include="1.5\Defs\SoundDefs\Sounds_Sex.xml" />
    <Content Include="1.5\Defs\TexPathVariantsDefs\TexPathVariants_Cat.xml" />
    <Content Include="1.5\Defs\TexPathVariantsDefs\TexPathVariants_Arms.xml" />
    <Content Include="1.5\Defs\TexPathVariantsDefs\TexPathVariants_Knees.xml" />
    <Content Include="1.5\Defs\TexPathVariantsDefs\TexPathVariants_Xray_Inside.xml" />
    <Content Include="1.5\Defs\TexPathVariantsDefs\TexPathVariants_XrayPenis_Horse.xml" />
    <Content Include="1.5\Defs\GroupAnimationDefs\Cowgirl\TexPathVariants_Cowgirl.xml" />
    <Content Include="1.5\Defs\VoiceDefs\VoiceDef_Human\SoundDef_HumanFemale.xml" />
    <Content Include="1.5\Defs\VoiceDefs\VoiceDef_Human\SoundDef_HumanMale.xml" />
    <Content Include="1.5\Defs\VoiceDefs\VoiceDef_Human\VoiceDef_Human.xml" />
    <Content Include="1.5\Defs\VoiceDefs\VoiceDef_Orassan.xml" />
    <Content Include="1.5\Defs\VoiceDefs\VoiceTagDef.xml" />
    <Content Include="1.5\Patches\AnimationPatchHSK.xml" />
    <Content Include="1.5\Patches\AnimationPatch_CompExtendedAnimator.xml" />
    <Content Include="1.5\Patches\AnimationPatch_PawnRenderTree_OffsetSubWorker.xml" />
    <Content Include="1.5\Patches\CompatibilityPatch_FacialAnimation.xml" />
    <Content Include="1.5\Patches\CompatibilityPatch_HCSK.xml" />
    <Content Include="1.5\Patches\CompPatches\AutoCleaner.xml" />
    <Content Include="1.5\Patches\CompPatches\CombatExtended.xml" />
    <Content Include="1.5\Patches\CompPatches\ZombieLand.xml" />
    <Content Include="1.5\Patches\Patch_GenitaliaRenderNode.xml" />
    <Content Include="1.5\Sounds\Sex\Clap_1.wav" />
    <Content Include="1.5\Sounds\Sex\Clap_2.wav" />
    <Content Include="1.5\Sounds\Sex\Clap_3.wav" />
    <Content Include="1.5\Sounds\Sex\Clap_4.wav" />
    <Content Include="1.5\Sounds\Sex\Clap_5.wav" />
    <Content Include="1.5\Sounds\Sex\Clap_6.wav" />
    <Content Include="1.5\Sounds\Sex\Clap_7.wav" />
    <Content Include="1.5\Sounds\Sex\Clap_8.wav" />
    <Content Include="1.5\Sounds\Sex\cum.wav" />
    <Content Include="1.5\Sounds\Sex\kucyu04.wav" />
    <Content Include="1.5\Sounds\Sex\Slap\Slap_1.wav" />
    <Content Include="1.5\Sounds\Sex\Slap\Slap_2.wav" />
    <Content Include="1.5\Sounds\Sex\Slap\Slap_3.wav" />
    <Content Include="1.5\Sounds\Sex\Slap\Slap_4.wav" />
    <Content Include="1.5\Sounds\Sex\Slap\Slap_5.wav" />
    <Content Include="1.5\Sounds\Sex\Slime\Slimy1.wav" />
    <Content Include="1.5\Sounds\Sex\Slime\Slimy2.wav" />
    <Content Include="1.5\Sounds\Sex\Slime\Slimy3.wav" />
    <Content Include="1.5\Sounds\Sex\Slime\Slimy4.wav" />
    <Content Include="1.5\Sounds\Sex\Slime\Slimy5.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Suck_1.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Suck_10.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Suck_2.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Suck_3.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Suck_4.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Suck_5.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Suck_6.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Suck_7.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Suck_8.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Suck_9.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Swallow_1.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Swallow_2.wav" />
    <Content Include="1.5\Sounds\Sex\Suck\Swallow_3.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Grunt1.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Grunt2.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Grunt3.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Grunt4.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Grunt5.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Grunt6.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Moan1.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Moan2.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Moan3.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Moan4.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\MoanShort1.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\MoanShort2.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\MoanShort3.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Scream1.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Scream2.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Scream3.wav" />
    <Content Include="1.5\Sounds\Voices\FVoice\Scream4.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\Grunt1.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\Grunt2.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\Grunt3.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\Grunt4.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\Moan1.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\Moan2.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\Moan3.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\Moan4.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\MoanShort1.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\MoanShort2.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\MoanShort3.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\Scream1.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\Scream2.wav" />
    <Content Include="1.5\Sounds\Voices\MVoice\Scream3.wav" />
    <Content Include="1.5\Textures\AnimationProps\Banana\Banana_east.png" />
    <Content Include="1.5\Textures\AnimationProps\Banana\Banana_north.png" />
    <Content Include="1.5\Textures\AnimationProps\Banana\Banana_south.png" />
    <Content Include="1.5\Textures\AnimationProps\Banana\Banana_west.png" />
    <Content Include="1.5\Textures\AnimationProps\Cat\Cat1_north.png" />
    <Content Include="1.5\Textures\AnimationProps\Cat\Cat2_north.png" />
    <Content Include="1.5\Textures\AnimationProps\Cum\Cum.png" />
    <Content Include="1.5\Textures\AnimationProps\Doggystyle\Doggy_Arms_north.png" />
    <Content Include="1.5\Textures\AnimationProps\Doggystyle\Doggy_Legs_north.png" />
    <Content Include="1.5\Textures\AnimationProps\Hand\Hand_north.png" />
    <Content Include="1.5\Textures\AnimationProps\MissingTexture\MissingTexture_east.png" />
    <Content Include="1.5\Textures\AnimationProps\MissingTexture\MissingTexture_north.png" />
    <Content Include="1.5\Textures\AnimationProps\MissingTexture\MissingTexture_south.png" />
    <Content Include="1.5\Textures\AnimationProps\MissingTexture\MissingTexture_west.png" />
    <Content Include="1.5\Textures\AnimationProps\CowgirlXray\XRay2-1.png" />
    <Content Include="1.5\Textures\AnimationProps\CowgirlXray\XRay2-2.png" />
    <Content Include="1.5\Textures\AnimationProps\CowgirlXray\XRay2-3.png" />
    <Content Include="1.5\Textures\AnimationProps\CowgirlXray\XRay2-4.png" />
    <Content Include="1.5\Textures\AnimationProps\CowgirlXray\XRay2-5.png" />
    <Content Include="1.5\Textures\AnimationProps\CowgirlXray\XRay2-6.png" />
    <Content Include="1.5\Textures\AnimationProps\Saliva\Saliva_north.png" />
    <Content Include="1.5\Textures\UI\MainTab.png" />
    <Content Include="About\About.xml" />
    <Content Include="About\Manifest.xml" />
    <Content Include="Languages\English\Keyed\RJWAnimations-LanguageData.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" />
    <Content Include="Patch_FacialAnimation\1.5\Patches\AnimationPatch_HideHeadWhenAnimating.xml" />
    <Content Include="Patch_NoRimNudeWorldZoo\1.5\Defs\PawnRenderNodeTagDef_AnimalPenisPlaceholder.xml" />
    <Content Include="Patch_NoRimNudeWorld\1.5\Defs\PawnRenderNodeTagDef_RimnudePenisPlaceholder.xml" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="1.5\Source\Extensions\" />
    <Folder Include="1.5\Source\Patches\OtherModPatches\" />
    <Folder Include="1.5\Source\Settings\" />
  </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}") = "Milkable-Colonists-Animations", "Milkable-Colonists-Animations\Milkable-Colonists-Animations.csproj", "{11DC70AF-FF23-4D4D-A4E5-6453664B1A12}"
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
		{11DC70AF-FF23-4D4D-A4E5-6453664B1A12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{11DC70AF-FF23-4D4D-A4E5-6453664B1A12}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{11DC70AF-FF23-4D4D-A4E5-6453664B1A12}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{11DC70AF-FF23-4D4D-A4E5-6453664B1A12}.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:
# To customize your server, make a copy of this file to `.env` and edit any
# values you want to change. Be sure to remove the `#` at the beginning of each
# line you want to modify.

# All values have reasonable defaults, so you only need to change the ones you
# want to override.

# Use production mode unless you are developing locally.
NODE_ENV=production

# ------------------------------------------------------------------------------
# General settings:

# The title displayed on the info page.
# SERVER_TITLE=Coom Tunnel

# The route name used to proxy requests to APIs, relative to the Web site root.
# PROXY_ENDPOINT_ROUTE=/proxy

# Text model requests allowed per minute per user.
# TEXT_MODEL_RATE_LIMIT=4
# Image model requests allowed per minute per user.
# IMAGE_MODEL_RATE_LIMIT=2

# Max number of context tokens a user can request at once.
# Increase this if your proxy allow GPT 32k or 128k context
# MAX_CONTEXT_TOKENS_OPENAI=16384

# Max number of output tokens a user can request at once.
# MAX_OUTPUT_TOKENS_OPENAI=400
# MAX_OUTPUT_TOKENS_ANTHROPIC=400

# Whether to show the estimated cost of consumed tokens on the info page.
# SHOW_TOKEN_COSTS=false

# Whether to automatically check API keys for validity.
# Note: CHECK_KEYS is disabled by default in local development mode, but enabled
# by default in production mode.
# CHECK_KEYS=true

# Which model types users are allowed to access.
# The following model families are recognized:
# turbo | gpt4 | gpt4-32k | gpt4-turbo | dall-e | claude | claude-opus | gemini-pro | mistral-tiny | mistral-small | mistral-medium | mistral-large | aws-claude | azure-turbo | azure-gpt4 | azure-gpt4-32k | azure-gpt4-turbo
# By default, all models are allowed except for 'dall-e'.
# To allow DALL-E image generation, uncomment the line below and add 'dall-e' to
# the list.
# ALLOWED_MODEL_FAMILIES=turbo,gpt4,gpt4-32k,gpt4-turbo,claude,claude-opus,gemini-pro,mistral-tiny,mistral-small,mistral-medium,mistral-large,aws-claude,azure-turbo,azure-gpt4,azure-gpt4-32k,azure-gpt4-turbo

# URLs from which requests will be blocked.
# BLOCKED_ORIGINS=reddit.com,9gag.com
# Message to show when requests are blocked.
# BLOCK_MESSAGE="You must be over the age of majority in your country to use this service."
# Destination to redirect blocked requests to.
# BLOCK_REDIRECT="https://roblox.com/"

# Comma-separated list of phrases that will be rejected. Only whole words are matched.
# Surround phrases with quotes if they contain commas.
# Avoid short or common phrases as this tests the entire prompt.
# REJECT_PHRASES="phrase one,phrase two,"phrase three, which has a comma",phrase four"
# Message to show when requests are rejected.
# REJECT_MESSAGE="This content violates /aicg/'s acceptable use policy."

# Whether prompts should be logged to Google Sheets.
# Requires additional setup. See `docs/google-sheets.md` for more information.
# PROMPT_LOGGING=false

# The port and network interface to listen on.
# PORT=7860
# BIND_ADDRESS=0.0.0.0

# Whether cookies should be set without the Secure flag, for hosts that don't support SSL.
# USE_INSECURE_COOKIES=false

# Detail level of logging. (trace | debug | info | warn | error)
# LOG_LEVEL=info

# ------------------------------------------------------------------------------
# Optional settings for user management, access control, and quota enforcement:
# See `docs/user-management.md` for more information and setup instructions.
# See `docs/user-quotas.md` to learn how to set up quotas.

# Which access control method to use. (none | proxy_key | user_token)
# GATEKEEPER=none
# Which persistence method to use. (memory | firebase_rtdb)
# GATEKEEPER_STORE=memory

# Maximum number of unique IPs a user can connect from. (0 for unlimited)
# MAX_IPS_PER_USER=0
# Whether user_tokens should be automatically disabled when reaching the IP limit.
# MAX_IPS_AUTO_BAN=true

# With user_token gatekeeper, whether to allow users to change their nickname.
# ALLOW_NICKNAME_CHANGES=true

# Default token quotas for each model family. (0 for unlimited)
# DALL-E "tokens" are counted at a rate of 100000 tokens per US$1.00 generated,
# which is similar to the cost of GPT-4 Turbo.
# DALL-E 3 costs around US$0.10 per image (10000 tokens).
# See `docs/dall-e-configuration.md` for more information.
# TOKEN_QUOTA_TURBO=0
# TOKEN_QUOTA_GPT4=0
# TOKEN_QUOTA_GPT4_32K=0
# TOKEN_QUOTA_GPT4_TURBO=0
# TOKEN_QUOTA_DALL_E=0
# TOKEN_QUOTA_CLAUDE=0
# TOKEN_QUOTA_GEMINI_PRO=0
# TOKEN_QUOTA_AWS_CLAUDE=0

# How often to refresh token quotas. (hourly | daily)
# Leave unset to never automatically refresh quotas.
# QUOTA_REFRESH_PERIOD=daily

# Specifies the number of proxies or load balancers in front of the server.
# For Cloudflare or Hugging Face deployments, the default of 1 is correct.
# For any other deployments, please see config.ts as the correct configuration
# depends on your setup. Misconfiguring this value can result in problems
# accurately tracking IP addresses and enforcing rate limits.
# TRUSTED_PROXIES=1

# ------------------------------------------------------------------------------
# Secrets and keys:
# For Huggingface, set them via the Secrets section in your Space's config UI. Dp not set them in .env.
# For Render, create a "secret file" called .env using the Environment tab.

# You can add multiple API keys by separating them with a comma.
# For AWS credentials, separate the access key ID, secret key, and region with a colon.
OPENAI_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ANTHROPIC_KEY=sk-ant-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# See `docs/aws-configuration.md` for more information, there may be additional steps required to set up AWS.
AWS_CREDENTIALS=myaccesskeyid:mysecretkey:us-east-1,anotheraccesskeyid:anothersecretkey:us-west-2
# See `docs/azure-configuration.md` for more information, there may be additional steps required to set up Azure.
AZURE_CREDENTIALS=azure-resource-name:deployment-id:api-key,another-azure-resource-name:another-deployment-id:another-api-key

# With proxy_key gatekeeper, the password users must provide to access the API.
# PROXY_KEY=your-secret-key

# With user_token gatekeeper, the admin password used to manage users.
# ADMIN_KEY=your-very-secret-key

# With firebase_rtdb gatekeeper storage, the Firebase project credentials.
# FIREBASE_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# FIREBASE_RTDB_URL=https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.firebaseio.com

# With prompt logging, the Google Sheets credentials.
# GOOGLE_SHEETS_SPREADSHEET_ID=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# GOOGLE_SHEETS_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

repo_name: Cucumber/oai


code:
*.7z filter=lfs diff=lfs merge=lfs -text
*.arrow filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.ckpt filter=lfs diff=lfs merge=lfs -text
*.ftz filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.h5 filter=lfs diff=lfs merge=lfs -text
*.joblib filter=lfs diff=lfs merge=lfs -text
*.lfs.* filter=lfs diff=lfs merge=lfs -text
*.mlmodel filter=lfs diff=lfs merge=lfs -text
*.model filter=lfs diff=lfs merge=lfs -text
*.msgpack filter=lfs diff=lfs merge=lfs -text
*.npy filter=lfs diff=lfs merge=lfs -text
*.npz filter=lfs diff=lfs merge=lfs -text
*.onnx filter=lfs diff=lfs merge=lfs -text
*.ot filter=lfs diff=lfs merge=lfs -text
*.parquet filter=lfs diff=lfs merge=lfs -text
*.pb filter=lfs diff=lfs merge=lfs -text
*.pickle filter=lfs diff=lfs merge=lfs -text
*.pkl filter=lfs diff=lfs merge=lfs -text
*.pt filter=lfs diff=lfs merge=lfs -text
*.pth filter=lfs diff=lfs merge=lfs -text
*.rar filter=lfs diff=lfs merge=lfs -text
*.safetensors filter=lfs diff=lfs merge=lfs -text
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
*.tar.* filter=lfs diff=lfs merge=lfs -text
*.tflite filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.wasm filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text
*tfevents* filter=lfs diff=lfs merge=lfs -text

path: .gitattributes


code:
.env*
!.env.vault
.venv
.vscode
.idea
build
greeting.md
node_modules

http-client.private.env.json

path: .gitignore


code:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run type-check

path: .husky/pre-push


code:
# OAI Reverse Proxy

Reverse proxy server for various LLM APIs.

### Table of Contents
- [What is this?](#what-is-this)
- [Features](#features)
- [Usage Instructions](#usage-instructions)
  - [Self-hosting](#self-hosting)
  - [Alternatives](#alternatives)
    - [Huggingface (outdated, not advised)](#huggingface-outdated-not-advised)
    - [Render (outdated, not advised)](#render-outdated-not-advised)
- [Local Development](#local-development)

## What is this?
This project allows you to run a reverse proxy server for various LLM APIs.

## Features
- [x] Support for multiple APIs
  - [x] [OpenAI](https://openai.com/)
  - [x] [Anthropic](https://www.anthropic.com/)
  - [x] [AWS Bedrock](https://aws.amazon.com/bedrock/)
  - [x] [Google MakerSuite/Gemini API](https://ai.google.dev/)
  - [x] [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service)
- [x] Translation from OpenAI-formatted prompts to any other API, including streaming responses
- [x] Multiple API keys with rotation and rate limit handling
- [x] Basic user management
  - [x] Simple role-based permissions
  - [x] Per-model token quotas
  - [x] Temporary user accounts
- [x] Prompt and completion logging
- [x] Abuse detection and prevention

---

## Usage Instructions
If you'd like to run your own instance of this server, you'll need to deploy it somewhere and configure it with your API keys. A few easy options are provided below, though you can also deploy it to any other service you'd like if you know what you're doing and the service supports Node.js.

### Self-hosting
[See here for instructions on how to self-host the application on your own VPS or local machine.](./docs/self-hosting.md)

**Ensure you set the `TRUSTED_PROXIES` environment variable according to your deployment.** Refer to [.env.example](./.env.example) and [config.ts](./src/config.ts) for more information.

### Alternatives
Fiz and Sekrit are working on some alternative ways to deploy this conveniently. While I'm not involved in this effort beyond providing technical advice regarding my code, I'll link to their work here for convenience: [Sekrit's rentry](https://rentry.org/sekrit)

### Huggingface (outdated, not advised)
[See here for instructions on how to deploy to a Huggingface Space.](./docs/deploy-huggingface.md)

### Render (outdated, not advised)
[See here for instructions on how to deploy to Render.com.](./docs/deploy-render.md)

## Local Development
To run the proxy locally for development or testing, install Node.js >= 18.0.0 and follow the steps below.

1. Clone the repo
2. Install dependencies with `npm install`
3. Create a `.env` file in the root of the project and add your API keys. See the [.env.example](./.env.example) file for an example.
4. Start the server in development mode with `npm run start:dev`.

You can also use `npm run start:dev:tsc` to enable project-wide type checking at the cost of slower startup times. `npm run type-check` can be used to run type checking without starting the server.

## Building
To build the project, run `npm run build`. This will compile the TypeScript code to JavaScript and output it to the `build` directory.

Note that if you are trying to build the server on a very memory-constrained (<= 1GB) VPS, you may need to run the build with `NODE_OPTIONS=--max_old_space_size=2048 npm run build` to avoid running out of memory during the build process, assuming you have swap enabled.  The application itself should run fine on a 512MB VPS for most reasonable traffic levels.

## Forking

If you are forking the repository on GitGud, you may wish to disable GitLab CI/CD or you will be spammed with emails about failed builds due not having any CI runners. You can do this by going to *Settings > General > Visibility, project features, permissions* and then disabling the "CI/CD" feature.


code:
stages:
  - build

build_image:
  stage: build
  image:
    name: gcr.io/kaniko-project/executor:debug
    entrypoint: [""]
  script:
    - |
      if [ "$CI_COMMIT_REF_NAME" = "main" ]; then
        TAG="latest"
      else
        TAG=$CI_COMMIT_REF_NAME
      fi
    - echo "Building image with tag $TAG"
    - BASE64_AUTH=$(echo -n "$DOCKER_HUB_USERNAME:$DOCKER_HUB_ACCESS_TOKEN" | base64)
    - echo "{\"auths\":{\"https://index.docker.io/v1/\":{\"auth\":\"$BASE64_AUTH\"}}}" > /kaniko/.docker/config.json
    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/docker/ci/Dockerfile --destination docker.io/khanonci/oai-reverse-proxy:$TAG --build-arg CI_COMMIT_REF_NAME=$CI_COMMIT_REF_NAME --build-arg CI_COMMIT_SHA=$CI_COMMIT_SHA --build-arg CI_PROJECT_PATH=$CI_PROJECT_PATH
  only:
    - main


code:
FROM node:18-bullseye-slim

WORKDIR /app
COPY . .

RUN npm ci
RUN npm run build
RUN npm prune --production

EXPOSE 7860
ENV PORT=7860
ENV NODE_ENV=production

ARG CI_COMMIT_REF_NAME
ARG CI_COMMIT_SHA
ARG CI_PROJECT_PATH

ENV GITGUD_BRANCH=$CI_COMMIT_REF_NAME
ENV GITGUD_COMMIT=$CI_COMMIT_SHA
ENV GITGUD_PROJECT=$CI_PROJECT_PATH

CMD [ "npm", "start" ]

path: docker/ci/Dockerfile


code:
# Before running this, create a .env and greeting.md file.
# Refer to .env.example for the required environment variables.
# User-generated content is stored in the data directory.
# When self-hosting, it's recommended to run this behind a reverse proxy like
# nginx or Caddy to handle SSL/TLS and rate limiting. Refer to
# docs/self-hosting.md for more information and an example nginx config.
version: '3.8'
services:
  oai-reverse-proxy:
    image: khanonci/oai-reverse-proxy:latest
    ports:
      - "127.0.0.1:7860:7860"
    env_file:
      - ./.env
    volumes:
      - ./greeting.md:/app/greeting.md
      - ./data:/app/data


code:
FROM node:18-bullseye-slim
RUN apt-get update && \
    apt-get install -y git
RUN git clone https://gitgud.io/khanon/oai-reverse-proxy.git /app
WORKDIR /app
RUN chown -R 1000:1000 /app
USER 1000
RUN npm install
COPY Dockerfile greeting.md* .env* ./
RUN npm run build
EXPOSE 7860
ENV NODE_ENV=production
# Huggigface free VMs have 16GB of RAM so we can be greedy
ENV NODE_OPTIONS="--max-old-space-size=12882"
CMD [ "npm", "start" ]

path: docker/huggingface/Dockerfile


code:
# syntax = docker/dockerfile:1.2

FROM node:18-bullseye-slim
RUN apt-get update && \
    apt-get install -y curl

# Unlike Huggingface, Render can only deploy straight from a git repo and
# doesn't allow you to create or modify arbitrary files via the web UI.
# To use a greeting file, set `GREETING_URL` to a URL that points to a raw
# text file containing your greeting, such as a GitHub Gist.

# You may need to clear the build cache if you change the greeting, otherwise
# Render will use the cached layer from the previous build.

WORKDIR /app
ARG GREETING_URL
RUN if [ -n "$GREETING_URL" ]; then \
    curl -sL "$GREETING_URL" > greeting.md; \
    fi
COPY package*.json greeting.md* ./
RUN npm install
COPY . .
RUN npm run build
RUN --mount=type=secret,id=_env,dst=/etc/secrets/.env cat /etc/secrets/.env >> .env
EXPOSE 10000
ENV NODE_ENV=production
CMD [ "npm", "start" ]

path: docker/render/Dockerfile


code:
openapi: 3.0.0
info:
  version: 1.0.0
  title: User Management API
paths:
  /admin/users:
    get:
      summary: List all users
      operationId: getUsers
      responses:
        "200":
          description: A list of users
          content:
            application/json:
              schema:
                type: object
                properties:
                  users:
                    type: array
                    items:
                      $ref: "#/components/schemas/User"
                  count:
                    type: integer
                    format: int32
    post:
      summary: Create a new user
      operationId: createUser
      requestBody:
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                  properties:
                    type:
                      type: string
                      enum: ["normal", "special"]
                - type: object
                  properties:
                    type:
                      type: string
                      enum: ["temporary"]
                    expiresAt:
                      type: integer
                      format: int64
                    tokenLimits:
                      $ref: "#/components/schemas/TokenCount"
      responses:
        "200":
          description: The created user's token
          content:
            application/json:
              schema:
                type: object
                properties:
                  token:
                    type: string
    put:
      summary: Bulk upsert users
      operationId: bulkUpsertUsers
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                users:
                  type: array
                  items:
                    $ref: "#/components/schemas/User"
      responses:
        "200":
          description: The upserted users
          content:
            application/json:
              schema:
                type: object
                properties:
                  upserted_users:
                    type: array
                    items:
                      $ref: "#/components/schemas/User"
                  count:
                    type: integer
                    format: int32
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string

  /admin/users/{token}:
    get:
      summary: Get a user by token
      operationId: getUser
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
      responses:
        "200":
          description: A user
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "404":
          description: Not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
    put:
      summary: Update a user by token
      operationId: upsertUser
      parameters:
        - name: token
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/User"
      responses:
        "200":
          description: The updated user
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
        "400":
          description: Bad request
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
    delete:
      summary: Disables the user with the given token
      description: Optionally accepts a `disabledReason` query parameter. Returns the disabled user.
      parameters:
        - in: path
          name: token
          required: true
          schema:
            type: string
          description: The token of the user to disable
        - in: query
          name: disabledReason
          required: false
          schema:
            type: string
          description: The reason for disabling the user
      responses:
        '200':
          description: The disabled user
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
        '404':
          description: Not found
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
components:
  schemas:
    TokenCount:
      type: object
      properties:
        turbo:
          type: integer
          format: int32
        gpt4:
          type: integer
          format: int32
        "gpt4-32k":
          type: integer
          format: int32
        claude:
          type: integer
          format: int32
    User:
      type: object
      properties:
        token:
          type: string
        ip:
          type: array
          items:
            type: string
        nickname:
          type: string
        type:
          type: string
          enum: ["normal", "special"]
        promptCount:
          type: integer
          format: int32
        tokenLimits:
          $ref: "#/components/schemas/TokenCount"
        tokenCounts:
          $ref: "#/components/schemas/TokenCount"
        createdAt:
          type: integer
          format: int64
        lastUsedAt:
          type: integer
          format: int64
        disabledAt:
          type: integer
          format: int64
        disabledReason:
          type: string
        expiresAt:
          type: integer
          format: int64


code:
# Configuring the proxy for AWS Bedrock

The proxy supports AWS Bedrock models via the `/proxy/aws/claude` endpoint. There are a few extra steps necessary to use AWS Bedrock compared to the other supported APIs.

- [Setting keys](#setting-keys)
- [Attaching policies](#attaching-policies)
- [Provisioning models](#provisioning-models)
- [Note regarding logging](#note-regarding-logging)

## Setting keys

Use the `AWS_CREDENTIALS` environment variable to set the AWS API keys.

Like other APIs, you can provide multiple keys separated by commas. Each AWS key, however, is a set of credentials including the access key, secret key, and region. These are separated by a colon (`:`).

For example:

```
AWS_CREDENTIALS=AKIA000000000000000:somesecretkey:us-east-1,AKIA111111111111111:anothersecretkey:us-west-2
```

## Attaching policies

Unless your credentials belong to the root account, the principal will need to be granted the following permissions:

- `bedrock:InvokeModel`
- `bedrock:InvokeModelWithResponseStream`
- `bedrock:GetModelInvocationLoggingConfiguration`
  - The proxy needs this to determine whether prompt/response logging is enabled. By default, the proxy won't use credentials unless it can conclusively determine that logging is disabled, for privacy reasons.

Use the IAM console or the AWS CLI to attach these policies to the principal associated with the credentials.

## Provisioning models

AWS does not automatically provide accounts with access to every model. You will need to provision the models you want to use, in the regions you want to use them in. You can do this from the AWS console.

⚠ **Models are region-specific.** Currently AWS only offers Claude in a small number of regions. Switch to the AWS region you want to use, then go to the models page and request access to **Anthropic / Claude**.

![](./assets/aws-request-model-access.png)

Access is generally granted more or less instantly. Once your account has access, you can enable the model by checking the box next to it.

You can also request Claude Instant, but support for this isn't fully implemented yet.

### Supported model IDs
Users can send these model IDs to the proxy to invoke the corresponding models.
- **Claude**
  - `anthropic.claude-v1` (~18k context, claude 1.3 -- EOL 2024-02-28)
  - `anthropic.claude-v2` (~100k context, claude 2.0)
  - `anthropic.claude-v2:1` (~200k context, claude 2.1)
- **Claude Instant**
  - `anthropic.claude-instant-v1` (~100k context, claude instant 1.2)

## Note regarding logging

By default, the proxy will refuse to use keys if it finds that logging is enabled, or if it doesn't have permission to check logging status.

If you can't attach the `bedrock:GetModelInvocationLoggingConfiguration` policy to the principal, you can set the `ALLOW_AWS_LOGGING` environment variable to `true` to force the proxy to use the keys anyway. A warning will appear on the info page when this is enabled.


code:
# Configuring the proxy for Azure

The proxy supports Azure OpenAI Service via the `/proxy/azure/openai` endpoint. The process of setting it up is slightly different from regular OpenAI.

- [Setting keys](#setting-keys)
- [Model assignment](#model-assignment)

## Setting keys

Use the `AZURE_CREDENTIALS` environment variable to set the Azure API keys.

Like other APIs, you can provide multiple keys separated by commas. Each Azure key, however, is a set of values including the Resource Name, Deployment ID, and API key. These are separated by a colon (`:`).

For example:
```
AZURE_CREDENTIALS=contoso-ml:gpt4-8k:0123456789abcdef0123456789abcdef,northwind-corp:testdeployment:0123456789abcdef0123456789abcdef
```

## Model assignment
Note that each Azure deployment is assigned a model when you create it in the Azure OpenAI Service portal. If you want to use a different model, you'll need to create a new deployment, and therefore a new key to be added to the AZURE_CREDENTIALS environment variable. Each credential only grants access to one model.

### Supported model IDs
Users can send normal OpenAI model IDs to the proxy to invoke the corresponding models. For the most part they work the same with Azure. GPT-3.5 Turbo has an ID of "gpt-35-turbo" because Azure doesn't allow periods in model names, but the proxy should automatically convert this to the correct ID.

As noted above, you can only use model IDs for which a deployment has been created and added to the proxy.

## On content filtering
Be aware that all Azure OpenAI Service deployments have content filtering enabled by default at a Medium level. Prompts or responses which are deemed to be inappropriate will be rejected by the API. This is a feature of the Azure OpenAI Service and not the proxy.

You can disable this from deployment's settings within Azure, but you would need to request an exemption from Microsoft for your organization first. See [this page](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/content-filters) for more information.