mirror of
				https://gitgud.io/AbstractConcept/rimworld-animations-patch.git
				synced 2024-08-15 00:43:27 +00:00 
			
		
		
		
	v 2.0.0
This commit is contained in:
		
							parent
							
								
									fcf187c7dd
								
							
						
					
					
						commit
						38ec4f86c1
					
				
					 68 changed files with 846 additions and 1934 deletions
				
			
		
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -19,7 +19,7 @@
 | 
			
		|||
    <DebugSymbols>true</DebugSymbols>
 | 
			
		||||
    <DebugType>full</DebugType>
 | 
			
		||||
    <Optimize>false</Optimize>
 | 
			
		||||
    <OutputPath>..\1.3\Assemblies\</OutputPath>
 | 
			
		||||
    <OutputPath>..\1.4\Assemblies\</OutputPath>
 | 
			
		||||
    <DefineConstants>DEBUG;TRACE</DefineConstants>
 | 
			
		||||
    <ErrorReport>prompt</ErrorReport>
 | 
			
		||||
    <WarningLevel>4</WarningLevel>
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +42,7 @@
 | 
			
		|||
      <Private>False</Private>
 | 
			
		||||
    </Reference>
 | 
			
		||||
    <Reference Include="AlienRace">
 | 
			
		||||
      <HintPath>..\..\..\..\..\workshop\content\294100\839005762\1.3\Assemblies\AlienRace.dll</HintPath>
 | 
			
		||||
      <HintPath>..\..\..\..\..\workshop\content\294100\839005762\1.4\Assemblies\AlienRace.dll</HintPath>
 | 
			
		||||
      <Private>False</Private>
 | 
			
		||||
    </Reference>
 | 
			
		||||
    <Reference Include="Assembly-CSharp">
 | 
			
		||||
| 
						 | 
				
			
			@ -66,34 +66,25 @@
 | 
			
		|||
      <Private>False</Private>
 | 
			
		||||
    </Reference>
 | 
			
		||||
    <Reference Include="RimNudeWorld">
 | 
			
		||||
      <HintPath>..\..\rimnude-unofficial-master\1.3 Assembly\Assemblies\RimNudeWorld.dll</HintPath>
 | 
			
		||||
      <HintPath>..\..\rimnude-unofficial-master\Assembly Folders\1.4 Assembly\Assemblies\RimNudeWorld.dll</HintPath>
 | 
			
		||||
      <Private>False</Private>
 | 
			
		||||
    </Reference>
 | 
			
		||||
    <Reference Include="Rimworld-Animations, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
 | 
			
		||||
      <SpecificVersion>False</SpecificVersion>
 | 
			
		||||
      <HintPath>..\..\rimworld-animations-master\1.3\Assemblies\Rimworld-Animations.dll</HintPath>
 | 
			
		||||
    <Reference Include="Rimworld-Animations">
 | 
			
		||||
      <HintPath>..\..\rimworld-animations-master\1.4\Assemblies\Rimworld-Animations.dll</HintPath>
 | 
			
		||||
      <Private>False</Private>
 | 
			
		||||
    </Reference>
 | 
			
		||||
    <Reference Include="RJW, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
 | 
			
		||||
      <SpecificVersion>False</SpecificVersion>
 | 
			
		||||
      <HintPath>..\..\rjw-master\1.3\Assemblies\RJW.dll</HintPath>
 | 
			
		||||
    <Reference Include="RJW">
 | 
			
		||||
      <HintPath>..\..\RJW\1.4\Assemblies\RJW.dll</HintPath>
 | 
			
		||||
      <Private>False</Private>
 | 
			
		||||
    </Reference>
 | 
			
		||||
    <Reference Include="RJW-Events">
 | 
			
		||||
      <HintPath>..\..\rjw-events-master\1.3\Assemblies\RJW-Events.dll</HintPath>
 | 
			
		||||
      <Private>False</Private>
 | 
			
		||||
    </Reference>
 | 
			
		||||
    <Reference Include="RJW-ToysAndMasturbation">
 | 
			
		||||
      <HintPath>..\..\rjw-toys-and-masturbation-master\Assemblies\RJW-ToysAndMasturbation.dll</HintPath>
 | 
			
		||||
      <Private>False</Private>
 | 
			
		||||
    </Reference>
 | 
			
		||||
    <Reference Include="RJWSexperience.Ideology">
 | 
			
		||||
      <HintPath>..\..\rjw-sexperience-ideology-master\1.3\Assemblies\RJWSexperience.Ideology.dll</HintPath>
 | 
			
		||||
      <HintPath>..\..\rjw-events-master\1.4\Assemblies\RJW-Events.dll</HintPath>
 | 
			
		||||
      <Private>False</Private>
 | 
			
		||||
    </Reference>
 | 
			
		||||
    <Reference Include="System" />
 | 
			
		||||
    <Reference Include="System.Data" />
 | 
			
		||||
    <Reference Include="System.Data.DataSetExtensions" />
 | 
			
		||||
    <Reference Include="System.Xml" />
 | 
			
		||||
    <Reference Include="UnityEngine">
 | 
			
		||||
      <HintPath>..\..\..\RimWorldWin64_Data\Managed\UnityEngine.dll</HintPath>
 | 
			
		||||
      <Private>False</Private>
 | 
			
		||||
| 
						 | 
				
			
			@ -117,20 +108,32 @@
 | 
			
		|||
    <Compile Include="Scripts\Comps\CompProperties_ApparelVisibility.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Comps\CompApparelVisibility.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Comps\CompPawnSexData.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Defs\ActorAddon.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Defs\ActorAddonDef.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Defs\ActorAnimationData.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Defs\AddonKeyframe.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Defs\BodyAddonData.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Defs\HandAnimationDef.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Extensions\StringExtension.cs" />
 | 
			
		||||
    <Compile Include="Scripts\HandMotions\HandMotion.cs" />
 | 
			
		||||
    <Compile Include="Scripts\HandMotions\RubBreasts_FacingEW.cs" />
 | 
			
		||||
    <Compile Include="Scripts\HandMotions\RubGenitals_FacingEW.cs" />
 | 
			
		||||
    <Compile Include="Scripts\HandMotions\StrokeGenitalsUpAndDown_FacingEW.cs" />
 | 
			
		||||
    <Compile Include="Scripts\HandMotions\RubGenitals_FacingNS.cs" />
 | 
			
		||||
    <Compile Include="Scripts\HandMotions\RubBreasts_FacingNS.cs" />
 | 
			
		||||
    <Compile Include="Scripts\HandMotions\StrokeGenitalsUpAndDown_FacingNS.cs" />
 | 
			
		||||
    <Compile Include="Scripts\HandMotions\StrokeGenitalsUpAndDownShort_FacingNS.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Extensions\PawnAnimationClipExt.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Defs\RimNudeData.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Enums.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Extensions\PawnKeyframeExt.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Patches\HarmonyPatch_ApparelGraphicRecordGetter.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Patches\HarmonyPatch_BabiesAndChildren.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Patches\HarmonyPatch_Pawn_ApparelTracker.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Patches\HarmonyPatch_DrawGUIOverlay.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Patches\HarmonyPatch_JobDriver.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Patches\HarmonyPatch_RJWEvents.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Patches\HarmonyPatch_ThoughtWorkers.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Settings\ApparelSettings.cs" />
 | 
			
		||||
    <Compile Include="Scripts\ThoughtWorkers\ThoughtWorker_ExposedUnderwear.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Utilities\ApparelAnimationUtility.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Utilities\ApparelSettingsUtility.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Utilities\DebugMode.cs" />
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +152,6 @@
 | 
			
		|||
    <Compile Include="Scripts\Settings\BasicSettings.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Utilities\GraphicMaskingUtility.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Utilities\MathUtility.cs" />
 | 
			
		||||
    <Compile Include="Scripts\Utilities\SexInteractionUtility.cs" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
 | 
			
		||||
</Project>
 | 
			
		||||
