This commit is contained in:
AbstractConcept 2023-02-04 01:13:57 -06:00
parent fcf187c7dd
commit 38ec4f86c1
68 changed files with 846 additions and 1934 deletions

View file

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

View file

@ -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;

View file

@ -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);
}
}
}
}

View 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;
}
}
}

View 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;
}
}

View file

@ -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;
}
}
}

View 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;
}
}
}

View file

@ -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; }
}

View file

@ -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;
}
}
}
}

View 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;
}
}
}
}
}

View file

@ -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);
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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);
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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 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;
}
}
}

View file

@ -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;
}
}
}

View 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 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;
}
}
}

View file

@ -29,7 +29,7 @@ namespace Rimworld_Animations_Patch
}))();
}
catch (TypeLoadException ex) { }
catch (TypeLoadException) { }
}
public static bool Prefix_ShouldNotDrawAddonsForPawn(ref bool __result, Pawn pawn)

View file

@ -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;

View file

@ -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); }
}
}
}

View file

@ -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)

View file

@ -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;
}

View file

@ -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; }
}

View file

@ -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;
}
}
}*/
}

View file

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

View file

@ -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;
}
}
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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;
}
}
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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)
{

View file

@ -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;
}
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}
}

View file

@ -1 +1 @@
80ac53cd1937a7f3bc413d1816a7df030fc583c6
0035486d66161d603d82d58abd89b02b042a2e0c

View file

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