| 
						 | 
				
			
			@ -32,6 +32,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
            Scribe_Values.Look(ref cellPosition, "cellPosition", default);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Used to find a place to have clothes thrown onto the floor
 | 
			
		||||
        public void GenerateFloorPosition(IntVec3 apparelCell, Vector2 apparelOffset = default)
 | 
			
		||||
        {
 | 
			
		||||
            Pawn pawn = apparel.Wearer;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,12 +16,13 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
        public Graphic handGraphic = null;
 | 
			
		||||
 | 
			
		||||
        public List<BodyPartRecord> hands = new List<BodyPartRecord>();
 | 
			
		||||
        public float sizeOfPenis = 0f;
 | 
			
		||||
        public float sizeOfBreasts = 0f;
 | 
			
		||||
 | 
			
		||||
        public Dictionary<AlienPartGenerator.BodyAddon, BodyAddonData> bodyAddonData = new Dictionary<AlienPartGenerator.BodyAddon, BodyAddonData>();
 | 
			
		||||
        public Dictionary<AlienPartGenerator.BodyAddon, BodyAddonData> bodyAddonDataPortraits = new Dictionary<AlienPartGenerator.BodyAddon, BodyAddonData>();
 | 
			
		||||
 | 
			
		||||
        private Pawn pawn;
 | 
			
		||||
        private int lastExclaimationTick = -1;
 | 
			
		||||
        private int exclaimationCoolDown = 90;
 | 
			
		||||
 | 
			
		||||
        public BodyAddonData GetBodyAddonData(AlienPartGenerator.BodyAddon bodyAddon, bool isPortrait)
 | 
			
		||||
        {
 | 
			
		||||
| 
						 | 
				
			
			@ -62,25 +63,22 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
            { kvp.Value?.UpdateVisibility(); }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void UpdateHands()
 | 
			
		||||
        public void UpdateBodyPartCountAndSize()
 | 
			
		||||
        {
 | 
			
		||||
            hands = pawn?.health?.hediffSet?.GetNotMissingParts()?.Where(x => x.def.tags.Contains(BodyPartTagDefOf.ManipulationLimbCore))?.ToList();
 | 
			
		||||
 | 
			
		||||
            Hediff hediffPenis = pawn?.health?.hediffSet?.hediffs?.FirstOrDefault(x => x.def.defName.Contains("penis") == true || x.def.defName.Contains("Penis"));
 | 
			
		||||
            sizeOfPenis = hediffPenis != null ? hediffPenis.Severity : 0f;
 | 
			
		||||
 | 
			
		||||
            Hediff hediffBreasts = pawn?.health?.hediffSet?.hediffs?.FirstOrDefault(x => x.def.defName.Contains("breasts") == true || x.def.defName.Contains("Breasts") == true);
 | 
			
		||||
            sizeOfBreasts = hediffBreasts != null ? hediffBreasts.Severity : 0f;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int GetNumberOfHands()
 | 
			
		||||
        {
 | 
			
		||||
            if (hands.NullOrEmpty()) return 0;
 | 
			
		||||
            if (hands.Any() == false) return 0;
 | 
			
		||||
 | 
			
		||||
            return hands.Count;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void TryToExclaim()
 | 
			
		||||
        {
 | 
			
		||||
            if (Find.TickManager.TicksGame > exclaimationCoolDown + lastExclaimationTick)
 | 
			
		||||
            {
 | 
			
		||||
                lastExclaimationTick = Find.TickManager.TicksGame;
 | 
			
		||||
                FleckMaker.ThrowMetaIcon(pawn.Position, pawn.Map, FleckDefOf.IncapIcon);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										70
									
								
								Source/Scripts/Defs/ActorAddon.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								Source/Scripts/Defs/ActorAddon.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Verse;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public class ActorAddon
 | 
			
		||||
    {
 | 
			
		||||
        // Data to/from animationDef
 | 
			
		||||
        public string addonName;
 | 
			
		||||
        public int? anchoringActor;
 | 
			
		||||
        public string anchorName;
 | 
			
		||||
        public string layer = "Pawn";
 | 
			
		||||
        public GraphicData graphicData;
 | 
			
		||||
        public bool? render;
 | 
			
		||||
 | 
			
		||||
        // Data helper functions
 | 
			
		||||
        public string AddonName
 | 
			
		||||
        {
 | 
			
		||||
            get { return addonName; }
 | 
			
		||||
            set { addonName = value; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public int AnchoringActor
 | 
			
		||||
        {
 | 
			
		||||
            get { return anchoringActor.HasValue ? anchoringActor.Value : 0; }
 | 
			
		||||
            set { anchoringActor = value; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string AnchorName
 | 
			
		||||
        {
 | 
			
		||||
            get { return anchorName; }
 | 
			
		||||
            set { anchorName = value; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Layer
 | 
			
		||||
        {
 | 
			
		||||
            get { return layer; }
 | 
			
		||||
            set { layer = value; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public GraphicData GraphicData
 | 
			
		||||
        {
 | 
			
		||||
            get { return graphicData; }
 | 
			
		||||
            set { graphicData = value; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool Render
 | 
			
		||||
        {
 | 
			
		||||
            get { return render == true; }
 | 
			
		||||
            set { render = value; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Simple curves
 | 
			
		||||
        public SimpleCurve PosX = new SimpleCurve();
 | 
			
		||||
        public SimpleCurve PosZ = new SimpleCurve();
 | 
			
		||||
        public SimpleCurve Rotation = new SimpleCurve();
 | 
			
		||||
 | 
			
		||||
        // Constructors
 | 
			
		||||
        public ActorAddon() { }
 | 
			
		||||
 | 
			
		||||
        public ActorAddon(ActorAddonDef actorAddonDef)
 | 
			
		||||
        {
 | 
			
		||||
            this.GraphicData = actorAddonDef.graphicData;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								Source/Scripts/Defs/ActorAddonDef.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Source/Scripts/Defs/ActorAddonDef.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Verse;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public class ActorAddonDef : Def
 | 
			
		||||
    {
 | 
			
		||||
        public float scale = 1f;
 | 
			
		||||
        public GraphicData graphicData;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -16,14 +16,16 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
        public int currentStage = 0;
 | 
			
		||||
        public int stageTicks = 0;
 | 
			
		||||
        public Rot4 actorFacing = Rot4.South;
 | 
			
		||||
        public bool isMirrored;
 | 
			
		||||
 | 
			
		||||
        public ActorAnimationData(AnimationDef animationDef, int actorID, int currentStage, int stageTicks, Rot4 actorFacing)
 | 
			
		||||
        public ActorAnimationData(AnimationDef animationDef, int actorID, int currentStage, int stageTicks, Rot4 actorFacing, bool isMirrored)
 | 
			
		||||
        {
 | 
			
		||||
            this.animationDef = animationDef;
 | 
			
		||||
            this.actorID = actorID;
 | 
			
		||||
            this.currentStage = currentStage;
 | 
			
		||||
            this.stageTicks = stageTicks;
 | 
			
		||||
            this.actorFacing = actorFacing;
 | 
			
		||||
            this.isMirrored = isMirrored;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										56
									
								
								Source/Scripts/Defs/AddonKeyFrame.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								Source/Scripts/Defs/AddonKeyFrame.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,56 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public class AddonKeyframe
 | 
			
		||||
    {
 | 
			
		||||
        // Data to/from animationDef
 | 
			
		||||
        public string addonName;
 | 
			
		||||
        public float? posX;
 | 
			
		||||
        public float? posZ;
 | 
			
		||||
        public float? rotation;
 | 
			
		||||
 | 
			
		||||
        // Data serialization control
 | 
			
		||||
        public bool ShouldSerializeposX() { return posX.HasValue; }
 | 
			
		||||
        public bool ShouldSerializeposZ() { return posZ.HasValue; }
 | 
			
		||||
        public bool ShouldSerializerotation() { return rotation.HasValue; }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        // Data helper functions
 | 
			
		||||
        public string AddonName
 | 
			
		||||
        {
 | 
			
		||||
            get { return addonName; }
 | 
			
		||||
            set { addonName = value; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public float PosX
 | 
			
		||||
        {
 | 
			
		||||
            get { return posX.HasValue ? posX.Value : 0f; }
 | 
			
		||||
            set { posX = value; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public float PosZ
 | 
			
		||||
        {
 | 
			
		||||
            get { return posZ.HasValue ? posZ.Value : 0f; }
 | 
			
		||||
            set { posZ = value; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public float Rotation
 | 
			
		||||
        {
 | 
			
		||||
            get { return rotation.HasValue ? rotation.Value : 0f; }
 | 
			
		||||
            set { rotation = value; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Constructors
 | 
			
		||||
        public AddonKeyframe() { }
 | 
			
		||||
 | 
			
		||||
        public AddonKeyframe(string addonName)
 | 
			
		||||
        {
 | 
			
		||||
            this.AddonName = addonName;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -16,7 +16,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
        public BodyPartRecord bodyPartRecord;
 | 
			
		||||
        public List<Vector3> bodyAddonOffsets = new List<Vector3>();
 | 
			
		||||
        public bool alignsWithHead = false;
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        private Pawn pawn;
 | 
			
		||||
        private string bodyType;
 | 
			
		||||
        private PawnRenderFlags renderFlags;
 | 
			
		||||
| 
						 | 
				
			
			@ -31,7 +31,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
            if (isPortrait)
 | 
			
		||||
            { renderFlags |= PawnRenderFlags.Portrait; }
 | 
			
		||||
 | 
			
		||||
            bodyPartRecord = pawn.def?.race?.body?.AllParts?.FirstOrDefault(x => x.def.defName == bodyAddon?.bodyPart || x.customLabel == bodyAddon?.bodyPart);
 | 
			
		||||
            bodyPartRecord = pawn.def?.race?.body?.AllParts?.FirstOrDefault(x => x.def == bodyAddon?.bodyPart);
 | 
			
		||||
            alignsWithHead = bodyAddon.alignWithHead || (bodyPartRecord != null && bodyPartRecord.IsInGroup(BodyPartGroupDefOf.FullHead));
 | 
			
		||||
 | 
			
		||||
            GenerateOffsets();
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +43,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
            bodyType = pawn.story.bodyType.defName;
 | 
			
		||||
            bodyAddonOffsets.Clear();
 | 
			
		||||
         
 | 
			
		||||
            int bodyAddonIndex = (pawn.def as ThingDef_AlienRace).alienRace.generalSettings.alienPartGenerator.bodyAddons.ToList().IndexOf(bodyAddon);
 | 
			
		||||
            int bodyAddonIndex = (pawn.def as ThingDef_AlienRace).alienRace.generalSettings.alienPartGenerator.bodyAddons.IndexOf(bodyAddon);
 | 
			
		||||
            AlienPartGenerator.AlienComp alienComp = pawn.GetComp<AlienPartGenerator.AlienComp>();
 | 
			
		||||
            Graphic addonGraphic = alienComp.addonGraphics[bodyAddonIndex];
 | 
			
		||||
      
 | 
			
		||||
| 
						 | 
				
			
			@ -53,20 +53,20 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
 | 
			
		||||
                // Get basic offset for body addon
 | 
			
		||||
                AlienPartGenerator.RotationOffset defaultOffsets = bodyAddon.defaultOffsets.GetOffset(apparentRotation);
 | 
			
		||||
                Vector3 bodyTypeOffset = (defaultOffsets != null) ? defaultOffsets.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, alienComp.crownType) : Vector3.zero;
 | 
			
		||||
                Vector3 bodyTypeOffset = (defaultOffsets != null) ? defaultOffsets.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, pawn.story.headType) : Vector3.zero;
 | 
			
		||||
                
 | 
			
		||||
                AlienPartGenerator.RotationOffset rotationOffsets = bodyAddon.offsets.GetOffset(apparentRotation);        
 | 
			
		||||
                Vector3 bodyAddonOffset = bodyTypeOffset + ((rotationOffsets != null) ? rotationOffsets.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, alienComp.crownType) : Vector3.zero);
 | 
			
		||||
                Vector3 bodyAddonOffset = bodyTypeOffset + ((rotationOffsets != null) ? rotationOffsets.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, pawn.story.headType) : Vector3.zero);
 | 
			
		||||
 | 
			
		||||
                // Offset private parts so that they render over tattoos but under apparel (rendering under tatoos looks weird)
 | 
			
		||||
                if (bodyAddon.bodyPart == "Genitals" || bodyAddon.bodyPart == "Chest" || bodyAddon.bodyPart == "Anus" || addonGraphic.path.ToLower().Contains("belly"))
 | 
			
		||||
                if (bodyAddon.bodyPart == PatchBodyPartDefOf.Genitals || bodyAddon.bodyPart == PatchBodyPartDefOf.Chest || bodyAddon.bodyPart == PatchBodyPartDefOf.Anus || addonGraphic.path.Contains("belly") || addonGraphic.path.Contains("Belly"))
 | 
			
		||||
                {
 | 
			
		||||
                    bodyAddonOffset.y = (bodyAddonOffset.y + 0.40f) / 1000f + 0.012f;
 | 
			
		||||
 | 
			
		||||
                    // Erected penises should be drawn over apparel
 | 
			
		||||
                    if (pawn.RaceProps.Humanlike &&
 | 
			
		||||
                        addonGraphic.path.ToLower().Contains("penis") &&
 | 
			
		||||
                        addonGraphic.path.ToLower().Contains("flaccid") == false &&
 | 
			
		||||
                        (addonGraphic.path.Contains("penis") || addonGraphic.path.Contains("Penis")) &&
 | 
			
		||||
                        (addonGraphic.path.Contains("flaccid") == false && addonGraphic.path.Contains("Flaccid") == false) &&
 | 
			
		||||
                        apparentRotation == Rot4.South)
 | 
			
		||||
                    { bodyAddonOffset.y += 0.010f; }
 | 
			
		||||
                }
 | 
			
		||||
| 
						 | 
				
			
			@ -121,7 +121,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
            if (renderFlags.FlagSet(PawnRenderFlags.Portrait) == false && (pawn.GetPosture() == PawnPosture.LayingOnGroundNormal || pawn.GetPosture() == PawnPosture.LayingOnGroundFaceUp) && bodyAddon.drawnOnGround == false)
 | 
			
		||||
            { return false; }
 | 
			
		||||
 | 
			
		||||
            if (bodyAddon.backstoryRequirement.NullOrEmpty() == false && pawn.story?.AllBackstories?.Any((Backstory x) => x.identifier == bodyAddon.backstoryRequirement) == false)
 | 
			
		||||
            if (bodyAddon.backstoryRequirement != null && pawn.story?.AllBackstories?.Any((BackstoryDef x) => x == bodyAddon.backstoryRequirement) == false)
 | 
			
		||||
            { return false; }
 | 
			
		||||
 | 
			
		||||
            if (bodyAddon.drawnDesiccated == false && pawn.Corpse?.GetRotStage() == RotStage.Dessicated)
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +130,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
            if (pawn.gender == Gender.Female && bodyAddon.drawForFemale == false || pawn.gender == Gender.Male && bodyAddon.drawForMale == false)
 | 
			
		||||
            { return false; }
 | 
			
		||||
 | 
			
		||||
            if (bodyAddon.bodyTypeRequirement.NullOrEmpty() == false && pawn.story?.bodyType.ToString() != bodyAddon.bodyTypeRequirement)
 | 
			
		||||
            if (bodyAddon.bodyTypeRequirement != null && pawn.story?.bodyType != bodyAddon.bodyTypeRequirement)
 | 
			
		||||
            { return false; }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
| 
						 | 
				
			
			@ -139,7 +139,6 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
        public void UpdateVisibility()
 | 
			
		||||
        {
 | 
			
		||||
            if (pawn == null || bodyAddon == null || bodyPartRecord == null) return;
 | 
			
		||||
  
 | 
			
		||||
            canDraw = true;
 | 
			
		||||
 | 
			
		||||
            if (pawn.health?.hediffSet?.GetNotMissingParts()?.Contains(bodyPartRecord) == false)
 | 
			
		||||
| 
						 | 
				
			
			@ -156,15 +155,18 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
 | 
			
		||||
                if (comp.isBeingWorn == false) continue;
 | 
			
		||||
 | 
			
		||||
                if (bodyAddon.bodyPart == "Genitals" || bodyAddon.bodyPart == "Anus" || bodyAddon.bodyPart == "Chest" || bodyAddon.hediffGraphics?.Any(x => x.path.NullOrEmpty() == false && x.path.ToLower().Contains("belly")) == true)
 | 
			
		||||
                if (bodyAddon.bodyPart == PatchBodyPartDefOf.Genitals || 
 | 
			
		||||
                    bodyAddon.bodyPart == PatchBodyPartDefOf.Anus || 
 | 
			
		||||
                    bodyAddon.bodyPart == PatchBodyPartDefOf.Chest || 
 | 
			
		||||
                    bodyAddon.hediffGraphics?.Any(x => x.path.NullOrEmpty() == false && (x.path.Contains("belly") || x.path.Contains("Belly"))) == true)
 | 
			
		||||
                {
 | 
			
		||||
                    if ((bodyAddon.bodyPart == "Genitals" || bodyAddon.bodyPart == "Anus") && comp.coversGroin)
 | 
			
		||||
                    if ((bodyAddon.bodyPart == PatchBodyPartDefOf.Genitals || bodyAddon.bodyPart == PatchBodyPartDefOf.Anus) && comp.coversGroin)
 | 
			
		||||
                    { canDraw = false; return; };
 | 
			
		||||
 | 
			
		||||
                    if (bodyAddon.bodyPart == "Chest" && comp.coversChest)
 | 
			
		||||
                    if (bodyAddon.bodyPart == PatchBodyPartDefOf.Chest && comp.coversChest)
 | 
			
		||||
                    { canDraw = false; return; };
 | 
			
		||||
 | 
			
		||||
                    if (bodyAddon.hediffGraphics?.Any(x => x.path.NullOrEmpty() == false && x.path.ToLower().Contains("belly")) == true && comp.coversBelly)
 | 
			
		||||
                    if (bodyAddon.hediffGraphics?.Any(x => x.path.NullOrEmpty() == false && (x.path.Contains("belly") || x.path.Contains("Belly"))) == true && comp.coversBelly)
 | 
			
		||||
                    { canDraw = false; return; }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,10 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
using System.Xml.Serialization;
 | 
			
		||||
using Verse;
 | 
			
		||||
using Rimworld_Animations;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -8,8 +12,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
{
 | 
			
		||||
    public class HandAnimationDef : Def
 | 
			
		||||
    {
 | 
			
		||||
        public string animationDefName;      
 | 
			
		||||
        
 | 
			
		||||
        public AnimationDef animationDef;          
 | 
			
		||||
        public List<HandAnimationData> handAnimationData = new List<HandAnimationData>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,5 +27,23 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
        public string motion;
 | 
			
		||||
        public int cycleTime = 0;
 | 
			
		||||
        public bool mirror = false;
 | 
			
		||||
 | 
			
		||||
        private HandMotion _motion;
 | 
			
		||||
 | 
			
		||||
        public HandMotion Motion 
 | 
			
		||||
        { 
 | 
			
		||||
            get 
 | 
			
		||||
            {
 | 
			
		||||
                if (_motion == null)
 | 
			
		||||
                {
 | 
			
		||||
                    if (Type.GetType(motion) == null)
 | 
			
		||||
                    { DebugMode.Message("ERROR: Hand motion " + motion + " does not exist!"); return null; }
 | 
			
		||||
 | 
			
		||||
                    _motion = (HandMotion)Activator.CreateInstance(Type.GetType(motion));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return _motion;
 | 
			
		||||
            } 
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										54
									
								
								Source/Scripts/Extensions/PawnAnimationClipExt.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								Source/Scripts/Extensions/PawnAnimationClipExt.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,54 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Rimworld_Animations;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public class PawnAnimationClipExt : PawnAnimationClip
 | 
			
		||||
    {
 | 
			
		||||
        public List<ActorAddon> addons = new List<ActorAddon>();
 | 
			
		||||
 | 
			
		||||
        public override void buildSimpleCurves()
 | 
			
		||||
        {
 | 
			
		||||
            base.buildSimpleCurves();
 | 
			
		||||
            int keyframePosition = 0;
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < keyframes.Count; i++)
 | 
			
		||||
            {
 | 
			
		||||
                PawnKeyframeExt keyframe = keyframes[i] as PawnKeyframeExt;
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
                if (keyframe.atTick.HasValue)
 | 
			
		||||
                {
 | 
			
		||||
                    foreach (ActorAddon addon in addons)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (keyframe.addonKeyframes.Any(x => x.AddonName == addon.AddonName) == false)
 | 
			
		||||
                        { keyframe.addonKeyframes.Add(new AddonKeyframe(addon.AddonName)); }
 | 
			
		||||
 | 
			
		||||
                        addon.PosX.Add((float)keyframe.atTick / (float)duration, keyframe.GetAddonKeyframe(addon.AddonName).PosX, true);
 | 
			
		||||
                        addon.PosZ.Add((float)keyframe.atTick / (float)duration, keyframe.GetAddonKeyframe(addon.AddonName).PosZ, true);
 | 
			
		||||
                        addon.Rotation.Add((float)keyframe.atTick / (float)duration, keyframe.GetAddonKeyframe(addon.AddonName).Rotation, true);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                else
 | 
			
		||||
                {                  
 | 
			
		||||
                    foreach (ActorAddon addon in addons)
 | 
			
		||||
                    {
 | 
			
		||||
                        if (keyframe.addonKeyframes.Any(x => x.AddonName == addon.AddonName) == false)
 | 
			
		||||
                        { keyframe.addonKeyframes.Add(new AddonKeyframe(addon.AddonName)); }
 | 
			
		||||
 | 
			
		||||
                        addon.PosX.Add((float)keyframePosition / (float)duration, keyframe.GetAddonKeyframe(addon.AddonName).PosX, true);
 | 
			
		||||
                        addon.PosZ.Add((float)keyframePosition / (float)duration, keyframe.GetAddonKeyframe(addon.AddonName).PosZ, true);
 | 
			
		||||
                        addon.Rotation.Add((float)keyframePosition / (float)duration, keyframe.GetAddonKeyframe(addon.AddonName).Rotation, true);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    keyframePosition += keyframe.tickDuration;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -85,14 +85,14 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
		
 | 
			
		||||
			if (pawn.GetSexReceiver() != null)
 | 
			
		||||
			{ 
 | 
			
		||||
				List<Pawn> partners = (pawn.GetSexReceiver().jobs.curDriver as JobDriver_SexBaseReciever).parteners.ToList();
 | 
			
		||||
				List<Pawn> partners = (pawn.GetSexReceiver().jobs.curDriver as JobDriver_SexBaseReciever).parteners;
 | 
			
		||||
 | 
			
		||||
				if (partners != null)
 | 
			
		||||
				{
 | 
			
		||||
					foreach (Pawn partner in partners)
 | 
			
		||||
					{
 | 
			
		||||
						if (partner != null)
 | 
			
		||||
						{ participants = partners; }
 | 
			
		||||
						{ participants = partners; break; }
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -111,77 +111,6 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			return participants;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool IsLoverOfOther(this Pawn pawn, Pawn other)
 | 
			
		||||
		{
 | 
			
		||||
			if (pawn == null || other == null)
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			List<DirectPawnRelation> lovers = SpouseRelationUtility.GetLoveRelations(pawn, false);
 | 
			
		||||
			return lovers.Any(x => x.otherPawn == other);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool HasPrivacy(this Pawn pawn, float radius)
 | 
			
		||||
		{
 | 
			
		||||
			if (pawn.AnimalOrWildMan() || pawn.RaceProps.Humanlike == false)
 | 
			
		||||
			{ return true; }
 | 
			
		||||
 | 
			
		||||
			if (pawn.IsHavingSex() == false && pawn.IsMasturbating() == false)
 | 
			
		||||
			{ return true; }
 | 
			
		||||
 | 
			
		||||
			if (pawn.GetLord() != null && pawn.GetLord().LordJob is LordJob_Ritual)
 | 
			
		||||
			{ return true; }
 | 
			
		||||
 | 
			
		||||
			if (pawn.GetLord() != null && pawn.GetLord().LordJob is LordJob_Joinable_Party)
 | 
			
		||||
			{ return true; }
 | 
			
		||||
 | 
			
		||||
			bool hasPrivacy = true;
 | 
			
		||||
			bool isExhibitionist = xxx.has_quirk(pawn, "Exhibitionist");
 | 
			
		||||
 | 
			
		||||
			pawn.IsInBed(out Building bed);
 | 
			
		||||
 | 
			
		||||
			foreach (Thing thing in GenRadial.RadialDistinctThingsAround(pawn.Position, pawn.Map, radius, true))
 | 
			
		||||
			{
 | 
			
		||||
				Pawn witness = thing as Pawn;
 | 
			
		||||
 | 
			
		||||
				// Caught having sex
 | 
			
		||||
				if (SexInteractionUtility.PawnCaughtLovinByWitness(pawn, witness))
 | 
			
		||||
				{
 | 
			
		||||
					SexInteractionUtility.ResolveThoughtsForWhenSexIsWitnessed(pawn, witness, out bool witnessJoiningSex);
 | 
			
		||||
 | 
			
		||||
					// Try to invite intruder to join in
 | 
			
		||||
					if (witnessJoiningSex)
 | 
			
		||||
					{
 | 
			
		||||
						if (pawn.IsMasturbating())
 | 
			
		||||
						{
 | 
			
		||||
							if (bed == null)
 | 
			
		||||
							{ 
 | 
			
		||||
								Job job = new Job(xxx.quick_sex, pawn);
 | 
			
		||||
								witness.jobs.TryTakeOrderedJob(job);
 | 
			
		||||
							}
 | 
			
		||||
 | 
			
		||||
							else
 | 
			
		||||
							{
 | 
			
		||||
								Job job = new Job(xxx.casual_sex, pawn, bed);
 | 
			
		||||
								witness.jobs.TryTakeOrderedJob(job);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
 | 
			
		||||
						else if (pawn.GetSexReceiver() != null)
 | 
			
		||||
						{
 | 
			
		||||
							Job job = new Job(DefDatabase<JobDef>.GetNamed("JoinInSex", false), pawn.GetSexReceiver(), bed);
 | 
			
		||||
							witness.jobs.TryTakeOrderedJob(job);
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					// The invitation failed
 | 
			
		||||
					else
 | 
			
		||||
					{ hasPrivacy = false; }
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return hasPrivacy || isExhibitionist || BasicSettings.needPrivacy == false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static ActorAnimationData GetAnimationData(this Pawn pawn)
 | 
			
		||||
		{
 | 
			
		||||
			if (pawn.TryGetComp<CompBodyAnimator>() == null) return null;
 | 
			
		||||
| 
						 | 
				
			
			@ -192,82 +121,9 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			int currentStage = (int)AccessTools.Field(typeof(CompBodyAnimator), "curStage").GetValue(pawn.TryGetComp<CompBodyAnimator>());
 | 
			
		||||
			int stageTicks = (int)AccessTools.Field(typeof(CompBodyAnimator), "stageTicks").GetValue(pawn.TryGetComp<CompBodyAnimator>());
 | 
			
		||||
			Rot4 actorFacing = (Rot4)AccessTools.Field(typeof(CompBodyAnimator), "bodyFacing").GetValue(pawn.TryGetComp<CompBodyAnimator>());
 | 
			
		||||
			bool isMirrored = pawn.TryGetComp<CompBodyAnimator>().Mirror;
 | 
			
		||||
 | 
			
		||||
			return new ActorAnimationData(animationDef, actorID, currentStage, stageTicks, actorFacing);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static List<BodyPartRecord> GetHands(this Pawn pawn)
 | 
			
		||||
		{
 | 
			
		||||
			return pawn.health.hediffSet.GetNotMissingParts().Where(x => x.def == PatchBodyPartDefOf.Hand)?.ToList();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool HasPreceptForIssue(this Pawn pawn, string issueDefName, out Precept precept)
 | 
			
		||||
		{
 | 
			
		||||
			precept = null;
 | 
			
		||||
 | 
			
		||||
			if (pawn?.Ideo == null)
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			foreach (Precept _precept in pawn.Ideo.PreceptsListForReading)
 | 
			
		||||
			{
 | 
			
		||||
				if (_precept.def.issue.defName == issueDefName)
 | 
			
		||||
				{
 | 
			
		||||
					precept = _precept;
 | 
			
		||||
					return true; 
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool EnjoysViolence(this Pawn pawn)
 | 
			
		||||
		{
 | 
			
		||||
			if (pawn.IsAnimal() || pawn.RaceProps.IsMechanoid)
 | 
			
		||||
			{ return true; }
 | 
			
		||||
 | 
			
		||||
			if (pawn?.story?.traits?.allTraits == null || pawn?.story?.traits?.allTraits.NullOrEmpty() == true)
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			List<string> traits = new List<string>() { "Brawler", "Psychopath", "Bloodlust" };
 | 
			
		||||
 | 
			
		||||
			return pawn.story.traits.allTraits.Any(x => traits.Contains(x.def.defName));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool DislikesViolence(this Pawn pawn)
 | 
			
		||||
		{
 | 
			
		||||
			if (pawn.IsAnimal() || pawn.RaceProps.IsMechanoid)
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			if (pawn?.story?.traits?.allTraits == null || pawn?.story?.traits?.allTraits.NullOrEmpty() == true)
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			List<string> traits = new List<string>() { "Kind", "Wimp" };
 | 
			
		||||
 | 
			
		||||
			return pawn.WorkTagIsDisabled(WorkTags.Violent) || pawn.story.traits.allTraits.Any(x => traits.Contains(x.def.defName));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool HasTrait(this Pawn pawn, string trait)
 | 
			
		||||
		{
 | 
			
		||||
			if (pawn?.story?.traits?.allTraits == null || pawn.story.traits.allTraits.NullOrEmpty())
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			TraitDef traitDef = DefDatabase<TraitDef>.GetNamedSilentFail(trait);
 | 
			
		||||
 | 
			
		||||
			if (traitDef == null)
 | 
			
		||||
			{ traitDef = DefDatabase<TraitDef>.GetNamedSilentFail(trait.ToLower()); }
 | 
			
		||||
 | 
			
		||||
			return HasTrait(pawn, traitDef);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool HasTrait(this Pawn pawn, TraitDef traitDef)
 | 
			
		||||
		{
 | 
			
		||||
			if (pawn?.story?.traits?.allTraits == null || pawn.story.traits.allTraits.NullOrEmpty())
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			if (traitDef == null)
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			return pawn.story.traits.HasTrait(traitDef);
 | 
			
		||||
			return new ActorAnimationData(animationDef, actorID, currentStage, stageTicks, actorFacing, isMirrored);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										19
									
								
								Source/Scripts/Extensions/PawnKeyframeExt.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								Source/Scripts/Extensions/PawnKeyframeExt.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using Rimworld_Animations;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public class PawnKeyframeExt : PawnKeyframe
 | 
			
		||||
    {
 | 
			
		||||
        public List<AddonKeyframe> addonKeyframes;
 | 
			
		||||
 | 
			
		||||
        public AddonKeyframe GetAddonKeyframe(string addonName)
 | 
			
		||||
        {
 | 
			
		||||
            return addonKeyframes.FirstOrDefault(x => x.AddonName == addonName);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								Source/Scripts/Extensions/StringExtension.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Source/Scripts/Extensions/StringExtension.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public static class StringExtension
 | 
			
		||||
    {
 | 
			
		||||
        public static bool Contains(this string fullString, string subString, StringComparison comparer)
 | 
			
		||||
        {
 | 
			
		||||
            return fullString?.IndexOf(subString, comparer) >= 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										15
									
								
								Source/Scripts/HandMotions/HandMotion.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Source/Scripts/HandMotions/HandMotion.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
using Verse;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public abstract class HandMotion
 | 
			
		||||
    {
 | 
			
		||||
        public abstract Vector3 GetHandPosition(Pawn pawn, HandAnimationData handAnimationData, float baseAngle);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								Source/Scripts/HandMotions/RubBreasts_FacingEW.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Source/Scripts/HandMotions/RubBreasts_FacingEW.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
using Verse;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public class RubBreasts_FacingEW : HandMotion
 | 
			
		||||
    {
 | 
			
		||||
        public override Vector3 GetHandPosition(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float a = ((float)data.stageTicks % (float)handAnimationData.cycleTime) / (float)handAnimationData.cycleTime * 360f;
 | 
			
		||||
            float m = (data.actorFacing == Rot4.West ? 1f : -1f) * (handAnimationData.mirror ? -1f : 1f);
 | 
			
		||||
            float size = HandAnimationUtility.GetGenitalSize(pawn, "breasts");
 | 
			
		||||
 | 
			
		||||
            handPosition.x = (Mathf.Sin(a / 180f * Mathf.PI) * 0.005f - size * 0.25f) * m;
 | 
			
		||||
            handPosition.z = Mathf.Cos(a / 180f * Mathf.PI) * 0.015f - size * 0.125f;
 | 
			
		||||
 | 
			
		||||
            handPosition = handPosition.RotatedBy(baseAngle);
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								Source/Scripts/HandMotions/RubBreasts_FacingNS.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Source/Scripts/HandMotions/RubBreasts_FacingNS.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
using Verse;
 | 
			
		||||
using Rimworld_Animations;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public class RubBreasts_FacingNS : HandMotion
 | 
			
		||||
    {
 | 
			
		||||
        public override Vector3 GetHandPosition(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
            
 | 
			
		||||
            float a = ((float)data.stageTicks % (float)handAnimationData.cycleTime) / (float)handAnimationData.cycleTime * 360f;
 | 
			
		||||
            float m = (data.actorFacing == Rot4.North ? -1f : 1f) * (handAnimationData.mirror ? -1f : 1f) * (data.isMirrored ? -1f : 1f);
 | 
			
		||||
            float size = HandAnimationUtility.GetGenitalSize(pawn, "breasts");
 | 
			
		||||
 | 
			
		||||
            handPosition.x = (Mathf.Sin(a / 180f * Mathf.PI) * 0.05f * size - size * 0.25f) * m;
 | 
			
		||||
            handPosition.z = Mathf.Cos(a / 180f * Mathf.PI) * 0.015f - size * 0.125f;
 | 
			
		||||
 | 
			
		||||
            handPosition = handPosition.RotatedBy(baseAngle);
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										34
									
								
								Source/Scripts/HandMotions/RubGenitals_FacingEW.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								Source/Scripts/HandMotions/RubGenitals_FacingEW.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
using Verse;
 | 
			
		||||
using Rimworld_Animations;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public class RubGenitals_FacingEW : HandMotion
 | 
			
		||||
    {
 | 
			
		||||
        public override Vector3 GetHandPosition(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float a = ((float)data.stageTicks % (float)handAnimationData.cycleTime) / (float)handAnimationData.cycleTime * 360f;
 | 
			
		||||
            float m = (data.actorFacing == Rot4.West ? 1f : -1f) * (handAnimationData.mirror ? -1f : 1f);
 | 
			
		||||
 | 
			
		||||
            handPosition.x = (Mathf.Sin(a / 180f * Mathf.PI) * 0.005f - 0.05f) * m;
 | 
			
		||||
            handPosition.z = Mathf.Cos(a / 180f * Mathf.PI) * 0.015f;
 | 
			
		||||
            //handPosition.y = -0.1f;
 | 
			
		||||
 | 
			
		||||
            handPosition = handPosition.RotatedBy(baseAngle);
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										33
									
								
								Source/Scripts/HandMotions/RubGenitals_FacingNS.cs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								Source/Scripts/HandMotions/RubGenitals_FacingNS.cs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
using Verse;
 | 
			
		||||
using Rimworld_Animations;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public class RubGenitals_FacingNS : HandMotion
 | 
			
		||||
    {
 | 
			
		||||
        public override Vector3 GetHandPosition(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float a = ((float)data.stageTicks % (float)handAnimationData.cycleTime) / (float)handAnimationData.cycleTime * 360f;
 | 
			
		||||
            float m = (data.actorFacing == Rot4.North ? 1f : -1f) * (handAnimationData.mirror ? -1f : 1f) * (data.isMirrored ? -1f : 1f);
 | 
			
		||||
 | 
			
		||||
            handPosition.x = (Mathf.Sin(a / 180f * Mathf.PI) * 0.05f - 0.025f) * m;
 | 
			
		||||
            handPosition.z = Mathf.Cos(a / 180f * Mathf.PI) * 0.015f + 0.03f;
 | 
			
		||||
 | 
			
		||||
            handPosition = handPosition.RotatedBy(baseAngle);
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
using System.Xml.Serialization;
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
using Verse;
 | 
			
		||||
using RimWorld;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public class StrokeGenitalsUpAndDownShort_FacingNS : HandMotion
 | 
			
		||||
    {
 | 
			
		||||
        public override Vector3 GetHandPosition(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float p = (Mathf.PingPong(data.stageTicks, handAnimationData.cycleTime) / handAnimationData.cycleTime);
 | 
			
		||||
            float length = 0.035f;
 | 
			
		||||
 | 
			
		||||
            handPosition.x = 0;
 | 
			
		||||
            handPosition.z = length * p;
 | 
			
		||||
 | 
			
		||||
            handPosition = handPosition.RotatedBy(baseAngle);
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
using Verse;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public class StrokeGenitalsUpAndDown_FacingEW : HandMotion
 | 
			
		||||
    {
 | 
			
		||||
        public override Vector3 GetHandPosition(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float p = Mathf.PingPong(data.stageTicks, handAnimationData.cycleTime) / handAnimationData.cycleTime;
 | 
			
		||||
            float size = HandAnimationUtility.GetGenitalSize(pawn, handAnimationData.targetBodyPart) * 0.2f;
 | 
			
		||||
            float m = (data.actorFacing == Rot4.West ? -1f : 1f) * (handAnimationData.mirror ? -1f : 1f);
 | 
			
		||||
 | 
			
		||||
            handPosition.x = Mathf.Sin(m * (baseAngle + 45f) / 180f * Mathf.PI) * size * p;
 | 
			
		||||
            handPosition.z = Mathf.Cos(m * (baseAngle + 45f) / 180f * Mathf.PI) * size * p;
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using System.Globalization;
 | 
			
		||||
using System.IO;
 | 
			
		||||
using System.Xml;
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
using Verse;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public class Motion_StrokeGenitalsUpAndDown_FacingNS : HandMotion
 | 
			
		||||
    {
 | 
			
		||||
        public override Vector3 GetHandPosition(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float p = (Mathf.PingPong(data.stageTicks, handAnimationData.cycleTime) / handAnimationData.cycleTime);
 | 
			
		||||
            float size = HandAnimationUtility.GetGenitalSize(pawn, handAnimationData.targetBodyPart) * 0.2f;
 | 
			
		||||
            float m = (data.actorFacing == Rot4.North ? -1f : 1f) * (handAnimationData.mirror ? -1f : 1f) * (data.isMirrored ? -1f : 1f);
 | 
			
		||||
 | 
			
		||||
            handPosition.x = 0.025f * m;
 | 
			
		||||
            handPosition.z = size * p;
 | 
			
		||||
 | 
			
		||||
            handPosition = handPosition.RotatedBy(baseAngle);
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -29,7 +29,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
				}))();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			catch (TypeLoadException ex) { }
 | 
			
		||||
			catch (TypeLoadException) {  }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool Prefix_ShouldNotDrawAddonsForPawn(ref bool __result, Pawn pawn)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,9 +23,9 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
				LocalTargetInfo b = jobdriver.job.targetB.IsValid ? jobdriver.job.targetB : jobdriver.job.targetQueueB.FirstValid();
 | 
			
		||||
				LocalTargetInfo targetC = jobdriver.job.targetC;
 | 
			
		||||
 | 
			
		||||
				__result = JobUtility.GetResolvedJobReport(jobdriver.pawn.GetAnimationData().animationDef.label, a, b, targetC);
 | 
			
		||||
				//__result = JobUtility.GetResolvedJobReport(jobdriver.pawn.GetAnimationData().animationDef.label, a, b, targetC);
 | 
			
		||||
 | 
			
		||||
				return false;
 | 
			
		||||
				//return false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,11 +17,6 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
        {
 | 
			
		||||
            Harmony harmony = new Harmony("Rimworld_Animations_Patch");
 | 
			
		||||
            harmony.PatchAll(Assembly.GetExecutingAssembly());
 | 
			
		||||
 | 
			
		||||
            Quirk voyeur = new Quirk("Voyeur", "VoyeurQuirk", null, null);
 | 
			
		||||
 | 
			
		||||
            if (Quirk.All.Contains(voyeur) == false)
 | 
			
		||||
            { Quirk.All.Add(voyeur); }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,67 +12,11 @@ using rjw;
 | 
			
		|||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
	/*[StaticConstructorOnStartup]
 | 
			
		||||
	[HarmonyPatch(typeof(PawnRenderer), "RenderPawnInternal", new Type[]
 | 
			
		||||
	{
 | 
			
		||||
				typeof(Vector3),
 | 
			
		||||
				typeof(float),
 | 
			
		||||
				typeof(bool),
 | 
			
		||||
				typeof(Rot4),
 | 
			
		||||
				typeof(RotDrawMode),
 | 
			
		||||
				typeof(PawnRenderFlags)
 | 
			
		||||
	}
 | 
			
		||||
	)]
 | 
			
		||||
	public static class HarmonyPatch_PawnRenderer_RenderPawnInternal
 | 
			
		||||
	{
 | 
			
		||||
		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
 | 
			
		||||
		{
 | 
			
		||||
			List<CodeInstruction> ins = instructions.ToList();
 | 
			
		||||
 | 
			
		||||
			for (int i = 0; i < instructions.Count(); i++)
 | 
			
		||||
			{
 | 
			
		||||
				bool runIns = true;
 | 
			
		||||
 | 
			
		||||
				// Replaces the rotation that gets passed to DrawHeadHair with one that is based the current 'true' head orientation
 | 
			
		||||
				if (i + 8 < instructions.Count() && ins[i + 8].opcode == OpCodes.Call && ins[i + 8].operand != null && ins[i + 8].OperandIs(AccessTools.DeclaredMethod(typeof(PawnRenderer), "DrawHeadHair")))
 | 
			
		||||
				{
 | 
			
		||||
					// Get the true head rotation
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Ldarg_0);
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(PawnRenderer), "pawn"));
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Ldloc, (object)7); // local body facing
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Ldarg_S, (object)6); // renderer flags
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(AnimationPatchUtility), "PawnHeadRotInAnimation"));
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Stloc_S, (object)7); // set local body facing to true head facing
 | 
			
		||||
 | 
			
		||||
					// Pass this head rotation to a new DrawHeadHair call
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Ldarg_0);
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Ldarg_1);
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Ldloc_S, (object)6);
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Ldarg_2);
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Ldloc_S, (object)7); // local true head facing
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Ldloc_S, (object)7); // local true head facing
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Ldarg_S, (object)5); // bodyDrawType
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Ldarg_S, (object)6); // renderer flags
 | 
			
		||||
					yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(PawnRenderer), "DrawHeadHair"));
 | 
			
		||||
 | 
			
		||||
					// Skip the original call to DrawHeadHair
 | 
			
		||||
					i = i + 8;
 | 
			
		||||
 | 
			
		||||
					runIns = false;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (runIns)
 | 
			
		||||
				{
 | 
			
		||||
					yield return ins[i];
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}*/
 | 
			
		||||
 | 
			
		||||
	[StaticConstructorOnStartup]
 | 
			
		||||
	[HarmonyPatch(typeof(PawnGraphicSet), "ResolveAllGraphics")]
 | 
			
		||||
	public static class HarmonyPatch_PawnGraphicSet_ResolveAllGraphics
 | 
			
		||||
	{
 | 
			
		||||
		// Update whether body parts are visible after resolving graphics
 | 
			
		||||
		public static void Postfix(PawnGraphicSet __instance)
 | 
			
		||||
		{
 | 
			
		||||
			if (__instance?.pawn?.apparel == null)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,6 @@ using Verse;
 | 
			
		|||
using Verse.AI;
 | 
			
		||||
using rjw;
 | 
			
		||||
using Rimworld_Animations;
 | 
			
		||||
using RJW_ToysAndMasturbation;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -16,48 +15,8 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
	{
 | 
			
		||||
		public static void Postfix(ref JobDriver_Sex __instance)
 | 
			
		||||
		{
 | 
			
		||||
			Pawn pawn = __instance.pawn;
 | 
			
		||||
 | 
			
		||||
			// Sets ticks so that the orgasm meter starts empty, plus stop any running animations
 | 
			
		||||
			HarmonyPatch_JobDriver_Masturbate_setup_ticks.Postfix(ref __instance);
 | 
			
		||||
 | 
			
		||||
			// Invite another for a threesome?
 | 
			
		||||
			if (RJWHookupSettings.QuickHookupsEnabled &&
 | 
			
		||||
				__instance is JobDriver_SexBaseInitiator &&
 | 
			
		||||
				pawn.GetAllSexParticipants().Count == 2 &&
 | 
			
		||||
				(__instance is JobDriver_JoinInSex) == false &&
 | 
			
		||||
				Random.value < BasicSettings.chanceForOtherToJoinInSex)
 | 
			
		||||
			{
 | 
			
		||||
				DebugMode.Message("Find another to join in sex");
 | 
			
		||||
 | 
			
		||||
				List<Pawn> candidates = new List<Pawn>();
 | 
			
		||||
				float radius = 4f;
 | 
			
		||||
 | 
			
		||||
				foreach (Thing thing in GenRadial.RadialDistinctThingsAround(pawn.Position, pawn.Map, radius, true))
 | 
			
		||||
				{
 | 
			
		||||
					Pawn other = thing as Pawn;
 | 
			
		||||
					ThoughtDef thoughtDef = SexInteractionUtility.GetThoughtsAboutSexAct(other, __instance, out Precept precept);
 | 
			
		||||
 | 
			
		||||
					// Find candidates to invite
 | 
			
		||||
					if (other != null && thoughtDef?.hediff == null && SexInteractionUtility.InvitePasserbyForSex(other, pawn.GetAllSexParticipants()))
 | 
			
		||||
					{
 | 
			
		||||
						DebugMode.Message(other.NameShortColored + " is a potential candidate");
 | 
			
		||||
						candidates.Add(other);
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Invite a random candidate (weighted by attraction)
 | 
			
		||||
				if (candidates.Count > 0)
 | 
			
		||||
				{
 | 
			
		||||
					Pawn invitedPawn = candidates.RandomElementByWeight(x => SexAppraiser.would_fuck(pawn, x, false, false, true) + SexAppraiser.would_fuck(pawn.GetSexPartner(), x, false, false, true));
 | 
			
		||||
					pawn.GetSexInitiator().IsInBed(out Building bed);
 | 
			
		||||
 | 
			
		||||
					DebugMode.Message(invitedPawn.NameShortColored + " was invited to join in sex");
 | 
			
		||||
 | 
			
		||||
					Job job = new Job(DefDatabase<JobDef>.GetNamed("JoinInSex", false), pawn.GetSexPartner(), bed);
 | 
			
		||||
					invitedPawn.jobs.TryTakeOrderedJob(job);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -117,10 +76,10 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
 | 
			
		||||
				SexProps sexProps = new SexProps();
 | 
			
		||||
				sexProps.pawn = __instance.pawn;
 | 
			
		||||
				sexProps.partner = __instance.pawn;
 | 
			
		||||
				sexProps.partner = null;
 | 
			
		||||
				sexProps.sexType = xxx.rjwSextype.Masturbation;
 | 
			
		||||
 | 
			
		||||
				List<InteractionDef> interactionDefs = DefDatabase<InteractionDef>.AllDefs.Where(x => x.HasModExtension<InteractionExtension>()).ToList();
 | 
			
		||||
				IEnumerable<InteractionDef> interactionDefs = DefDatabase<InteractionDef>.AllDefs.Where(x => x.HasModExtension<InteractionExtension>());
 | 
			
		||||
				Dictionary<rjw.Modules.Interactions.Objects.InteractionWithExtension, float> interactionsPlusWeights = new Dictionary<rjw.Modules.Interactions.Objects.InteractionWithExtension, float>();
 | 
			
		||||
 | 
			
		||||
				foreach (InteractionDef interactionDef in interactionDefs)
 | 
			
		||||
| 
						 | 
				
			
			@ -161,7 +120,6 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			{ return; }
 | 
			
		||||
 | 
			
		||||
			// Get animation data
 | 
			
		||||
			AnimationDef anim = __instance.pawn.GetAnimationData()?.animationDef;
 | 
			
		||||
			List<Pawn> pawnsToAnimate = __instance.pawn.GetAllSexParticipants();
 | 
			
		||||
 | 
			
		||||
			// Sync animations across participants
 | 
			
		||||
| 
						 | 
				
			
			@ -276,8 +234,8 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			if (sexProps.sexType != xxx.rjwSextype.Oral || pawnsToAnimate.Count != 2)
 | 
			
		||||
			{ return; }
 | 
			
		||||
 | 
			
		||||
			List<AnimationDef> kissingAnims = DefDatabase<AnimationDef>.AllDefs.Where(x => x.defName.Contains("Kiss")).ToList();
 | 
			
		||||
			AnimationDef anim = kissingAnims[Random.Range(0, kissingAnims.Count)];
 | 
			
		||||
			IEnumerable<AnimationDef> kissingAnims = DefDatabase<AnimationDef>.AllDefs.Where(x => x.defName.Contains("Kiss"));
 | 
			
		||||
			AnimationDef anim = kissingAnims.ElementAt(Random.Range(0, kissingAnims.Count()));
 | 
			
		||||
 | 
			
		||||
			if (anim == null)
 | 
			
		||||
			{ DebugMode.Message("No animation found"); return; }
 | 
			
		||||
| 
						 | 
				
			
			@ -299,41 +257,6 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[HarmonyPatch(typeof(JobDriver_Sex), "SexTick")]
 | 
			
		||||
	public static class HarmonyPatch_JobDriver_Sex_SexTick
 | 
			
		||||
	{
 | 
			
		||||
		// If pawns don't have privacy, they'll stop having sex
 | 
			
		||||
		public static void Postfix(ref JobDriver_Sex __instance, Pawn pawn)
 | 
			
		||||
		{
 | 
			
		||||
			if (pawn.IsHashIntervalTick(90))
 | 
			
		||||
			{
 | 
			
		||||
				if (pawn.IsMasturbating() && pawn.HasPrivacy(8f) == false)
 | 
			
		||||
				{ pawn.jobs.EndCurrentJob(JobCondition.InterruptForced, false, false); }
 | 
			
		||||
 | 
			
		||||
				else if (pawn.IsHavingSex())
 | 
			
		||||
				{
 | 
			
		||||
					bool havePrivacy = true;
 | 
			
		||||
					List<Pawn> participants = pawn.GetAllSexParticipants();
 | 
			
		||||
 | 
			
		||||
					foreach (Pawn participant in participants)
 | 
			
		||||
					{
 | 
			
		||||
						if (participant.HasPrivacy(8f) == false)
 | 
			
		||||
						{ havePrivacy = false; }
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
					if (__instance.Sexprops != null && (__instance.Sexprops.isRape || __instance.Sexprops.isWhoring))
 | 
			
		||||
					{ return; }
 | 
			
		||||
 | 
			
		||||
					if (havePrivacy == false)
 | 
			
		||||
					{
 | 
			
		||||
						foreach (Pawn participant in participants)
 | 
			
		||||
						{ participant.jobs.EndCurrentJob(JobCondition.InterruptForced, false, false); }
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[HarmonyPatch(typeof(JobDriver_Sex), "Orgasm")]
 | 
			
		||||
	public static class HarmonyPatch_JobDriver_Sex_Orgasm
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -346,7 +269,8 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool ParticipantsDesireMoreSex(JobDriver_Sex jobdriver)
 | 
			
		||||
		// Causes too much trouble...
 | 
			
		||||
		/*public static bool ParticipantsDesireMoreSex(JobDriver_Sex jobdriver)
 | 
			
		||||
		{
 | 
			
		||||
			List<Pawn> participants = jobdriver.pawn.GetAllSexParticipants();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -387,7 +311,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
					participants[1].jobs.StartJob(job, JobCondition.Succeeded);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		}*/
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "End")]
 | 
			
		||||
| 
						 | 
				
			
			@ -398,7 +322,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
		{
 | 
			
		||||
			if (__instance.Partner != null && __instance?.Partner?.jobs?.curDriver != null && __instance.Partner.Dead == false && __instance.Partner?.jobs.curDriver is JobDriver_SexBaseReciever)
 | 
			
		||||
			{
 | 
			
		||||
				foreach (Pawn participant in (__instance.Partner?.jobs.curDriver as JobDriver_SexBaseReciever).parteners.ToList())
 | 
			
		||||
				foreach (Pawn participant in (__instance.Partner?.jobs.curDriver as JobDriver_SexBaseReciever).parteners)
 | 
			
		||||
				{ participant.jobs.EndCurrentJob(JobCondition.Succeeded, false, true); }
 | 
			
		||||
 | 
			
		||||
				(__instance.Partner?.jobs.curDriver as JobDriver_SexBaseReciever).parteners.Clear();
 | 
			
		||||
| 
						 | 
				
			
			@ -426,7 +350,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
		// Fixes mouth check
 | 
			
		||||
		public static bool Prefix(ref bool __result, Pawn pawn)
 | 
			
		||||
		{
 | 
			
		||||
			__result = pawn.health.hediffSet.GetNotMissingParts().Any(x => x.def.defName.ToLower().ContainsAny("mouth", "teeth", "jaw", "beak"));
 | 
			
		||||
			__result = pawn.health.hediffSet.GetNotMissingParts().Any(x => x.def.defName.ContainsAny("mouth", "teeth", "jaw", "beak", "Mouth", "Teeth", "Jaw", "Beak"));
 | 
			
		||||
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,7 +26,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
				}))();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			catch (TypeLoadException ex) { }
 | 
			
		||||
			catch (TypeLoadException) { }
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool Postfix_ThinkNode_ConditionalNude_Satisfied(ref bool __result, Pawn pawn)
 | 
			
		||||
| 
						 | 
				
			
			@ -34,7 +34,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			if (__result == false && pawn?.apparel?.WornApparel != null)
 | 
			
		||||
			{
 | 
			
		||||
				// If 'isBeingWorn' has a value, the apparel has already been checked if it should be discarded
 | 
			
		||||
				if (pawn.apparel.WornApparel.Any(x => x.TryGetComp<CompApparelVisibility>() != null && x.TryGetComp<CompApparelVisibility>().isBeingWorn.HasValue))
 | 
			
		||||
				if (pawn.apparel.WornApparel.NullOrEmpty() || pawn.apparel.WornApparel.Any(x => x.TryGetComp<CompApparelVisibility>() != null && x.TryGetComp<CompApparelVisibility>().isBeingWorn.HasValue))
 | 
			
		||||
				{ __result = true; }
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@ using RimNudeWorld;
 | 
			
		|||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
	[StaticConstructorOnStartup]
 | 
			
		||||
	/*[StaticConstructorOnStartup]
 | 
			
		||||
	public static class HarmonyPatch_RimNudeWorld
 | 
			
		||||
	{
 | 
			
		||||
		static HarmonyPatch_RimNudeWorld()
 | 
			
		||||
| 
						 | 
				
			
			@ -33,5 +33,5 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
		{
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	}*/
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,7 +38,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
				CompPawnSexData comp = participant.TryGetComp<CompPawnSexData>();
 | 
			
		||||
				if (comp == null) return;
 | 
			
		||||
 | 
			
		||||
				comp.UpdateHands();
 | 
			
		||||
				comp.UpdateBodyPartCountAndSize();
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,7 +48,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			CompPawnSexData comp = __instance.pawn.TryGetComp<CompPawnSexData>();
 | 
			
		||||
			if (comp == null) return;
 | 
			
		||||
 | 
			
		||||
			comp.handAnimationDef = DefDatabase<HandAnimationDef>.AllDefs.FirstOrDefault(x => x.animationDefName == __instance.pawn?.GetAnimationData()?.animationDef?.defName);
 | 
			
		||||
			comp.handAnimationDef = DefDatabase<HandAnimationDef>.AllDefs.FirstOrDefault(x => x.animationDef == __instance.pawn?.GetAnimationData()?.animationDef);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Extend the animation selector's body part check to include hands and whether the pawn is in bed or not
 | 
			
		||||
| 
						 | 
				
			
			@ -108,32 +108,32 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
		public static bool Prefix_DrawAddons(PawnRenderFlags renderFlags, Vector3 vector, Vector3 headOffset, Pawn pawn, Quaternion quat, Rot4 rotation)
 | 
			
		||||
		{
 | 
			
		||||
			if (!(pawn.def is ThingDef_AlienRace alienProps) || renderFlags.FlagSet(PawnRenderFlags.Invisible)) return false;
 | 
			
		||||
			if (AnimationPatchUtility.ShouldNotDrawAddonsForPawn(pawn))
 | 
			
		||||
			{ return false; }	
 | 
			
		||||
 | 
			
		||||
			// Get actor components and body addons
 | 
			
		||||
			List<AlienPartGenerator.BodyAddon> bodyAddons = alienProps.alienRace.generalSettings.alienPartGenerator.bodyAddons;
 | 
			
		||||
			AlienPartGenerator.AlienComp alienComp = pawn.GetComp<AlienPartGenerator.AlienComp>();
 | 
			
		||||
			CompBodyAnimator animatorComp = pawn.TryGetComp<CompBodyAnimator>();
 | 
			
		||||
			CompPawnSexData sexDataComp = pawn.TryGetComp<CompPawnSexData>();
 | 
			
		||||
			ActorAnimationData pawnAnimationData = pawn?.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
			// Try to draw apparel thrown on ground
 | 
			
		||||
			if (ApparelSettings.clothesThrownOnGround)
 | 
			
		||||
			{ ApparelAnimationUtility.TryToDrawApparelOnFloor(pawn); }
 | 
			
		||||
 | 
			
		||||
			// Get actor components and body addons
 | 
			
		||||
			List<AlienPartGenerator.BodyAddon> bodyAddons = alienProps.alienRace.generalSettings.alienPartGenerator.bodyAddons.ToList();
 | 
			
		||||
			AlienPartGenerator.AlienComp alienComp = pawn.GetComp<AlienPartGenerator.AlienComp>();
 | 
			
		||||
			CompBodyAnimator animatorComp = pawn.TryGetComp<CompBodyAnimator>();
 | 
			
		||||
			CompPawnSexData sexDataComp = pawn.TryGetComp<CompPawnSexData>();
 | 
			
		||||
			if (sexDataComp == null) return true;
 | 
			
		||||
			// Exit clauses
 | 
			
		||||
			if (BasicSettings.useLegacyAnimationSystem || AnimationPatchUtility.ShouldNotDrawAddonsForPawn(pawn) || sexDataComp == null)
 | 
			
		||||
			{ return true; }
 | 
			
		||||
 | 
			
		||||
			// Get available hands
 | 
			
		||||
			int handsAvailableCount = sexDataComp.GetNumberOfHands();
 | 
			
		||||
 | 
			
		||||
			// Sort addons by their layer offset, otherwise body parts will actualy be layered according to their position in the list
 | 
			
		||||
			// Note that sorting the addons directly seems to mess up relations between lists need by AlienRace
 | 
			
		||||
			var sortedBodyAddons = bodyAddons.Select((x, i) => new KeyValuePair<AlienPartGenerator.BodyAddon, int>(x, i)).OrderBy(x => x.Key.offsets.GetOffset(rotation).layerOffset).ToList();
 | 
			
		||||
			List<int> idxBodyAddons = sortedBodyAddons.Select(x => x.Value).ToList();
 | 
			
		||||
			var sortedBodyAddons = bodyAddons.Select((x, i) => new KeyValuePair<AlienPartGenerator.BodyAddon, int>(x, i)).OrderBy(x => x.Key.offsets.GetOffset(rotation).layerOffset);
 | 
			
		||||
			IEnumerable<int> idxBodyAddons = sortedBodyAddons.Select(x => x.Value);
 | 
			
		||||
 | 
			
		||||
			for (int idx = 0; idx < idxBodyAddons.Count; idx++)
 | 
			
		||||
			foreach(int i in idxBodyAddons)
 | 
			
		||||
			{
 | 
			
		||||
				int i = idxBodyAddons[idx];
 | 
			
		||||
 | 
			
		||||
				// Get body addon components
 | 
			
		||||
				AlienPartGenerator.BodyAddon bodyAddon = bodyAddons[i];
 | 
			
		||||
				Graphic addonGraphic = alienComp.addonGraphics[i];
 | 
			
		||||
| 
						 | 
				
			
			@ -141,33 +141,33 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
				if (bodyAddonDatum == null) continue;
 | 
			
		||||
 | 
			
		||||
				// Can draw?
 | 
			
		||||
				bool canDraw = addonGraphic.path.ToLower().Contains("featureless") == false && bodyAddonDatum.CanDraw();
 | 
			
		||||
				bool canDraw = addonGraphic.path.Contains("featureless") == false && addonGraphic.path.Contains("Featureless") == false && bodyAddonDatum.CanDraw();
 | 
			
		||||
				bool drawHand = BasicSettings.showHands && handsAvailableCount > 0 && renderFlags.FlagSet(PawnRenderFlags.Portrait) == false;
 | 
			
		||||
				
 | 
			
		||||
 | 
			
		||||
				if (canDraw == false && drawHand == false)
 | 
			
		||||
				{ continue; }
 | 
			
		||||
 | 
			
		||||
				// Get body angle
 | 
			
		||||
				float bodyAngle = animatorComp?.isAnimating == true && renderFlags.FlagSet(PawnRenderFlags.Portrait) == false ? animatorComp.bodyAngle : quat.eulerAngles.y;
 | 
			
		||||
				bodyAngle = bodyAngle < 0f ? 360f + (bodyAngle % 360f) : bodyAngle % 360f;
 | 
			
		||||
				bodyAngle = MathUtility.ClampAngle(bodyAngle);
 | 
			
		||||
 | 
			
		||||
				// Get the apparent rotation and body addon angle
 | 
			
		||||
				Rot4 apparentRotation = rotation;
 | 
			
		||||
				float bodyAddonAngle = bodyAddon.angle;
 | 
			
		||||
 | 
			
		||||
				float bodyAddonAngle = MathUtility.ClampAngle(bodyAddon.angle);
 | 
			
		||||
 | 
			
		||||
				if (renderFlags.FlagSet(PawnRenderFlags.Portrait) == false && animatorComp?.isAnimating == true)
 | 
			
		||||
				{
 | 
			
		||||
					apparentRotation = bodyAddonDatum.alignsWithHead ? animatorComp.headFacing : animatorComp.bodyFacing;
 | 
			
		||||
 | 
			
		||||
					if (animatorComp.controlGenitalAngle && addonGraphic.path.ToLower().Contains("penis"))
 | 
			
		||||
					{ bodyAddonAngle += AnimationSettings.controlGenitalRotation ? animatorComp.genitalAngle : 0f; }
 | 
			
		||||
 | 
			
		||||
					if (bodyAddonDatum.alignsWithHead)
 | 
			
		||||
					{ bodyAngle = animatorComp.headAngle; }
 | 
			
		||||
					{ bodyAngle = MathUtility.ClampAngle(animatorComp.headAngle); }
 | 
			
		||||
 | 
			
		||||
					if (animatorComp.controlGenitalAngle && (addonGraphic.path.Contains("penis") || addonGraphic.path.Contains("Penis")))
 | 
			
		||||
					{ bodyAddonAngle = MathUtility.ClampAngle(bodyAddonAngle + (AnimationSettings.controlGenitalRotation ? MathUtility.ClampAngle(animatorComp.genitalAngle) : 0f)); }
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				bodyAddonAngle = bodyAddonAngle < 0f ? 360f - (bodyAddonAngle % 360) : bodyAddonAngle % 360f;
 | 
			
		||||
				float combinedAngle = (bodyAngle + bodyAddonAngle) < 0f ? 360f + ((bodyAngle + bodyAddonAngle) % 360) : (bodyAngle + bodyAddonAngle) % 360f;
 | 
			
		||||
				float combinedAngle = MathUtility.ClampAngle(bodyAngle + bodyAddonAngle);
 | 
			
		||||
 | 
			
		||||
				Vector3 vector_ = vector;
 | 
			
		||||
				Vector3 headOffset_ = bodyAddonDatum.alignsWithHead ? headOffset : Vector3.zero;
 | 
			
		||||
| 
						 | 
				
			
			@ -190,21 +190,45 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
				{
 | 
			
		||||
					GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: apparentRotation),
 | 
			
		||||
						loc: bodyAddonPosition,
 | 
			
		||||
						quat: Mathf.Approximately(combinedAngle, 0f) ? quat : Quaternion.AngleAxis(angle: combinedAngle, axis: Vector3.up),
 | 
			
		||||
						quat: Quaternion.AngleAxis(angle: combinedAngle, axis: Vector3.up),
 | 
			
		||||
						mat: addonGraphic.MatAt(rot: apparentRotation),
 | 
			
		||||
						drawNow: renderFlags.FlagSet(PawnRenderFlags.DrawNow));
 | 
			
		||||
				}
 | 
			
		||||
				
 | 
			
		||||
 | 
			
		||||
				// Draw hand over the body part if required
 | 
			
		||||
				if (drawHand)
 | 
			
		||||
				{
 | 
			
		||||
					float finalAngle = 0;
 | 
			
		||||
 | 
			
		||||
					if (HandAnimationUtility.TryToDrawHand(pawn, addonGraphic.path, bodyAddonPosition, finalAngle, rotation, renderFlags))
 | 
			
		||||
					if (HandAnimationUtility.TryToDrawHand(pawn, pawnAnimationData, addonGraphic.path, bodyAddonPosition, finalAngle, rotation, renderFlags))
 | 
			
		||||
					{ handsAvailableCount--; }
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Add-ons
 | 
			
		||||
			if (animatorComp?.isAnimating == true)
 | 
			
		||||
			{
 | 
			
		||||
				//ActorAnimationData actorData = pawn.GetAnimationData();
 | 
			
		||||
				//PawnAnimationClipExt clip = actorData.animationDef.animationStages[actorData.currentStage].animationClips[actorData.actorID] as PawnAnimationClipExt;
 | 
			
		||||
 | 
			
		||||
				/*foreach (ActorAddon addon in clip.Addons)
 | 
			
		||||
				{
 | 
			
		||||
					actorBodypart = actorBody.GetActorBodyPart(addon.AddonName);
 | 
			
		||||
					if (actorBodypart == null) continue;
 | 
			
		||||
 | 
			
		||||
					ActorBody anchoringActorBody = actorBodies.GetComponentsInChildren<ActorBody>()?.FirstOrDefault(x => x.actorID == addon.AnchoringActor);
 | 
			
		||||
					Vector3 anchor = PawnUtility.GetBodyPartAnchor(anchoringActorBody, addon.anchorName);
 | 
			
		||||
 | 
			
		||||
					actorBodypart.transform.position = anchor + new Vector3(addon.PosX.Evaluate(clipPercent), addon.PosZ.Evaluate(clipPercent), 0);
 | 
			
		||||
					actorBodypart.transform.eulerAngles = new Vector3(0, 0, -addon.Rotation.Evaluate(clipPercent));
 | 
			
		||||
 | 
			
		||||
					actorBodypart.bodyPartRenderer.sortingLayerName = addon.Layer;
 | 
			
		||||
					//actorBodypart.bodyPartRenderer.sprite
 | 
			
		||||
 | 
			
		||||
					actorBodypart.gameObject.SetActive(addon.Render);
 | 
			
		||||
				}*/
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Body addons are sometimes are not appropriately concealed by long hair in portraits, so re-draw the pawn's hair here
 | 
			
		||||
			if (pawn.Drawer.renderer.graphics.headGraphic != null && renderFlags.FlagSet(PawnRenderFlags.Portrait) && BasicSettings.redrawHair)
 | 
			
		||||
			{
 | 
			
		||||
| 
						 | 
				
			
			@ -214,10 +238,10 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
				float headAngle = animatorComp != null && animatorComp.isAnimating && !renderFlags.FlagSet(PawnRenderFlags.Portrait) ? animatorComp.headAngle : quat.eulerAngles.y;
 | 
			
		||||
				RotDrawMode rotDrawMode = (RotDrawMode)AccessTools.Property(typeof(PawnRenderer), "CurRotDrawMode").GetValue(pawn.Drawer.renderer);
 | 
			
		||||
 | 
			
		||||
				methodInfo.Invoke(pawn.Drawer.renderer, new object[] { vector + new Vector3(0f, YOffset_Head, 0f), headOffset, headAngle, headFacing, headFacing, rotDrawMode, renderFlags });
 | 
			
		||||
				methodInfo.Invoke(pawn.Drawer.renderer, new object[] { vector + new Vector3(0f, YOffset_Head, 0f), headOffset, headAngle, headFacing, headFacing, rotDrawMode, renderFlags, true });
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return false;
 | 
			
		||||
			return false;		
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// List of potentially useful layer offsets
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,107 +0,0 @@
 | 
			
		|||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
using RimWorld;
 | 
			
		||||
using Verse;
 | 
			
		||||
using HarmonyLib;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
	[HarmonyPatch(typeof(ThoughtWorker_Precept_GroinChestHairOrFaceUncovered), "HasUncoveredGroinChestHairOrFace")]
 | 
			
		||||
	public static class HarmonyPatch_ThoughtWorker_Precept_GroinChestHairOrFaceUncovered
 | 
			
		||||
	{
 | 
			
		||||
		public static void Postfix(ref bool __result, Pawn p)
 | 
			
		||||
		{
 | 
			
		||||
			if (__result == false) return;
 | 
			
		||||
 | 
			
		||||
			Pawn pawn = p;
 | 
			
		||||
			if (ApparelSettings.underwearSufficentForIdeos == false) return;
 | 
			
		||||
 | 
			
		||||
			if (pawn?.apparel == null)
 | 
			
		||||
			{ __result = false; return; }
 | 
			
		||||
 | 
			
		||||
			if (pawn.apparel.WornApparel.NullOrEmpty())
 | 
			
		||||
			{ __result = true; return; }
 | 
			
		||||
 | 
			
		||||
			bool fullHeadCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.FullHead));
 | 
			
		||||
			bool groinCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Legs) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.GenitalsBPG));
 | 
			
		||||
			bool chestCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Torso) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.ChestBPG));
 | 
			
		||||
			bool faceCovered = fullHeadCovered || pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Eyes));
 | 
			
		||||
			bool hairCovered = fullHeadCovered || pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.UpperHead));
 | 
			
		||||
 | 
			
		||||
			__result = !(groinCovered && chestCovered && faceCovered && hairCovered);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[HarmonyPatch(typeof(ThoughtWorker_Precept_GroinChestOrHairUncovered), "HasUncoveredGroinChestOrHair")]
 | 
			
		||||
	public static class HarmonyPatch_ThoughtWorker_Precept_GroinChestOrHairUncovered
 | 
			
		||||
	{
 | 
			
		||||
		public static void Postfix(ref bool __result, Pawn p)
 | 
			
		||||
		{
 | 
			
		||||
			if (__result == false) return;
 | 
			
		||||
 | 
			
		||||
			Pawn pawn = p;
 | 
			
		||||
			if (ApparelSettings.underwearSufficentForIdeos == false) return;
 | 
			
		||||
 | 
			
		||||
			if (pawn?.apparel == null)
 | 
			
		||||
			{ __result = false; return; }
 | 
			
		||||
 | 
			
		||||
			if (pawn.apparel.WornApparel.NullOrEmpty())
 | 
			
		||||
			{ __result = true; return; }
 | 
			
		||||
 | 
			
		||||
			bool fullHeadCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.FullHead));
 | 
			
		||||
			bool groinCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Legs) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.GenitalsBPG));
 | 
			
		||||
			bool chestCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Torso) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.ChestBPG));
 | 
			
		||||
			bool hairCovered = fullHeadCovered || pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.UpperHead));
 | 
			
		||||
 | 
			
		||||
			__result = !(groinCovered && chestCovered && hairCovered);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[HarmonyPatch(typeof(ThoughtWorker_Precept_GroinOrChestUncovered), "HasUncoveredGroinOrChest")]
 | 
			
		||||
	public static class HarmonyPatch_ThoughtWorker_Precept_HasUncoveredGroinOrChest
 | 
			
		||||
	{
 | 
			
		||||
		public static void Postfix(ref bool __result, Pawn p)
 | 
			
		||||
		{
 | 
			
		||||
			if (__result == false) return;
 | 
			
		||||
 | 
			
		||||
			Pawn pawn = p;
 | 
			
		||||
			if (ApparelSettings.underwearSufficentForIdeos == false) return;
 | 
			
		||||
 | 
			
		||||
			if (pawn?.apparel == null)
 | 
			
		||||
			{ __result = false; return; }
 | 
			
		||||
 | 
			
		||||
			if (pawn.apparel.WornApparel.NullOrEmpty())
 | 
			
		||||
			{ __result = true; return; }
 | 
			
		||||
 | 
			
		||||
			bool groinCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Legs) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.GenitalsBPG));
 | 
			
		||||
			bool chestCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Torso) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.ChestBPG));
 | 
			
		||||
 | 
			
		||||
			__result = !(groinCovered && chestCovered);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	[HarmonyPatch(typeof(ThoughtWorker_Precept_GroinUncovered), "HasUncoveredGroin")]
 | 
			
		||||
	public static class HarmonyPatch_ThoughtWorker_Precept_GroinUncovered
 | 
			
		||||
	{
 | 
			
		||||
		public static void Postfix(ref bool __result, Pawn p)
 | 
			
		||||
		{
 | 
			
		||||
			if (__result == false) return;
 | 
			
		||||
 | 
			
		||||
			Pawn pawn = p;
 | 
			
		||||
			if (ApparelSettings.underwearSufficentForIdeos == false) return;
 | 
			
		||||
 | 
			
		||||
			if (pawn?.apparel == null)
 | 
			
		||||
			{ __result = false; return; }
 | 
			
		||||
 | 
			
		||||
			if (pawn.apparel.WornApparel.NullOrEmpty())
 | 
			
		||||
			{ __result = true; return; }
 | 
			
		||||
 | 
			
		||||
			bool groinCovered = pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Legs) || x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.GenitalsBPG));
 | 
			
		||||
 | 
			
		||||
			__result = !groinCovered;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -15,8 +15,6 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
		public static bool cropApparel = false;
 | 
			
		||||
		public static bool clothesThrownOnGround = true;
 | 
			
		||||
		public static RJWPreferenceSettings.Clothing apparelWornForQuickies = RJWPreferenceSettings.Clothing.Clothed;
 | 
			
		||||
		public static bool underwearSufficentForIdeos = true;
 | 
			
		||||
		public static bool exposedUnderwearMood = true;
 | 
			
		||||
 | 
			
		||||
		public override void ExposeData()
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -25,8 +23,6 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			Scribe_Values.Look(ref cropApparel, "cropApparel", false);
 | 
			
		||||
			Scribe_Values.Look(ref clothesThrownOnGround, "clothesThrownOnGround", true);
 | 
			
		||||
			Scribe_Values.Look(ref apparelWornForQuickies, "apparelWornForQuickies", RJWPreferenceSettings.Clothing.Clothed);
 | 
			
		||||
			Scribe_Values.Look(ref underwearSufficentForIdeos, "underwearSufficentForIdeos", true);
 | 
			
		||||
			Scribe_Values.Look(ref underwearSufficentForIdeos, "exposedUnderwearMood", true);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static RimNudeData GetRimNudeData(Apparel apparel)
 | 
			
		||||
| 
						 | 
				
			
			@ -46,8 +42,8 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
 | 
			
		||||
	public class ApparelSettingsDisplay : Mod
 | 
			
		||||
	{
 | 
			
		||||
		private const float windowY = 280f;
 | 
			
		||||
		private const float windowHeight = 330f;
 | 
			
		||||
		private const float windowY = 290f;
 | 
			
		||||
		private const float windowHeight = 320f;
 | 
			
		||||
 | 
			
		||||
		private Vector2 scrollPosition;
 | 
			
		||||
		private const float scrollBarWidthMargin = 18f;
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +62,11 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
		private const float singleColumnWidth = 100f;
 | 
			
		||||
		private const float doubleColumnWidth = 180f;
 | 
			
		||||
 | 
			
		||||
		private static List<ThingDef> thingDefs = new List<ThingDef>();
 | 
			
		||||
		private IEnumerable<ThingDef> shortListedApparelDefs = new List<ThingDef>();
 | 
			
		||||
		private IEnumerable<ThingDef> allApparelDefs = new List<ThingDef>();
 | 
			
		||||
 | 
			
		||||
		private IEnumerable<ModContentPack> relevantModContentPacks = new List<ModContentPack>();
 | 
			
		||||
		private ModContentPack currentModContentPack;
 | 
			
		||||
		
 | 
			
		||||
		public ApparelSettingsDisplay(ModContentPack content) : base(content)
 | 
			
		||||
		{
 | 
			
		||||
| 
						 | 
				
			
			@ -146,8 +146,6 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
 | 
			
		||||
			listingStandard.CheckboxLabeled("clothes_thrown_on_ground".Translate(), ref ApparelSettings.clothesThrownOnGround, "clothes_thrown_on_ground_desc".Translate());
 | 
			
		||||
			listingStandard.CheckboxLabeled("crop_apparel".Translate(), ref ApparelSettings.cropApparel, "crop_apparel_desc".Translate());
 | 
			
		||||
			listingStandard.CheckboxLabeled("underwear_sufficent_for_ideos".Translate(), ref ApparelSettings.underwearSufficentForIdeos, "underwear_sufficent_for_ideos_desc".Translate());
 | 
			
		||||
			listingStandard.CheckboxLabeled("exposed_underwear_mood".Translate(), ref ApparelSettings.exposedUnderwearMood, "exposed_underwear_mood_desc".Translate());
 | 
			
		||||
 | 
			
		||||
			listingStandard.End();
 | 
			
		||||
			base.DoSettingsWindowContents(inRect);
 | 
			
		||||
| 
						 | 
				
			
			@ -162,9 +160,20 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
 | 
			
		||||
			bool isEnabled = false;
 | 
			
		||||
 | 
			
		||||
			// Get a list of apparel of interest
 | 
			
		||||
			if (thingDefs.NullOrEmpty())
 | 
			
		||||
			{ thingDefs = ApparelSettingsUtility.GetApparelOfInterest(); }
 | 
			
		||||
			// Get a list of apparel and mods of interest
 | 
			
		||||
			if (allApparelDefs.Any() == false)
 | 
			
		||||
			{
 | 
			
		||||
				allApparelDefs = ApparelSettingsUtility.GetApparelOfInterest();
 | 
			
		||||
 | 
			
		||||
				foreach (ThingDef thingDef in allApparelDefs)
 | 
			
		||||
				{
 | 
			
		||||
					if (relevantModContentPacks.Contains(thingDef.modContentPack) == false)
 | 
			
		||||
					{ relevantModContentPacks.Append(thingDef.modContentPack); }
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				currentModContentPack = relevantModContentPacks.First();
 | 
			
		||||
				shortListedApparelDefs = allApparelDefs.Where(x => x.modContentPack == currentModContentPack);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Ensure that all apparel has associated RimNudeData
 | 
			
		||||
			if (ApparelSettings.rimNudeData.NullOrEmpty())
 | 
			
		||||
| 
						 | 
				
			
			@ -173,6 +182,28 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			// Add buttons to the top of the main window
 | 
			
		||||
			innerX = halfColumnWidth;
 | 
			
		||||
 | 
			
		||||
			// Mod selection button
 | 
			
		||||
			if (Widgets.ButtonText(new Rect(innerX + SettingsUtility.Align(labelWidth, doubleColumnWidth), windowY - 2 * headerHeight - 5, 2 * doubleColumnWidth + 10, headerHeight), currentModContentPack.Name))
 | 
			
		||||
			{
 | 
			
		||||
				List<FloatMenuOption> options = new List<FloatMenuOption> { };
 | 
			
		||||
 | 
			
		||||
				foreach (ModContentPack modContentPack in relevantModContentPacks)
 | 
			
		||||
				{
 | 
			
		||||
					FloatMenuOption option = new FloatMenuOption(modContentPack.Name, delegate ()
 | 
			
		||||
						{ 
 | 
			
		||||
							currentModContentPack = modContentPack;
 | 
			
		||||
							shortListedApparelDefs = allApparelDefs.Where(x => x.modContentPack == currentModContentPack);
 | 
			
		||||
						},
 | 
			
		||||
						MenuOptionPriority.Default, null, null, 0f, null, null, true, 0);
 | 
			
		||||
 | 
			
		||||
					options.Add(option);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				Find.WindowStack.Add(new FloatMenu(options));
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (shortListedApparelDefs.Any() == false) return;
 | 
			
		||||
 | 
			
		||||
			// Apparel
 | 
			
		||||
			tempRect = new Rect(innerX + SettingsUtility.Align(labelWidth, doubleColumnWidth), windowY - headerHeight - 5, labelWidth, headerHeight);
 | 
			
		||||
			Widgets.DrawHighlightIfMouseover(tempRect);
 | 
			
		||||
| 
						 | 
				
			
			@ -183,10 +214,10 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
				List<FloatMenuOption> options = new List<FloatMenuOption>
 | 
			
		||||
			{
 | 
			
		||||
				new FloatMenuOption("Sort by name", delegate()
 | 
			
		||||
				{ thingDefs = thingDefs.OrderBy(x => x.label).ToList();
 | 
			
		||||
				{ shortListedApparelDefs = shortListedApparelDefs.OrderBy(x => x.label);
 | 
			
		||||
				}, MenuOptionPriority.Default, null, null, 0f, null, null, true, 0),
 | 
			
		||||
				new FloatMenuOption("Sort by mod", delegate()
 | 
			
		||||
				{ thingDefs = ApparelSettingsUtility.GetApparelOfInterest();
 | 
			
		||||
				{ shortListedApparelDefs = ApparelSettingsUtility.GetApparelOfInterest();
 | 
			
		||||
				}, MenuOptionPriority.Default, null, null, 0f, null, null, true, 0),
 | 
			
		||||
			}; Find.WindowStack.Add(new FloatMenu(options));
 | 
			
		||||
			}; innerX += doubleColumnWidth;
 | 
			
		||||
| 
						 | 
				
			
			@ -272,7 +303,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			{ ApparelSettingsUtility.ResetRimNudeData(ApparelSettings.rimNudeData); }; innerX += singleColumnWidth + scrollBarWidthMargin;
 | 
			
		||||
 | 
			
		||||
			// Determine the height of the scrollable area
 | 
			
		||||
			int apparelCount = thingDefs.Count;
 | 
			
		||||
			int apparelCount = shortListedApparelDefs.Count();
 | 
			
		||||
			float totalContentHeight = rowHeight * (float)apparelCount;
 | 
			
		||||
 | 
			
		||||
			// Create a rect for the scroll window
 | 
			
		||||
| 
						 | 
				
			
			@ -288,7 +319,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			Widgets.DrawHighlight(contentRect);
 | 
			
		||||
			Widgets.BeginScrollView(contentRect, ref scrollPosition, scrollViewTotal);
 | 
			
		||||
 | 
			
		||||
			foreach (ThingDef thingDef in thingDefs)
 | 
			
		||||
			foreach (ThingDef thingDef in shortListedApparelDefs)
 | 
			
		||||
			{
 | 
			
		||||
				isEnabled = false;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,23 +9,12 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
{
 | 
			
		||||
	public class BasicSettings : ModSettings
 | 
			
		||||
	{
 | 
			
		||||
		public static bool autoscaleDeltaPos = true;
 | 
			
		||||
		
 | 
			
		||||
		public static bool linkChanges = false;
 | 
			
		||||
		public static bool needPrivacy = true;
 | 
			
		||||
		public static float chanceForOtherToJoinInSex = 0.25f;
 | 
			
		||||
		public static bool autoscaleDeltaPos = true;	
 | 
			
		||||
		public static bool hideNamesForSex = false;
 | 
			
		||||
		public static bool debugMode = false;
 | 
			
		||||
		public static bool showHands = true;
 | 
			
		||||
		public static bool redrawHair = true;
 | 
			
		||||
		
 | 
			
		||||
		public static bool worryAboutInfidelity = true;
 | 
			
		||||
		public static bool worryAboutBeastiality = true;
 | 
			
		||||
		public static bool worryAboutRape = true;
 | 
			
		||||
		public static bool worryAboutNecro = true;
 | 
			
		||||
		public static bool worryAboutXeno = true;
 | 
			
		||||
		public static bool ignoreSlaveRape = false;
 | 
			
		||||
		public static bool majorTabooCanStartFights = false;
 | 
			
		||||
		public static bool useLegacyAnimationSystem = false;
 | 
			
		||||
 | 
			
		||||
		public static float genitalMasturbationChance = 1.0f;
 | 
			
		||||
		public static float analMasturbationChance = 0.25f;
 | 
			
		||||
| 
						 | 
				
			
			@ -40,21 +29,10 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			base.ExposeData();
 | 
			
		||||
 | 
			
		||||
			Scribe_Values.Look(ref autoscaleDeltaPos, "autoscaleDeltaPos", true);
 | 
			
		||||
			Scribe_Values.Look(ref linkChanges, "linkChanges", false);
 | 
			
		||||
			Scribe_Values.Look(ref needPrivacy, "needPrivacy", true);
 | 
			
		||||
			Scribe_Values.Look(ref chanceForOtherToJoinInSex, "chanceForSexExtra", 0.25f);
 | 
			
		||||
			Scribe_Values.Look(ref hideNamesForSex, "hideNamesForSex", false);
 | 
			
		||||
			Scribe_Values.Look(ref debugMode, "debugMode", false);
 | 
			
		||||
			Scribe_Values.Look(ref showHands, "showHands", true);
 | 
			
		||||
			Scribe_Values.Look(ref redrawHair, "redrawHair", true);
 | 
			
		||||
			Scribe_Values.Look(ref worryAboutInfidelity, "worryAboutInfidelity", true);
 | 
			
		||||
			Scribe_Values.Look(ref worryAboutBeastiality, "worryAboutBeastiality", true);
 | 
			
		||||
			Scribe_Values.Look(ref worryAboutRape, "worryAboutRape", true);
 | 
			
		||||
			Scribe_Values.Look(ref worryAboutNecro, "worryAboutNecro", true);
 | 
			
		||||
			Scribe_Values.Look(ref worryAboutXeno, "worryAboutXeno", true);
 | 
			
		||||
			Scribe_Values.Look(ref ignoreSlaveRape, "ignoreSlaveRape", false);
 | 
			
		||||
			Scribe_Values.Look(ref majorTabooCanStartFights, "majorTabooCanStartFights", false);
 | 
			
		||||
			
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -98,23 +76,6 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			listingStandard.Label("rimworld_animation_patch_general".Translate());
 | 
			
		||||
			listingStandard.Gap(5f);
 | 
			
		||||
 | 
			
		||||
			listingStandard.CheckboxLabeled("need_privacy".Translate(), ref BasicSettings.needPrivacy, "need_privacy_desc".Translate());
 | 
			
		||||
			listingStandard.CheckboxLabeled("worry_about_infidelity".Translate(), ref BasicSettings.worryAboutInfidelity, "worry_about_infidelity_desc".Translate());
 | 
			
		||||
			listingStandard.CheckboxLabeled("worry_about_beastiality".Translate(), ref BasicSettings.worryAboutBeastiality, "worry_about_beastiality_desc".Translate());
 | 
			
		||||
			listingStandard.CheckboxLabeled("worry_about_rape".Translate(), ref BasicSettings.worryAboutRape, "worry_about_rape_desc".Translate());
 | 
			
		||||
 | 
			
		||||
			if (BasicSettings.worryAboutRape)
 | 
			
		||||
			{
 | 
			
		||||
				listingStandard.CheckboxLabeled("ignore_slave_rape".Translate(), ref BasicSettings.ignoreSlaveRape);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			listingStandard.CheckboxLabeled("worry_about_necro".Translate(), ref BasicSettings.worryAboutNecro, "worry_about_necro_desc".Translate());
 | 
			
		||||
			listingStandard.CheckboxLabeled("worry_about_xeno".Translate(), ref BasicSettings.worryAboutXeno, "worry_about_xeno_desc".Translate());
 | 
			
		||||
			listingStandard.CheckboxLabeled("major_taboo_can_start_fights".Translate(), ref BasicSettings.majorTabooCanStartFights, "major_taboo_can_start_fights_desc".Translate());
 | 
			
		||||
			
 | 
			
		||||
			listingStandard.Label("chance_for_other_to_join_in_sex".Translate() + ": " + BasicSettings.chanceForOtherToJoinInSex.ToString("F"), -1f, "chance_for_other_to_join_in_sex_desc".Translate());
 | 
			
		||||
			BasicSettings.chanceForOtherToJoinInSex = listingStandard.Slider(BasicSettings.chanceForOtherToJoinInSex, 0f, 1f);
 | 
			
		||||
 | 
			
		||||
			//listingStandard.Label("test slide: " + BasicSettings.sliderValue.ToString("F"), -1f);
 | 
			
		||||
			//BasicSettings.sliderValue = listingStandard.Slider(BasicSettings.sliderValue, -2f, 2f);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -125,10 +86,11 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			listingStandard.Label("rimworld_animation_patch_animation".Translate());
 | 
			
		||||
			listingStandard.Gap(5f);
 | 
			
		||||
 | 
			
		||||
			listingStandard.CheckboxLabeled("autoscale_delta_pos".Translate(), ref BasicSettings.autoscaleDeltaPos, "autoscale_delta_pos_desc".Translate());
 | 
			
		||||
			listingStandard.CheckboxLabeled("show_hands".Translate(), ref BasicSettings.showHands, "show_hands_desc".Translate());
 | 
			
		||||
			listingStandard.CheckboxLabeled("autoscale_delta_pos".Translate(), ref BasicSettings.autoscaleDeltaPos, "autoscale_delta_pos_desc".Translate());			
 | 
			
		||||
			listingStandard.CheckboxLabeled("redraw_hair".Translate(), ref BasicSettings.redrawHair, "redraw_hair_desc".Translate());
 | 
			
		||||
 | 
			
		||||
			listingStandard.CheckboxLabeled("show_hands".Translate(), ref BasicSettings.showHands, "show_hands_desc".Translate());
 | 
			
		||||
 | 
			
		||||
			listingStandard.End();
 | 
			
		||||
			base.DoSettingsWindowContents(inRect);
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,37 +0,0 @@
 | 
			
		|||
using System;
 | 
			
		||||
using RimWorld;
 | 
			
		||||
using Verse;
 | 
			
		||||
using rjw;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
	public class ThoughtWorker_ExposedUnderwear : ThoughtWorker
 | 
			
		||||
	{
 | 
			
		||||
		public static ThoughtState CurrentThoughtState(Pawn pawn)
 | 
			
		||||
		{
 | 
			
		||||
			if (xxx.has_quirk(pawn, "Exhibitionist"))
 | 
			
		||||
			{
 | 
			
		||||
				return ThoughtState.ActiveAtStage(1);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return ThoughtState.ActiveAtStage(0);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		protected override ThoughtState CurrentStateInternal(Pawn pawn)
 | 
			
		||||
		{
 | 
			
		||||
			if (ApparelSettings.exposedUnderwearMood == false) return false;
 | 
			
		||||
 | 
			
		||||
			if (pawn?.apparel?.WornApparel == null || pawn.apparel.WornApparel.NullOrEmpty()) return false;
 | 
			
		||||
 | 
			
		||||
			if (pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.GenitalsBPG)) && 
 | 
			
		||||
				pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Legs)) == false)
 | 
			
		||||
			{ return CurrentThoughtState(pawn); }
 | 
			
		||||
 | 
			
		||||
			if (pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(PatchBodyPartGroupDefOf.ChestBPG)) &&
 | 
			
		||||
				pawn.apparel.WornApparel.Any(x => x.def.apparel.bodyPartGroups.Contains(BodyPartGroupDefOf.Torso)) == false)
 | 
			
		||||
			{ return CurrentThoughtState(pawn); }
 | 
			
		||||
 | 
			
		||||
			return ThoughtState.Inactive;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +60,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
			if (orgasmTick > ticks)
 | 
			
		||||
			{
 | 
			
		||||
				// Safeguard for penial, vaginal and anal sex
 | 
			
		||||
				if (anim.actors[actorId].isFucked || anim.actors[actorId].isFucking || (anim.actors[actorId].requiredGenitals.NullOrEmpty() == false && anim.actors[actorId].requiredGenitals.Any(x => x.ToLower().ContainsAny("penis", "vagina", "anus"))))
 | 
			
		||||
				if (anim.actors[actorId].isFucked || anim.actors[actorId].isFucking || (anim.actors[actorId].requiredGenitals.NullOrEmpty() == false && anim.actors[actorId].requiredGenitals.Any(x => x.ContainsAny("penis", "vagina", "anus", "Penis", "Vagina", "Anus"))))
 | 
			
		||||
				{ orgasmTick = Mathf.Clamp(ticks - 5, 0, int.MaxValue); }
 | 
			
		||||
 | 
			
		||||
				// Actor does not orgasm
 | 
			
		||||
| 
						 | 
				
			
			@ -187,16 +187,17 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
		{ 
 | 
			
		||||
			{ "Alien_Orassan", new Vector3(1.4f, 1.4f, 1.4f) },
 | 
			
		||||
			{ "Alien_Cutebold", new Vector3(1.2f, 1f, 1f) },
 | 
			
		||||
			{ "Rabbie", new Vector3(-0.5f, 1f, 1f) },
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		public static Vector2 GetRaceSpecificOffsetMultipliers(Pawn pawn, string bodypart)
 | 
			
		||||
		public static Vector2 GetRaceSpecificOffsetMultipliers(Pawn pawn, BodyPartDef bodypart)
 | 
			
		||||
		{
 | 
			
		||||
			Vector2 multiplierVector = new Vector2(); 
 | 
			
		||||
 | 
			
		||||
			if (GetBodySize(pawn) == 1f || raceSpecifocChildMultipliers.TryGetValue(pawn.def.defName, out Vector3 raceVector) == false)
 | 
			
		||||
			{ raceVector = new Vector3(1f, 1f, 1f); }
 | 
			
		||||
 | 
			
		||||
			if (bodypart?.ToLower() == "tail")
 | 
			
		||||
			if (bodypart?.defName == "tail" || bodypart?.defName == "Tail")
 | 
			
		||||
			{
 | 
			
		||||
				multiplierVector.x = raceVector.z;
 | 
			
		||||
				multiplierVector.y = raceVector.x;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -153,7 +153,7 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
            { bodyPartCovered = bodyPartCovered || PrivatePartCoveredByApparel(apparel, "Chest"); }
 | 
			
		||||
 | 
			
		||||
            if (requiredGenitals.Contains("Mouth"))
 | 
			
		||||
            { bodyPartCovered = bodyPartCovered || apparel.def.apparel.CoversBodyPart(bodyParts.FirstOrDefault(x => x.def.defName.ToLower().ContainsAny("mouth", "teeth", "jaw", "beak"))); }
 | 
			
		||||
            { bodyPartCovered = bodyPartCovered || apparel.def.apparel.CoversBodyPart(bodyParts.FirstOrDefault(x => x.def.defName.ContainsAny("mouth", "teeth", "jaw", "beak", "Mouth", "Teeth", "Jaw", "Beak"))); }
 | 
			
		||||
 | 
			
		||||
            return bodyPartCovered;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,27 +13,21 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
    {
 | 
			
		||||
        public static BodyPartDef handDef;
 | 
			
		||||
 | 
			
		||||
        public static bool BodyPartIsBeingTouched(Pawn pawn, string bodypartFilePath, out List<HandAnimationData> handAnimationData)
 | 
			
		||||
        public static bool BodyPartIsBeingTouched(Pawn pawn, ActorAnimationData actorAnimationData, string bodypartFilePath, out List<HandAnimationData> handAnimationData)
 | 
			
		||||
        {
 | 
			
		||||
            handAnimationData = new List<HandAnimationData>();
 | 
			
		||||
            HandAnimationDef handAnimationDef = pawn?.TryGetComp<CompPawnSexData>()?.handAnimationDef;
 | 
			
		||||
            ActorAnimationData actorAnimationData = pawn?.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            if (handAnimationDef == null || actorAnimationData == null || bodypartFilePath.NullOrEmpty())
 | 
			
		||||
            { return false; }
 | 
			
		||||
 | 
			
		||||
            foreach (HandAnimationData datum in handAnimationDef.handAnimationData)
 | 
			
		||||
            {
 | 
			
		||||
                if (datum.stageID != actorAnimationData.currentStage || datum.actorID != actorAnimationData.actorID)
 | 
			
		||||
                { continue; }
 | 
			
		||||
                if (datum.stageID != actorAnimationData.currentStage || datum.actorID != actorAnimationData.actorID) continue;
 | 
			
		||||
                if (datum.bodySide.NullOrEmpty() == false && bodypartFilePath.Contains(datum.bodySide, StringComparison.OrdinalIgnoreCase) == false) continue;
 | 
			
		||||
 | 
			
		||||
                if (datum.bodySide.NullOrEmpty() == false && bodypartFilePath.ToLower().Contains(datum.bodySide) == false)
 | 
			
		||||
                { continue; }
 | 
			
		||||
                    
 | 
			
		||||
                if (datum.targetBodyPart.NullOrEmpty() == false && bodypartFilePath.ToLower().Contains(datum.targetBodyPart.ToLower()))
 | 
			
		||||
                { handAnimationData.Add(datum); }
 | 
			
		||||
 | 
			
		||||
                else if (datum.targetBodyParts.Any(x => bodypartFilePath.ToLower().Contains(x.ToLower())))
 | 
			
		||||
                if ((datum.targetBodyPart.NullOrEmpty() == false && bodypartFilePath.Contains(datum.targetBodyPart, StringComparison.OrdinalIgnoreCase)) || 
 | 
			
		||||
                    datum.targetBodyParts.Any(x => bodypartFilePath.Contains(x, StringComparison.OrdinalIgnoreCase)))
 | 
			
		||||
                { handAnimationData.Add(datum); }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -42,147 +36,26 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
 | 
			
		||||
        public static Vector3 GetHandPosition(Pawn pawn, HandAnimationData handAnimationData, Vector3 basePosition, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            var methodInfo = AccessTools.Method(typeof(HandAnimationUtility), handAnimationData.motion, null, null);
 | 
			
		||||
 | 
			
		||||
            if (methodInfo == null)
 | 
			
		||||
            {
 | 
			
		||||
                Debug.LogWarning("Hand animation motion '" + handAnimationData.motion + "' was not found");
 | 
			
		||||
                return default; 
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Vector3 handPosition = (Vector3)methodInfo.Invoke(null, new object[] { pawn, handAnimationData, baseAngle });
 | 
			
		||||
  
 | 
			
		||||
            Vector3 handPosition = handAnimationData.Motion.GetHandPosition(pawn, handAnimationData, baseAngle);
 | 
			
		||||
            return handPosition * pawn.RaceProps.baseBodySize + basePosition + new Vector3(0f, 0.3f, 0f);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static float GetGenitalSize(Pawn pawn, string genitalName)
 | 
			
		||||
        {
 | 
			
		||||
            switch(genitalName.ToLower())
 | 
			
		||||
            CompPawnSexData data = pawn?.TryGetComp<CompPawnSexData>();
 | 
			
		||||
            if (data == null) return 0f;
 | 
			
		||||
 | 
			
		||||
            switch (genitalName)
 | 
			
		||||
            {
 | 
			
		||||
                case "penis": return pawn.health.hediffSet.hediffs.First(x => x.def.defName.ToLower().Contains("penis")).Severity;
 | 
			
		||||
                case "breasts": return pawn.health.hediffSet.hediffs.First(x => x.def.defName.ToLower().Contains("breasts")).Severity;
 | 
			
		||||
                case "vagina": return 0.1f;
 | 
			
		||||
                case "anus": return 0.1f;
 | 
			
		||||
                case "penis": return data.sizeOfPenis;
 | 
			
		||||
                case "breasts": return data.sizeOfBreasts;
 | 
			
		||||
                case "Penis": return data.sizeOfPenis;
 | 
			
		||||
                case "Breasts": return data.sizeOfBreasts;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return 0.1f;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Vector3 Motion_StrokeGenitalsUpAndDownShort_FacingNS(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float p = (Mathf.PingPong(data.stageTicks, handAnimationData.cycleTime) / handAnimationData.cycleTime);
 | 
			
		||||
            float length = 0.035f;
 | 
			
		||||
 | 
			
		||||
            handPosition.x = 0;
 | 
			
		||||
            handPosition.z = length * p;
 | 
			
		||||
 | 
			
		||||
            handPosition = handPosition.RotatedBy(baseAngle);
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Vector3 Motion_StrokeGenitalsUpAndDown_FacingNS(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float p = (Mathf.PingPong(data.stageTicks, handAnimationData.cycleTime) / handAnimationData.cycleTime);
 | 
			
		||||
            float size = GetGenitalSize(pawn, handAnimationData.targetBodyPart) * 0.2f;
 | 
			
		||||
            float m = (data.actorFacing == Rot4.North ? -1f : 1f) * (handAnimationData.mirror ? -1f : 1f) * (pawn.TryGetComp<CompBodyAnimator>().Mirror ? -1f : 1f);
 | 
			
		||||
 | 
			
		||||
            handPosition.x = 0.025f * m;
 | 
			
		||||
            handPosition.z = size * p;
 | 
			
		||||
 | 
			
		||||
            handPosition = handPosition.RotatedBy(baseAngle);
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Vector3 Motion_StrokeGenitalsUpAndDown_FacingEW(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float p = Mathf.PingPong(data.stageTicks, handAnimationData.cycleTime) / handAnimationData.cycleTime;
 | 
			
		||||
            float size = GetGenitalSize(pawn, handAnimationData.targetBodyPart) * 0.2f;
 | 
			
		||||
            float m = (data.actorFacing == Rot4.West ? -1f : 1f) * (handAnimationData.mirror ? -1f : 1f);
 | 
			
		||||
 | 
			
		||||
            handPosition.x = Mathf.Sin(m * (baseAngle + 45f) / 180f * Mathf.PI) * size * p;
 | 
			
		||||
            handPosition.z = Mathf.Cos(m * (baseAngle + 45f) / 180f * Mathf.PI) * size * p;
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Vector3 Motion_RubGenitals_FacingNS(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float a = ((float)data.stageTicks % (float)handAnimationData.cycleTime) / (float)handAnimationData.cycleTime * 360f;
 | 
			
		||||
            float m = (data.actorFacing == Rot4.North ? 1f : -1f) * (handAnimationData.mirror ? -1f : 1f) * (pawn.TryGetComp<CompBodyAnimator>().Mirror ? -1f : 1f);
 | 
			
		||||
 | 
			
		||||
            handPosition.x = (Mathf.Sin(a / 180f * Mathf.PI) * 0.05f - 0.025f) * m;
 | 
			
		||||
            handPosition.z = Mathf.Cos(a / 180f * Mathf.PI) * 0.015f + 0.03f;
 | 
			
		||||
 | 
			
		||||
            handPosition = handPosition.RotatedBy(baseAngle);
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Vector3 Motion_RubGenitals_FacingEW(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float a = ((float)data.stageTicks % (float)handAnimationData.cycleTime) / (float)handAnimationData.cycleTime * 360f;
 | 
			
		||||
            float m = (data.actorFacing == Rot4.West ? 1f : -1f) * (handAnimationData.mirror ? -1f : 1f);
 | 
			
		||||
 | 
			
		||||
            handPosition.x = (Mathf.Sin(a / 180f * Mathf.PI) * 0.005f - 0.05f) * m;
 | 
			
		||||
            handPosition.z = Mathf.Cos(a / 180f * Mathf.PI) * 0.015f;
 | 
			
		||||
            //handPosition.y = -0.1f;
 | 
			
		||||
 | 
			
		||||
            handPosition = handPosition.RotatedBy(baseAngle);
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Vector3 Motion_RubBreasts_FacingNS(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float a = ((float)data.stageTicks % (float)handAnimationData.cycleTime) / (float)handAnimationData.cycleTime * 360f;
 | 
			
		||||
            float m = (data.actorFacing == Rot4.North ? -1f : 1f) * (handAnimationData.mirror ? -1f : 1f) * (pawn.TryGetComp<CompBodyAnimator>().Mirror ? -1f : 1f);
 | 
			
		||||
            float size = GetGenitalSize(pawn, "breasts");
 | 
			
		||||
 | 
			
		||||
            handPosition.x = (Mathf.Sin(a / 180f * Mathf.PI) * 0.05f * size - size * 0.25f) * m;
 | 
			
		||||
            handPosition.z = Mathf.Cos(a / 180f * Mathf.PI) * 0.015f - size * 0.125f;
 | 
			
		||||
 | 
			
		||||
            handPosition = handPosition.RotatedBy(baseAngle);
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Vector3 Motion_RubBreasts_FacingEW(Pawn pawn, HandAnimationData handAnimationData, float baseAngle)
 | 
			
		||||
        {
 | 
			
		||||
            Vector3 handPosition = new Vector3();
 | 
			
		||||
            ActorAnimationData data = pawn.GetAnimationData();
 | 
			
		||||
 | 
			
		||||
            float a = ((float)data.stageTicks % (float)handAnimationData.cycleTime) / (float)handAnimationData.cycleTime * 360f;
 | 
			
		||||
            float m = (data.actorFacing == Rot4.West ? 1f : -1f) * (handAnimationData.mirror ? -1f : 1f);
 | 
			
		||||
            float size = GetGenitalSize(pawn, "breasts");
 | 
			
		||||
 | 
			
		||||
            handPosition.x = (Mathf.Sin(a / 180f * Mathf.PI) * 0.005f - size * 0.25f) * m;
 | 
			
		||||
            handPosition.z = Mathf.Cos(a / 180f * Mathf.PI) * 0.015f - size * 0.125f;
 | 
			
		||||
 | 
			
		||||
            handPosition = handPosition.RotatedBy(baseAngle);
 | 
			
		||||
 | 
			
		||||
            return handPosition;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static Graphic GetHandGraphic(Pawn touchingPawn)
 | 
			
		||||
        {
 | 
			
		||||
            CompPawnSexData comp = touchingPawn?.TryGetComp<CompPawnSexData>();
 | 
			
		||||
| 
						 | 
				
			
			@ -198,9 +71,9 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
            return comp.handGraphic;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static bool TryToDrawHand(Pawn pawn, string bodyAddonName, Vector3 bodyAddonPosition, float bodyAddonAngle, Rot4 bodyAddonRotation, PawnRenderFlags renderFlags)
 | 
			
		||||
        public static bool TryToDrawHand(Pawn pawn, ActorAnimationData pawnAnimationData, string bodyAddonName, Vector3 bodyAddonPosition, float bodyAddonAngle, Rot4 bodyAddonRotation, PawnRenderFlags renderFlags)
 | 
			
		||||
        {   
 | 
			
		||||
            if (BodyPartIsBeingTouched(pawn, bodyAddonName, out List<HandAnimationData> handAnimationData))
 | 
			
		||||
            if (BodyPartIsBeingTouched(pawn, pawnAnimationData, bodyAddonName, out List<HandAnimationData> handAnimationData))
 | 
			
		||||
            {       
 | 
			
		||||
                foreach (HandAnimationData datum in handAnimationData)
 | 
			
		||||
                {        
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,14 +33,19 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
            {
 | 
			
		||||
                for (int radius = 1; radius < maxRadius; radius++)
 | 
			
		||||
                {
 | 
			
		||||
                    List<IntVec3> cells = GenRadial.RadialCellsAround(pawn.Position, radius + 0.75f, false).Where(x => x.Standable(pawn.Map) && x.GetRoom(pawn.Map) == pawn.GetRoom())?.ToList();
 | 
			
		||||
                    IEnumerable<IntVec3> cells = GenRadial.RadialCellsAround(pawn.Position, radius + 0.75f, false).Where(x => x.Standable(pawn.Map) && x.GetRoom(pawn.Map) == pawn.GetRoom());
 | 
			
		||||
 | 
			
		||||
                    if (cells.NullOrEmpty() == false && cells.Count > 0)
 | 
			
		||||
                    if (cells.Any() == true && cells.Count() > 0)
 | 
			
		||||
                    { return cells.RandomElement(); }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return GenAdj.RandomAdjacentCellCardinal(pawn);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public static float ClampAngle(float angle)
 | 
			
		||||
        {
 | 
			
		||||
            return angle < 0f ? 360f - (angle % 360) : angle % 360f;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,5 +16,8 @@ namespace Rimworld_Animations_Patch
 | 
			
		|||
    public static class PatchBodyPartDefOf
 | 
			
		||||
    {
 | 
			
		||||
        public static BodyPartDef Hand;
 | 
			
		||||
        public static BodyPartDef Genitals;
 | 
			
		||||
        public static BodyPartDef Anus;
 | 
			
		||||
        public static BodyPartDef Chest;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,266 +0,0 @@
 | 
			
		|||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using Verse;
 | 
			
		||||
using Verse.AI;
 | 
			
		||||
using Verse.AI.Group;
 | 
			
		||||
using RimWorld;
 | 
			
		||||
using rjw;
 | 
			
		||||
using RJWSexperience.Ideology;
 | 
			
		||||
using UnityEngine;
 | 
			
		||||
 | 
			
		||||
namespace Rimworld_Animations_Patch
 | 
			
		||||
{
 | 
			
		||||
    public static class SexInteractionUtility
 | 
			
		||||
    {
 | 
			
		||||
		public static bool PawnCaughtLovinByWitness(Pawn pawn, Pawn witness)
 | 
			
		||||
		{
 | 
			
		||||
			if (witness == null || pawn == witness || witness.AnimalOrWildMan() || witness.RaceProps.IsMechanoid || witness.Awake() == false || witness.CanSee(pawn) == false)
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			if (pawn.IsHavingSex() == false && pawn.IsMasturbating() == false)
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			List<Pawn> sexParticipants = pawn.GetAllSexParticipants();
 | 
			
		||||
			bool witnessIsCourtingSexParticipant = witness.jobs.curDriver is JobDriver_SexBaseInitiator && sexParticipants.Contains((witness.jobs.curDriver as JobDriver_SexBaseInitiator).Partner);
 | 
			
		||||
 | 
			
		||||
			if (sexParticipants.Contains(witness) || witnessIsCourtingSexParticipant)
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool PawnIsCheatingOnPartner(Pawn pawn, Pawn partner)
 | 
			
		||||
		{
 | 
			
		||||
			if (BasicSettings.worryAboutInfidelity == false || pawn.IsMasturbating() || pawn.IsHavingSex() == false || pawn.GetAllSexParticipants().Contains(partner))
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			if (pawn.GetAllSexParticipants().Any(x => pawn.GetSpouseCount(false) > 0 && pawn.GetSpouses(false).Contains(x)))
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			if (pawn.GetSexPartner().Dead || pawn.GetSexPartner().IsAnimal())
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			return partner.IsLoverOfOther(pawn) && pawn.HasTrait("Polygamist") == false && partner.HasTrait("Polygamist") == false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool InvitePasserbyForSex(Pawn passerby, List<Pawn> participants)
 | 
			
		||||
		{
 | 
			
		||||
			if (passerby == null || participants.NullOrEmpty() || participants.Contains(passerby) || passerby.AnimalOrWildMan() || passerby.RaceProps.IsMechanoid || passerby.Awake() == false || participants.All(x => x.CanSee(passerby) == false))
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			if (participants.Any(x => x.IsForbidden(passerby) || x.HostileTo(passerby) || PawnIsCheatingOnPartner(x, passerby)) || CasualSex_Helper.CanHaveSex(passerby) == false || xxx.IsTargetPawnOkay(passerby) == false || participants.Count > 2)
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			if (passerby.MentalState != null ||
 | 
			
		||||
				passerby.jobs.curDriver is JobDriver_Flee ||
 | 
			
		||||
				passerby.jobs.curDriver is JobDriver_AttackMelee ||
 | 
			
		||||
				passerby.jobs.curDriver is JobDriver_Vomit)
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			if (SexUtility.ReadyForHookup(passerby) &&
 | 
			
		||||
				(passerby?.jobs?.curJob == null || (passerby.jobs.curJob.playerForced == false && CasualSex_Helper.quickieAllowedJobs.Contains(passerby.jobs.curJob.def))) &&
 | 
			
		||||
				participants.Any(x => SexAppraiser.would_fuck(x, passerby) > 0.1f && SexAppraiser.would_fuck(passerby, x) > 0.1f) &&
 | 
			
		||||
				participants.All(x => SexAppraiser.would_fuck(x, passerby, false, false, true) > 0.1f && SexAppraiser.would_fuck(passerby, x, false, false, true) > 0.1f))
 | 
			
		||||
			{
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static ThoughtDef GetThoughtsAboutSexAct(Pawn pawn, JobDriver_Sex jobDriver, out Precept precept)
 | 
			
		||||
		{
 | 
			
		||||
			ThoughtDef thoughtDef = null;
 | 
			
		||||
			precept = null;
 | 
			
		||||
 | 
			
		||||
			if (pawn == null || jobDriver == null)
 | 
			
		||||
			{ return null; }
 | 
			
		||||
 | 
			
		||||
			bool sexIsNecro = jobDriver.Partner != null && jobDriver.Partner.Dead;
 | 
			
		||||
			bool sexIsBeastial = jobDriver.Partner != null && jobDriver.Partner.RaceProps.Animal;
 | 
			
		||||
			bool sexIsRape = sexIsBeastial == false && sexIsNecro == false &&
 | 
			
		||||
				(jobDriver is JobDriver_Rape || jobDriver is JobDriver_RapeEnemy || jobDriver is JobDriver_SexBaseRecieverRaped);	
 | 
			
		||||
			bool sexIsSlaveRape = sexIsRape && (jobDriver.Partner.IsPrisoner || jobDriver.Partner.IsSlave);
 | 
			
		||||
			bool sexIsXeno = jobDriver.Partner != null && jobDriver.Partner.def.defName != jobDriver.pawn.def.defName;
 | 
			
		||||
			bool isXenophobe = pawn.HasTrait("Xenophobia") && pawn.story.traits.DegreeOfTrait(DefDatabase<TraitDef>.GetNamedSilentFail("Xenophobia")) > 0;
 | 
			
		||||
			bool isXenophile = pawn.HasTrait("Xenophobia") && pawn.story.traits.DegreeOfTrait(DefDatabase<TraitDef>.GetNamedSilentFail("Xenophobia")) < 0;
 | 
			
		||||
 | 
			
		||||
			if (BasicSettings.worryAboutNecro && sexIsNecro && xxx.is_necrophiliac(pawn) == false)
 | 
			
		||||
			{
 | 
			
		||||
				thoughtDef = xxx.is_necrophiliac(pawn) ? DefDatabase<ThoughtDef>.GetNamedSilentFail("SawNecrophilia_Honorable") :
 | 
			
		||||
					pawn.HasPreceptForIssue("Necrophilia", out precept) ? DefDatabase<ThoughtDef>.GetNamedSilentFail("Saw" + precept.def.defName) :
 | 
			
		||||
					DefDatabase<ThoughtDef>.GetNamedSilentFail("SawNecrophilia_Abhorrent");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			else if (BasicSettings.worryAboutBeastiality && sexIsBeastial)
 | 
			
		||||
			{
 | 
			
		||||
				thoughtDef = xxx.is_zoophile(pawn) ? DefDatabase<ThoughtDef>.GetNamedSilentFail("SawBeastility_Honorable") :
 | 
			
		||||
					pawn.HasPreceptForIssue("Beastility", out precept) ? DefDatabase<ThoughtDef>.GetNamedSilentFail("Saw" + precept.def.defName) :
 | 
			
		||||
					DefDatabase<ThoughtDef>.GetNamedSilentFail("SawBeastility_Abhorrent");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			else if (BasicSettings.worryAboutRape && BasicSettings.ignoreSlaveRape == false && sexIsSlaveRape)
 | 
			
		||||
			{
 | 
			
		||||
				thoughtDef = xxx.is_rapist(pawn) ? DefDatabase<ThoughtDef>.GetNamedSilentFail("SawRape_Honorable") :
 | 
			
		||||
					pawn.HasPreceptForIssue("Rape", out precept) ? DefDatabase<ThoughtDef>.GetNamedSilentFail("Saw" + precept.def.defName) :
 | 
			
		||||
					DefDatabase<ThoughtDef>.GetNamedSilentFail("SawRape_Abhorrent");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			else if (BasicSettings.worryAboutRape && sexIsRape)
 | 
			
		||||
			{
 | 
			
		||||
				thoughtDef = xxx.is_rapist(pawn) ? DefDatabase<ThoughtDef>.GetNamedSilentFail("SawRape_Honorable") :
 | 
			
		||||
					pawn.HasPreceptForIssue("Rape", out precept) ? DefDatabase<ThoughtDef>.GetNamedSilentFail("Saw" + precept.def.defName) :
 | 
			
		||||
					DefDatabase<ThoughtDef>.GetNamedSilentFail("SawRape_Abhorrent");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			else if (BasicSettings.worryAboutXeno && sexIsXeno)
 | 
			
		||||
			{
 | 
			
		||||
				thoughtDef = isXenophobe ? DefDatabase<ThoughtDef>.GetNamedSilentFail("SawHAR_AlienDating_Prohibited") :
 | 
			
		||||
					isXenophile ? DefDatabase<ThoughtDef>.GetNamedSilentFail("SawHAR_AlienDating_Honorable") :
 | 
			
		||||
					pawn.HasPreceptForIssue("HAR_AlienDating", out precept) ? DefDatabase<ThoughtDef>.GetNamedSilentFail("Saw" + precept.def.defName) :
 | 
			
		||||
					DefDatabase<ThoughtDef>.GetNamedSilentFail("SawHAR_AlienDating_Acceptable"); 
 | 
			
		||||
			}
 | 
			
		||||
	
 | 
			
		||||
			//DebugMode.Message("Sex job is: " + jobDriver + " Issue is: " + (precept?.def?.issue?.defName).ToStringSafe() + " Opinion is: " + (precept?.def?.defName).ToStringSafe() + " Thought is: " + (thoughtDef?.defName).ToStringSafe());
 | 
			
		||||
 | 
			
		||||
			return thoughtDef;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static void TriggerReactionInWitness(Pawn witness, Pawn otherPawn, string reaction)
 | 
			
		||||
		{
 | 
			
		||||
			if (BasicSettings.majorTabooCanStartFights == false || reaction.NullOrEmpty())
 | 
			
		||||
			{ return; }
 | 
			
		||||
 | 
			
		||||
			if (witness.MentalState != null ||
 | 
			
		||||
				witness.jobs.curDriver is JobDriver_Flee ||
 | 
			
		||||
				witness.jobs.curDriver is JobDriver_AttackMelee ||
 | 
			
		||||
				witness.jobs.curDriver is JobDriver_Vomit)
 | 
			
		||||
			{ return; }
 | 
			
		||||
 | 
			
		||||
			// Panicked
 | 
			
		||||
			if (reaction == "Panicked" || (reaction == "Indignant" && Random.value <= 0.5f))
 | 
			
		||||
			{
 | 
			
		||||
				// Fight
 | 
			
		||||
				if (otherPawn.RaceProps.Humanlike && witness.RaceProps.Humanlike && witness.DislikesViolence() == false && (Random.value <= 0.2f || witness.EnjoysViolence()) && witness.HostileTo(otherPawn) == false && InteractionUtility.TryGetRandomVerbForSocialFight(witness, out Verb verbToUse))
 | 
			
		||||
				{ witness.interactions.StartSocialFight(otherPawn, "MessageSocialFight"); }
 | 
			
		||||
 | 
			
		||||
				// Flight
 | 
			
		||||
				else
 | 
			
		||||
				{
 | 
			
		||||
					Job job = JobMaker.MakeJob(JobDefOf.FleeAndCower, CellFinderLoose.GetFleeDest(witness, new List<Thing>() { otherPawn }, 24f), otherPawn);
 | 
			
		||||
					witness.jobs.EndCurrentJob(JobCondition.InterruptForced, false, false);
 | 
			
		||||
					witness.jobs.StartJob(job);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Vomit
 | 
			
		||||
			else if (reaction == "Nauseated")
 | 
			
		||||
			{
 | 
			
		||||
				Job jobVomit = JobMaker.MakeJob(JobDefOf.Vomit);
 | 
			
		||||
				Job jobFlee = JobMaker.MakeJob(JobDefOf.FleeAndCower, CellFinderLoose.GetFleeDest(witness, new List<Thing>() { otherPawn }, 24f), otherPawn);
 | 
			
		||||
 | 
			
		||||
				witness.jobs.EndCurrentJob(JobCondition.InterruptForced, false, false);
 | 
			
		||||
 | 
			
		||||
				if (Random.value <= 0.2f)
 | 
			
		||||
				{
 | 
			
		||||
					witness.jobs.StartJob(jobVomit);
 | 
			
		||||
					witness.jobs.jobQueue.EnqueueFirst(jobFlee);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				else
 | 
			
		||||
				{ witness.jobs.StartJob(jobFlee); }
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			// Indignant 
 | 
			
		||||
			else if (reaction == "Indignant")
 | 
			
		||||
			{
 | 
			
		||||
				witness.mindState.mentalStateHandler.TryStartMentalState(DefDatabase<MentalStateDef>.GetNamedSilentFail("TargetedInsultingSpree"), null, true, false, null, true, false, false);
 | 
			
		||||
				(witness.mindState.mentalStateHandler.CurState as MentalState_TargetedInsultingSpree).target = otherPawn;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		public static bool ResolveThoughtsForWhenSexIsWitnessed(Pawn pawn, Pawn witness, out bool witnessJoiningSex)
 | 
			
		||||
		{
 | 
			
		||||
			witnessJoiningSex = Random.value < BasicSettings.chanceForOtherToJoinInSex && InvitePasserbyForSex(witness, pawn.GetAllSexParticipants());
 | 
			
		||||
 | 
			
		||||
			// Exit clauses
 | 
			
		||||
			if (witness.AnimalOrWildMan() || witness.RaceProps.IsMechanoid || witness.Dead || witness.Faction == null || witness.Faction.HostileTo(Faction.OfPlayer))
 | 
			
		||||
			{ return false; }
 | 
			
		||||
 | 
			
		||||
			// Get basic thoughts
 | 
			
		||||
			string pawnThoughtDefName = pawn.IsMasturbating() ? "SeenMasturbating" : "SeenHavingSex";
 | 
			
		||||
			string witnessThoughtDefName = pawn.IsMasturbating() ? "SawMasturbation" : "SawSex";
 | 
			
		||||
 | 
			
		||||
			ThoughtDef pawnThoughtDef = BasicSettings.needPrivacy ? DefDatabase<ThoughtDef>.GetNamedSilentFail(pawnThoughtDefName) : null;
 | 
			
		||||
			ThoughtDef witnessThoughtDef = BasicSettings.needPrivacy ? DefDatabase<ThoughtDef>.GetNamedSilentFail(witnessThoughtDefName) : null;
 | 
			
		||||
 | 
			
		||||
			// Exhibitionist pawn
 | 
			
		||||
			if (xxx.has_quirk(pawn, "Exhibitionist"))
 | 
			
		||||
			{ pawnThoughtDef = DefDatabase<ThoughtDef>.GetNamedSilentFail(pawnThoughtDefName + "Exhibitionist"); }
 | 
			
		||||
 | 
			
		||||
			// Voyeuristic witness
 | 
			
		||||
			if (xxx.has_quirk(witness, "Voyeur"))
 | 
			
		||||
			{ witnessThoughtDef = DefDatabase<ThoughtDef>.GetNamedSilentFail(witnessThoughtDefName + "Voyeur"); }
 | 
			
		||||
 | 
			
		||||
			// Mediating cirumstances
 | 
			
		||||
			bool sexIsRitual = pawn.GetLord() != null && pawn.GetLord().LordJob is LordJob_Ritual && witness?.Ideo == pawn?.Ideo;
 | 
			
		||||
			bool sexIsParty = pawn.GetLord() != null && pawn.GetLord().LordJob is LordJob_Joinable_Party;
 | 
			
		||||
			bool pawnIsVictim = pawn.CurJob.def == xxx.gettin_raped || pawn.Dead;
 | 
			
		||||
			bool pawnIsCheating = sexIsRitual == false && sexIsParty == false && pawnIsVictim == false && PawnIsCheatingOnPartner(pawn, witness);
 | 
			
		||||
 | 
			
		||||
			// Wipe thoughts if pawn is a victim
 | 
			
		||||
			if (pawnIsVictim)
 | 
			
		||||
			{
 | 
			
		||||
				pawnThoughtDef = null;
 | 
			
		||||
				witnessThoughtDef = null;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Add thought if pawn is cheating
 | 
			
		||||
			if (pawnIsCheating)
 | 
			
		||||
			{
 | 
			
		||||
				if (pawn.needs.mood.thoughts.memories.GetFirstMemoryOfDef(DefDatabase<ThoughtDef>.GetNamedSilentFail("CaughtCheating")) == null)
 | 
			
		||||
				{ pawn.needs.mood.thoughts.memories.TryGainMemory(DefDatabase<ThoughtDef>.GetNamedSilentFail("CaughtCheating"), witness); }
 | 
			
		||||
 | 
			
		||||
				if (witness.needs.mood.thoughts.memories.GetFirstMemoryOfDef(ThoughtDefOf.CheatedOnMe) == null)
 | 
			
		||||
				{ witness.needs.mood.thoughts.memories.TryGainMemory(ThoughtDefOf.CheatedOnMe, pawn); }
 | 
			
		||||
 | 
			
		||||
				witnessJoiningSex = false;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Determine if there are any issues with the sex event and the witness' morals
 | 
			
		||||
			Precept precept = null;
 | 
			
		||||
 | 
			
		||||
			if (sexIsRitual == false && sexIsParty == false && pawnIsVictim == false)
 | 
			
		||||
			{ witnessThoughtDef = GetThoughtsAboutSexAct(witness, pawn.jobs.curDriver as JobDriver_Sex, out precept); }
 | 
			
		||||
 | 
			
		||||
			// Apply thoughts to witness
 | 
			
		||||
			if (witnessThoughtDef != null)
 | 
			
		||||
			{
 | 
			
		||||
				witness.needs.mood.thoughts.memories.TryGainMemory(witnessThoughtDef, pawn, precept);
 | 
			
		||||
 | 
			
		||||
				if (witnessThoughtDef.stages[0].baseMoodEffect < 0)
 | 
			
		||||
				{ witness?.TryGetComp<CompPawnSexData>()?.TryToExclaim(); }
 | 
			
		||||
 | 
			
		||||
				witnessJoiningSex = witnessThoughtDef.hediff != null ? false : witnessJoiningSex;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Extreme reaction
 | 
			
		||||
			if (witnessThoughtDef?.hediff != null)
 | 
			
		||||
			{ TriggerReactionInWitness(witness, pawn, witnessThoughtDef.hediff.defName); }
 | 
			
		||||
 | 
			
		||||
			else if (pawnIsCheating)
 | 
			
		||||
			{ TriggerReactionInWitness(witness, pawn, "Indignant"); }
 | 
			
		||||
 | 
			
		||||
			// Apply thoughts to pawn
 | 
			
		||||
			if (pawnThoughtDef != null)
 | 
			
		||||
			{
 | 
			
		||||
				pawn.needs.mood.thoughts.memories.TryGainMemory(pawnThoughtDef, witness, null);
 | 
			
		||||
 | 
			
		||||
				if (pawnThoughtDef.stages[0].baseMoodEffect < 0)
 | 
			
		||||
				{ pawn?.TryGetComp<CompPawnSexData>()?.TryToExclaim(); }
 | 
			
		||||
			}
 | 
			
		||||
			
 | 
			
		||||
			return witnessJoiningSex;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -1 +1 @@
 | 
			
		|||
80ac53cd1937a7f3bc413d1816a7df030fc583c6
 | 
			
		||||
0035486d66161d603d82d58abd89b02b042a2e0c
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,3 +4,5 @@ C:\Program Files (x86)\Steam\SteamApps\common\RimWorld\Mods\rimworld-animations-
 | 
			
		|||
C:\Program Files (x86)\Steam\SteamApps\common\RimWorld\Mods\rimworld-animations-patch-abscon\Source\obj\Debug\Rimworld-Animations-Patch.dll
 | 
			
		||||
C:\Program Files (x86)\Steam\SteamApps\common\RimWorld\Mods\rimworld-animations-patch-abscon\Source\obj\Debug\Rimworld-Animations-Patch.pdb
 | 
			
		||||
C:\Program Files (x86)\Steam\SteamApps\common\RimWorld\Mods\rimworld-animations-patch-abscon\Source\obj\Debug\Rimworld-Animations-Patch.csprojAssemblyReference.cache
 | 
			
		||||
C:\Program Files (x86)\Steam\SteamApps\common\RimWorld\Mods\rimworld-animations-patch-abscon\1.4\Assemblies\Rimworld-Animations-Patch.dll
 | 
			
		||||
C:\Program Files (x86)\Steam\SteamApps\common\RimWorld\Mods\rimworld-animations-patch-abscon\1.4\Assemblies\Rimworld-Animations-Patch.pdb
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue