This commit is contained in:
Platinum 2020-04-08 17:43:01 -07:00
parent 74a34b6f8d
commit 55ae1c5d10
29 changed files with 1387 additions and 28 deletions

View File

@ -62,33 +62,33 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Source\Rimworld-Animations\Actors\Actor.cs" />
<Compile Include="Source\Rimworld-Animations\Animations\AnimationStage.cs" />
<Compile Include="Source\Rimworld-Animations\Animations\Clips\BaseAnimationClip.cs" />
<Compile Include="Source\Rimworld-Animations\Animations\Clips\PawnAnimationClip.cs" />
<Compile Include="Source\Rimworld-Animations\Animations\Clips\ThingAnimationClip.cs" />
<Compile Include="Source\Rimworld-Animations\Animations\Keyframes\Keyframe.cs" />
<Compile Include="Source\Rimworld-Animations\Animations\Keyframes\PawnKeyframe.cs" />
<Compile Include="Source\Rimworld-Animations\Animations\Keyframes\ThingKeyframe.cs" />
<Compile Include="Source\Rimworld-Animations\AnimationUtility.cs" />
<Compile Include="Source\Rimworld-Animations\Comps\CompBodyAnimator.cs" />
<Compile Include="Source\Rimworld-Animations\Comps\CompProperties_BodyAnimator.cs" />
<Compile Include="Source\Rimworld-Animations\Comps\CompProperties_ThingAnimator.cs" />
<Compile Include="Source\Rimworld-Animations\Comps\CompThingAnimator.cs" />
<Compile Include="Source\Rimworld-Animations\Defs\AnimationDef.cs" />
<Compile Include="Source\Rimworld-Animations\JobDrivers\JobDriver_SexBaseRecieverLovedForAnimation.cs" />
<Compile Include="Source\Rimworld-Animations\JobDrivers\JobDriver_SexCasualForAnimation.cs" />
<Compile Include="Source\Rimworld-Animations\MainTabWindows\MainTabWindow_SexAnimator.cs" />
<Compile Include="Source\Rimworld-Animations\Patches\HarmonyPatch_AlienRace.cs" />
<Compile Include="Source\Rimworld-Animations\Patches\HarmonyPatch_FacialAnimation.cs" />
<Compile Include="Source\Rimworld-Animations\Patches\HarmonyPatch_PawnRenderer.cs" />
<Compile Include="Source\Rimworld-Animations\Patches\HarmonyPatch_Pawn_DrawTracker.cs" />
<Compile Include="Source\Rimworld-Animations\Patches\HarmonyPatch_ShowHairWithHats.cs" />
<Compile Include="Source\Rimworld-Animations\Patches\Harmony_PatchAll.cs" />
<Compile Include="Source\Rimworld-Animations\Patches\rjwPatches\HarmonyPatch_JobDriver_SexBaseInitiator.cs" />
<Compile Include="Source\Rimworld-Animations\Patches\rjwPatches\HarmonyPatch_JoinInBedGiveJob.cs" />
<Compile Include="Source\Rimworld-Animations\Patches\rjwPatches\HarmonyPatch_SexTick.cs" />
<Compile Include="Source\Rimworld-Animations\Patches\rjwPatches\HarmonyPatch_WorkGiverSex.cs" />
<Compile Include="Source\Actors\Actor.cs" />
<Compile Include="Source\Animations\AnimationStage.cs" />
<Compile Include="Source\Animations\Clips\BaseAnimationClip.cs" />
<Compile Include="Source\Animations\Clips\PawnAnimationClip.cs" />
<Compile Include="Source\Animations\Clips\ThingAnimationClip.cs" />
<Compile Include="Source\Animations\Keyframes\Keyframe.cs" />
<Compile Include="Source\Animations\Keyframes\PawnKeyframe.cs" />
<Compile Include="Source\Animations\Keyframes\ThingKeyframe.cs" />
<Compile Include="Source\AnimationUtility.cs" />
<Compile Include="Source\Comps\CompBodyAnimator.cs" />
<Compile Include="Source\Comps\CompProperties_BodyAnimator.cs" />
<Compile Include="Source\Comps\CompProperties_ThingAnimator.cs" />
<Compile Include="Source\Comps\CompThingAnimator.cs" />
<Compile Include="Source\Defs\AnimationDef.cs" />
<Compile Include="Source\JobDrivers\JobDriver_SexBaseRecieverLovedForAnimation.cs" />
<Compile Include="Source\JobDrivers\JobDriver_SexCasualForAnimation.cs" />
<Compile Include="Source\MainTabWindows\MainTabWindow_SexAnimator.cs" />
<Compile Include="Source\Patches\HarmonyPatch_AlienRace.cs" />
<Compile Include="Source\Patches\HarmonyPatch_FacialAnimation.cs" />
<Compile Include="Source\Patches\HarmonyPatch_PawnRenderer.cs" />
<Compile Include="Source\Patches\HarmonyPatch_Pawn_DrawTracker.cs" />
<Compile Include="Source\Patches\HarmonyPatch_ShowHairWithHats.cs" />
<Compile Include="Source\Patches\Harmony_PatchAll.cs" />
<Compile Include="Source\Patches\rjwPatches\HarmonyPatch_JobDriver_SexBaseInitiator.cs" />
<Compile Include="Source\Patches\rjwPatches\HarmonyPatch_JoinInBedGiveJob.cs" />
<Compile Include="Source\Patches\rjwPatches\HarmonyPatch_SexTick.cs" />
<Compile Include="Source\Patches\rjwPatches\HarmonyPatch_WorkGiverSex.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

18
Source/Actors/Actor.cs Normal file
View File

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace Rimworld_Animations {
public class Actor {
public List<string> defNames;
public List<string> requiredGenitals;
public bool activeRole = false;
public string gender;
public bool isFucking = false;
public bool isFucked = false;
public Vector3 offset = new Vector3(0, 0, 0);
}
}

114
Source/AnimationUtility.cs Normal file
View File

@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.AI;
namespace Rimworld_Animations {
public static class AnimationUtility {
/*
Note: always make the list in this order:
Female pawns, animal female pawns, male pawns, animal male pawns
*/
public static AnimationDef tryFindAnimation(ref List<Pawn> participants) {
//aggressors first
participants = participants.OrderByDescending(p => p.jobs.curDriver is rjw.JobDriver_SexBaseInitiator).ToList();
//fucked first, fucking second
participants = participants.OrderBy(p => rjw.xxx.can_fuck(p)).ToList();
List<Pawn> localParticipants = new List<Pawn>(participants);
IEnumerable<AnimationDef> options = DefDatabase<AnimationDef>.AllDefs.Where((AnimationDef x) => {
if (x.actors.Count != localParticipants.Count) {
return false;
}
for (int i = 0; i < x.actors.Count; i++) {
if(x.actors[i].defNames.Contains("Human")) {
if (!rjw.xxx.is_human(localParticipants[i])) {
if(rjw.RJWSettings.DevMode) {
Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is not human");
}
return false;
}
} else {
if (!x.actors[i].defNames.Contains(localParticipants[i].def.defName)) {
if (rjw.RJWSettings.DevMode) {
Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is not ");
foreach(String defname in x.actors[i].defNames) {
Log.Message(defname + ", ");
}
}
return false;
}
}
if(x.actors[i].requiredGenitals != null && x.actors[i].requiredGenitals.Contains("Vagina")) {
if (!rjw.Genital_Helper.has_vagina(localParticipants[i])) {
Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " doesn't have vagina");
return false;
}
}
//TESTING ANIMATIONS ONLY REMEMBER TO COMMENT OUT BEFORE PUSH
/*
if (x.defName != "Tribadism")
return false;
*/
if (x.actors[i].isFucking && !rjw.xxx.can_fuck(localParticipants[i])) {
Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " can't fuck");
return false;
}
if (x.actors[i].isFucked && !rjw.xxx.can_be_fucked(localParticipants[i])) {
Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " can't be fucked");
return false;
}
}
return true;
});
if (options != null && options.Any()) {
Log.Message("Randomly selecting animation...");
return options.RandomElement();
}
else
return null;
}
public static void RenderPawnHeadMeshInAnimation(Mesh mesh, Vector3 loc, Quaternion quaternion, Material material, bool portrait, Pawn pawn) {
if(pawn == null) {
GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
return;
}
CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();
if (pawnAnimator == null || !pawnAnimator.isAnimating || portrait) {
GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
} else {
Vector3 pawnHeadPosition = pawnAnimator.getPawnHeadPosition();
pawnHeadPosition.y = loc.y;
GenDraw.DrawMeshNowOrLater(mesh, pawnHeadPosition, Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up), material, portrait);
}
}
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Rimworld_Animations {
public class AnimationStage
{
public string stageName;
public int stageIndex;
public int playTimeTicks = 0;
public bool isLooping;
public List<BaseAnimationClip> animationClips;
public void initialize() {
foreach (BaseAnimationClip clip in animationClips) {
clip.buildSimpleCurves();
//select playTimeTicks as longest playtime of all the animations
if(clip.duration > playTimeTicks) {
playTimeTicks = clip.duration;
}
}
}
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;
namespace Rimworld_Animations {
public abstract class BaseAnimationClip
{
public List<ThingDef> types; //types of participants
public int duration;
public abstract void buildSimpleCurves();
public string soundDef = null; //for playing sounds
public int actor;
}
}

View File

@ -0,0 +1,101 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;
namespace Rimworld_Animations {
public class PawnAnimationClip : BaseAnimationClip {
public List<PawnKeyframe> keyframes;
public AltitudeLayer layer = AltitudeLayer.Pawn;
public Dictionary<int, string> SoundEffects = new Dictionary<int, string>();
public SimpleCurve BodyAngle = new SimpleCurve();
public SimpleCurve HeadAngle = new SimpleCurve();
public SimpleCurve HeadBob = new SimpleCurve();
public SimpleCurve BodyOffsetX = new SimpleCurve();
public SimpleCurve BodyOffsetZ = new SimpleCurve();
public SimpleCurve HeadFacing = new SimpleCurve();
public SimpleCurve BodyFacing = new SimpleCurve();
public override void buildSimpleCurves() {
int duration = 0;
//getting the length of the whole clip
foreach(PawnKeyframe frame in keyframes) {
duration += frame.tickDuration;
}
//guarantees loops don't get cut off mid-anim
this.duration = duration;
int keyframePosition = 0;
foreach (PawnKeyframe frame in keyframes) {
if (frame.atTick.HasValue) {
if (frame.bodyAngle.HasValue)
BodyAngle.Add((float)frame.atTick / (float)duration, frame.bodyAngle.Value, true);
if (frame.headAngle.HasValue)
HeadAngle.Add((float)frame.atTick / (float)duration, frame.headAngle.Value, true);
if (frame.bodyOffsetX.HasValue)
BodyOffsetX.Add((float)frame.atTick / (float)duration, frame.bodyOffsetX.Value, true);
if (frame.bodyOffsetZ.HasValue)
BodyOffsetZ.Add((float)frame.atTick / (float)duration, frame.bodyOffsetZ.Value, true);
if (frame.headFacing.HasValue)
HeadFacing.Add((float)frame.atTick / (float)duration, frame.headFacing.Value, true);
if (frame.bodyFacing.HasValue)
BodyFacing.Add((float)frame.atTick / (float)duration, frame.bodyFacing.Value, true);
if (frame.headBob.HasValue)
HeadBob.Add((float)frame.atTick / (float)duration, frame.headBob.Value, true);
if (frame.soundEffect != null) {
SoundEffects.Add((int)frame.atTick, frame.soundEffect);
}
}
else {
if (frame.bodyAngle.HasValue)
BodyAngle.Add((float)keyframePosition / (float)duration, frame.bodyAngle.Value, true);
if (frame.headAngle.HasValue)
HeadAngle.Add((float)keyframePosition / (float)duration, frame.headAngle.Value, true);
if (frame.bodyOffsetX.HasValue)
BodyOffsetX.Add((float)keyframePosition / (float)duration, frame.bodyOffsetX.Value, true);
if (frame.bodyOffsetZ.HasValue)
BodyOffsetZ.Add((float)keyframePosition / (float)duration, frame.bodyOffsetZ.Value, true);
if (frame.headFacing.HasValue)
HeadFacing.Add((float)keyframePosition / (float)duration, frame.headFacing.Value, true);
if (frame.bodyFacing.HasValue)
BodyFacing.Add((float)keyframePosition / (float)duration, frame.bodyFacing.Value, true);
if (frame.headBob.HasValue)
HeadBob.Add((float)keyframePosition / (float)duration, frame.headBob.Value, true);
if (frame.soundEffect != null) {
SoundEffects.Add(keyframePosition, frame.soundEffect);
}
keyframePosition += frame.tickDuration;
}
}
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Rimworld_Animations {
public class ThingAnimationClip : BaseAnimationClip
{
public List<ThingKeyframe> keyframes;
public override void buildSimpleCurves() {
}
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Rimworld_Animations {
public abstract class Keyframe
{
public int tickDuration = 1;
}
}

View File

@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
namespace Rimworld_Animations {
public class PawnKeyframe : Keyframe
{
public float? bodyAngle;
public float? headAngle;
public float? bodyOffsetZ;
public float? bodyOffsetX;
public float? headBob;
//todo: add headOffsets l/r?
public int? bodyFacing;
public int? headFacing;
public string soundEffect;
public float? atTick;
}
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Rimworld_Animations {
public class ThingKeyframe : Keyframe
{
}
}

View File

@ -0,0 +1,238 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using rjw;
using UnityEngine;
using Verse;
using Verse.Sound;
namespace Rimworld_Animations {
class CompBodyAnimator : ThingComp
{
public Pawn pawn => base.parent as Pawn;
public PawnGraphicSet Graphics;
public CompProperties_BodyAnimator Props => (CompProperties_BodyAnimator)(object)base.props;
public bool isAnimating {
get {
return Animating;
}
set {
Animating = value;
if(value == true) {
xxx.DrawNude(pawn);
} else {
pawn.Drawer.renderer.graphics.ResolveAllGraphics();
}
PortraitsCache.SetDirty(pawn);
}
}
private bool Animating;
private bool mirror = false;
private int actor;
private int animTicks = 0, stageTicks = 0, clipTicks = 0;
private int curStage = 0;
private float clipPercent = 0;
public Vector3 anchor, deltaPos, headBob;
public float bodyAngle, headAngle;
public Rot4 headFacing, bodyFacing;
private AnimationDef anim;
private AnimationStage stage => anim.animationStages[curStage];
private PawnAnimationClip clip => (PawnAnimationClip)stage.animationClips[actor];
public void setAnchor(IntVec3 pos)
{
anchor = pos.ToVector3();
}
public void setAnchor(Thing thing) {
anchor = thing.Position.ToVector3();
//center on bed
if(thing is Building_Bed) {
if(((Building_Bed)thing).SleepingSlotsCount == 2) {
if (thing.Rotation.AsInt == 0) {
anchor.x += 1;
anchor.z += 1;
}
else if (thing.Rotation.AsInt == 1) {
anchor.x += 1;
}
else if(thing.Rotation.AsInt == 3) {
anchor.z += 1;
}
}
else {
if(thing.Rotation.AsInt == 0) {
anchor.x += 0.5f;
anchor.z += 1f;
}
else if(thing.Rotation.AsInt == 1) {
anchor.x += 1f;
anchor.z += 0.5f;
}
else if(thing.Rotation.AsInt == 2) {
anchor.x += 0.5f;
} else {
anchor.z += 0.5f;
}
}
}
}
public void StartAnimation(AnimationDef anim, int actor, bool mirror = false) {
isAnimating = true;
pawn.jobs.posture = PawnPosture.Standing;
this.actor = actor;
this.anim = anim;
this.mirror = mirror;
curStage = 0;
animTicks = 0;
stageTicks = 0;
clipTicks = 0;
//tick once for initialization
tickAnim();
}
public override void CompTick() {
base.CompTick();
//maybe this is causing anim to stop mid?
if(Animating) {
tickAnim();
if (pawn?.jobs?.curDriver == null || (pawn?.jobs?.curDriver != null && !(pawn?.jobs?.curDriver is rjw.JobDriver_Sex))) {
Animating = false;
}
}
}
public void animatePawn(ref Vector3 rootLoc, ref float angle, ref Rot4 bodyFacing, ref Rot4 headFacing) {
if(!Animating) {
return;
}
rootLoc = anchor + deltaPos;
angle = bodyAngle;
bodyFacing = this.bodyFacing;
headFacing = this.headFacing;
}
public void tickGraphics(PawnGraphicSet graphics) {
this.Graphics = graphics;
}
public void tickAnim() {
if (!Animating) return;
animTicks++;
if (animTicks < anim.animationTimeTicks) {
tickStage();
} else {
Animating = false;
}
}
public void tickStage()
{
stageTicks++;
if(stageTicks >= stage.playTimeTicks) {
curStage++;
stageTicks = 0;
clipTicks = 0;
clipPercent = 0;
}
tickClip();
}
public void tickClip() {
clipTicks++;
//play sound effect
if(rjw.RJWSettings.sounds_enabled && clip.SoundEffects.ContainsKey(clipTicks)) {
SoundDef.Named(clip.SoundEffects[clipTicks]).PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
}
//loop animation if possible
if (clipPercent >= 1 && stage.isLooping) {
clipTicks = 1;//warning: don't set to zero or else calculations go wrong
}
clipPercent = (float)clipTicks / (float)clip.duration;
calculateDrawValues();
}
public void calculateDrawValues() {
deltaPos = new Vector3(clip.BodyOffsetX.Evaluate(clipPercent) * (mirror ? -1 : 1), clip.layer.AltitudeFor(), clip.BodyOffsetZ.Evaluate(clipPercent));
bodyAngle = clip.BodyAngle.Evaluate(clipPercent) * (mirror ? -1 : 1);
headAngle = clip.HeadAngle.Evaluate(clipPercent) * (mirror ? -1 : 1);
bodyFacing = mirror ? new Rot4((int)clip.BodyFacing.Evaluate(clipPercent)).Opposite : new Rot4((int)clip.BodyFacing.Evaluate(clipPercent));
bodyFacing = new Rot4((int)clip.BodyFacing.Evaluate(clipPercent));
if(bodyFacing.IsHorizontal && mirror) {
bodyFacing = bodyFacing.Opposite;
}
headFacing = new Rot4((int)clip.HeadFacing.Evaluate(clipPercent));
if(headFacing.IsHorizontal && mirror) {
headFacing = headFacing.Opposite;
}
headBob = new Vector3(0, 0, clip.HeadBob.Evaluate(clipPercent));
}
public Vector3 getPawnHeadPosition() {
return anchor + deltaPos + Quaternion.AngleAxis(bodyAngle, Vector3.up) * (pawn.Drawer.renderer.BaseHeadOffsetAt(headFacing) + headBob);
}
public override void PostExposeData() {
base.PostExposeData();
Scribe_Defs.Look(ref anim, "anim");
Scribe_Values.Look(ref animTicks, "animTicks", 1);
Scribe_Values.Look(ref stageTicks, "stageTicks", 1);
Scribe_Values.Look(ref clipTicks, "clipTicks", 1);
Scribe_Values.Look(ref clipPercent, "clipPercent", 1);
Scribe_Values.Look(ref mirror, "mirror");
Scribe_Values.Look(ref curStage, "curStage", 0);
Scribe_Values.Look(ref actor, "actor");
Scribe_Values.Look(ref Animating, "Animating");
Scribe_Values.Look(ref anchor, "anchor");
Scribe_Values.Look(ref deltaPos, "deltaPos");
Scribe_Values.Look(ref headBob, "headBob");
Scribe_Values.Look(ref bodyAngle, "bodyAngle");
Scribe_Values.Look(ref headAngle, "headAngle");
Scribe_Values.Look(ref headFacing, "headFacing");
Scribe_Values.Look(ref headFacing, "bodyFacing");
}
}
}

View File

@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
namespace Rimworld_Animations {
class CompProperties_BodyAnimator : CompProperties
{
public CompProperties_BodyAnimator() {
base.compClass = typeof(CompBodyAnimator);
}
}
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Rimworld_Animations {
class CompProperties_ThingAnimator
{
}
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Rimworld_Animations {
class CompThingAnimator
{
}
}

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using Verse;
namespace Rimworld_Animations {
public class AnimationDef : Def
{
public List<AnimationStage> animationStages;
public List<Actor> actors;
public int animationTimeTicks = 0; //do not set manually
public bool sounds = false;
public override void PostLoad() {
base.PostLoad();
foreach(AnimationStage stage in animationStages) {
stage.initialize();
animationTimeTicks += stage.playTimeTicks;
}
}
}
}

View File

@ -0,0 +1,62 @@
using System.Collections.Generic;
using RimWorld;
using Verse;
using Verse.AI;
using System;
using rjw;
namespace Rimworld_Animations {
public class JobDriver_SexBaseRecieverLovedForAnimation : JobDriver_SexBaseReciever {
public readonly TargetIndex ipartner = TargetIndex.A;
public readonly TargetIndex ibed = TargetIndex.B;
public Pawn Partner => (Pawn)(job.GetTarget(ipartner));
public new Building_Bed Bed => (Building_Bed)(job.GetTarget(ibed));
protected override IEnumerable<Toil> MakeNewToils() {
setup_ticks();
parteners.Add(Partner);// add job starter, so this wont fail, before Initiator starts his job
//--Log.Message("[RJW]JobDriver_GettinLoved::MakeNewToils is called");
float partner_ability = xxx.get_sex_ability(Partner);
// More/less hearts based on partner ability.
if (partner_ability < 0.8f)
ticks_between_thrusts += 100;
else if (partner_ability > 2.0f)
ticks_between_thrusts -= 25;
// More/less hearts based on opinion.
if (pawn.relations.OpinionOf(Partner) < 0)
ticks_between_hearts += 50;
else if (pawn.relations.OpinionOf(Partner) > 60)
ticks_between_hearts -= 25;
this.FailOnDespawnedOrNull(ipartner);
this.FailOn(() => !Partner.health.capacities.CanBeAwake);
this.FailOn(() => pawn.Drafted);
this.KeepLyingDown(ibed);
yield return Toils_Reserve.Reserve(ipartner, 1, 0);
yield return Toils_Reserve.Reserve(ibed, Bed.SleepingSlotsCount, 0);
Toil get_loved = Toils_LayDown.LayDown(ibed, true, false, false, false);
get_loved.FailOn(() => Partner.CurJobDef != DefDatabase<JobDef>.GetNamed("JoinInBedAnimation", true));
get_loved.defaultCompleteMode = ToilCompleteMode.Never;
get_loved.socialMode = RandomSocialMode.Off;
get_loved.AddPreTickAction(delegate {
if (pawn.IsHashIntervalTick(ticks_between_hearts))
MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
if (pawn.IsHashIntervalTick(ticks_between_thrusts))
xxx.sexTick(pawn, Partner, false);
});
get_loved.AddFinishAction(delegate {
if (xxx.is_human(pawn))
pawn.Drawer.renderer.graphics.ResolveApparelGraphics();
});
yield return get_loved;
}
}
}

View File

@ -0,0 +1,92 @@
using RimWorld;
using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using Verse.AI;
namespace Rimworld_Animations {
class JobDriver_SexCasualForAnimation : JobDriver_SexBaseInitiator {
public readonly TargetIndex ipartner = TargetIndex.A;
public readonly TargetIndex ibed = TargetIndex.B;
public Pawn Partner => (Pawn)job.GetTarget(ipartner);
public new Building_Bed Bed => (Building_Bed)job.GetTarget(ibed);
public override bool TryMakePreToilReservations(bool errorOnFailed) {
return ReservationUtility.Reserve(base.pawn, Partner, job, xxx.max_rapists_per_prisoner, 0, null, errorOnFailed);
}
protected override IEnumerable<Toil> MakeNewToils() {
setup_ticks();
this.FailOnDespawnedOrNull(ipartner);
this.FailOnDespawnedOrNull(ibed);
this.FailOn(() => !Partner.health.capacities.CanBeAwake);
yield return Toils_Reserve.Reserve(ipartner, xxx.max_rapists_per_prisoner, 0, null);
Toil goToPawnInBed = Toils_Goto.GotoThing(ipartner, PathEndMode.OnCell);
goToPawnInBed.FailOn(() => !RestUtility.InBed(Partner) && !xxx.in_same_bed(Partner, pawn));
yield return goToPawnInBed;
Toil startPartnerSex = new Toil();
startPartnerSex.initAction = delegate {
Log.Message("Attempting to start job...");
Job gettinLovedJob = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("GettinLovedAnimation"), pawn, Bed); // new gettin loved toil that wakes up the pawn goes here
Partner.jobs.jobQueue.EnqueueFirst(gettinLovedJob);
Partner.jobs.EndCurrentJob(JobCondition.InterruptForced);
};
yield return startPartnerSex;
Toil sexToil = new Toil();
sexToil.FailOn(() => (Partner.CurJobDef == null) || Partner.CurJobDef != DefDatabase<JobDef>.GetNamed("GettinLovedAnimation", true)); //partner jobdriver is not sexbaserecieverlovedforanim
sexToil.socialMode = RandomSocialMode.Off;
sexToil.defaultCompleteMode = ToilCompleteMode.Never;
sexToil.initAction = delegate {
usedCondom = (CondomUtility.TryUseCondom(base.pawn) || CondomUtility.TryUseCondom(Partner));
Start();
};
sexToil.AddPreTickAction(delegate {
ticks_left--;
if(Gen.IsHashIntervalTick(pawn, ticks_between_hearts)) {
MoteMaker.ThrowMetaIcon(pawn.Position, pawn.Map, ThingDefOf.Mote_Heart);
}
PawnUtility.GainComfortFromCellIfPossible(pawn, false);
PawnUtility.GainComfortFromCellIfPossible(Partner, false);
xxx.reduce_rest(Partner);
xxx.reduce_rest(pawn, 2);
if (ticks_left <= 0)
ReadyForNextToil();
});
sexToil.AddFinishAction(delegate {
End();
if(xxx.is_human(pawn)) {
pawn.Drawer.renderer.graphics.ResolveApparelGraphics();
}
});
yield return sexToil;
Toil finish = new Toil();
finish.initAction = delegate {
SexUtility.ProcessSex(pawn, Partner, usedCondom);
};
finish.defaultCompleteMode = ToilCompleteMode.Instant;
yield return finish;
}
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using RimWorld;
namespace Rimworld_Animations {
class MainTabWindow_SexAnimator : MainTabWindow
{
//todo: add animation maker window
}
}

View File

@ -0,0 +1,35 @@
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
namespace Rimworld_Animations {
[StaticConstructorOnStartup]
public static class HarmonyPatch_AlienRace {
static HarmonyPatch_AlienRace () {
try {
((Action)(() => {
if (LoadedModManager.RunningModsListForReading.Any(x => x.Name == "Humanoid Alien Races 2.0")) {
(new Harmony("rjw")).Patch(AccessTools.Method(typeof(PawnGraphicSet), "ResolveApparelGraphics"),
prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_AlienRace), "Prefix_StopResolveAllGraphicsWhileSex")));
}
}))();
}
catch (TypeLoadException ex) {
}
}
public static bool Prefix_StopResolveAllGraphicsWhileSex(ref Pawn ___pawn) {
if(___pawn.TryGetComp<CompBodyAnimator>() != null && ___pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,44 @@
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;
namespace Rimworld_Animations {
[StaticConstructorOnStartup]
public static class Patch_FacialAnimation {
static Patch_FacialAnimation() {
try {
((Action)(() => {
if (LoadedModManager.RunningModsListForReading.Any(x => x.Name == "[NL] Facial Animation - WIP")) {
(new Harmony("rjw")).Patch(AccessTools.Method(AccessTools.TypeByName("FacialAnimation.DrawFaceGraphicsComp"), "DrawGraphics"),
prefix: new HarmonyMethod(AccessTools.Method(typeof(Patch_FacialAnimation), "Prefix")));
}
}))();
}
catch (TypeLoadException ex) {
}
}
public static bool Prefix(ref Pawn ___pawn, ref Rot4 headFacing, ref Vector3 headOrigin, ref Quaternion quaternion, ref bool portrait) {
CompBodyAnimator bodyAnim = ___pawn.TryGetComp<CompBodyAnimator>();
if (bodyAnim.isAnimating && !portrait) {
headFacing = bodyAnim.headFacing;
headOrigin = new Vector3(bodyAnim.getPawnHeadPosition().x, headOrigin.y, bodyAnim.getPawnHeadPosition().z);
quaternion = Quaternion.AngleAxis(bodyAnim.headAngle, Vector3.up);
}
return true;
}
}
}

View File

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RimWorld;
using Verse;
using UnityEngine;
using System.Reflection;
using System.Reflection.Emit;
namespace Rimworld_Animations {
[HarmonyPatch(typeof(PawnRenderer), "RenderPawnInternal", new Type[]
{
typeof(Vector3),
typeof(float),
typeof(bool),
typeof(Rot4),
typeof(Rot4),
typeof(RotDrawMode),
typeof(bool),
typeof(bool),
typeof(bool)
})]
public static class HarmonyPatch_PawnRenderer
{
[HarmonyBefore(new string[] { "showhair.kv.rw", "erdelf.HumanoidAlienRaces", "Nals.FacialAnimation" })]
public static void Prefix(PawnRenderer __instance, ref Vector3 rootLoc, ref float angle, bool renderBody, ref Rot4 bodyFacing, ref Rot4 headFacing, RotDrawMode bodyDrawType, bool portrait, bool headStump, bool invisible)
{
PawnGraphicSet graphics = __instance.graphics;
Pawn pawn = graphics.pawn;
CompBodyAnimator bodyAnim = pawn.TryGetComp<CompBodyAnimator>();
if (!graphics.AllResolved) {
graphics.ResolveAllGraphics();
}
if (bodyAnim != null && bodyAnim.isAnimating && !portrait) {
bodyAnim.tickGraphics(graphics);
pawn.TryGetComp<CompBodyAnimator>().animatePawn(ref rootLoc, ref angle, ref bodyFacing, ref headFacing);
}
}
[HarmonyAfter(new string[] { "showhair.kv.rw", "erdelf.HumanoidAlienRaces", "Nals.FacialAnimation" })]
[HarmonyReversePatch(HarmonyReversePatchType.Snapshot)]
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
MethodInfo drawMeshNowOrLater = AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater");//typeof(GenDraw).GetMethod("DrawMeshNowOrLater", BindingFlags.Static | BindingFlags.Public);
FieldInfo headGraphic = AccessTools.Field(typeof(PawnGraphicSet), "headGraphic");
List<CodeInstruction> codes = instructions.ToList();
bool forHead = true;
for(int i = 0; i < codes.Count(); i++) {
//Instead of calling drawmeshnoworlater, add pawn to the stack and call my special static method
if (codes[i].OperandIs(drawMeshNowOrLater) && forHead) {
yield return new CodeInstruction(OpCodes.Ldarg_0);
yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(PawnRenderer), "pawn"));
yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(AnimationUtility), nameof(AnimationUtility.RenderPawnHeadMeshInAnimation)));
}
//checking for if(graphics.headGraphic != null)
else if (codes[i].opcode == OpCodes.Ldfld && codes[i].OperandIs(headGraphic)) {
forHead = true;
yield return codes[i];
}
//checking for if(renderbody)
else if(codes[i].opcode == OpCodes.Ldarg_3) {
forHead = false;
yield return codes[i];
}
else {
yield return codes[i];
}
}
}
}
}

View File

@ -0,0 +1,25 @@
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse;
namespace Rimworld_Animations {
[HarmonyPatch(typeof(Pawn_DrawTracker), "DrawPos", MethodType.Getter)]
public static class HarmonyPatch_Pawn_DrawTracker {
public static bool Prefix(ref Pawn ___pawn, ref Vector3 __result) {
if(___pawn.TryGetComp<CompBodyAnimator>() != null && ___pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
__result = ___pawn.TryGetComp<CompBodyAnimator>().anchor + ___pawn.TryGetComp<CompBodyAnimator>().deltaPos;
___pawn.Position = __result.ToIntVec3();
return false;
}
return true;
}
}
}

View File

@ -0,0 +1,50 @@
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using Verse;
namespace Rimworld_Animations {
[StaticConstructorOnStartup]
public static class Patch_ShowHairWithHats {
static Patch_ShowHairWithHats() {
try {
((Action)(() =>
{
if (LoadedModManager.RunningModsListForReading.Any(x => x.Name == "[KV] Show Hair With Hats or Hide All Hats - 1.1")) {
(new Harmony("rjw")).Patch(AccessTools.Method(AccessTools.TypeByName("ShowHair.Patch_PawnRenderer_RenderPawnInternal"), "Postfix"), //typeof(ShowHair.Patch_PawnRenderer_RenderPawnInternal), nameof(ShowHair.Patch_PawnRenderer_RenderPawnInternal.Postfix)),
transpiler: new HarmonyMethod(AccessTools.Method(typeof(Patch_ShowHairWithHats), "Transpiler")));
}
}))();
}
catch (TypeLoadException ex) { }
}
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) {
MethodInfo drawMeshNowOrLater = AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater");
List<CodeInstruction> codes = instructions.ToList();
for (int i = 0; i < codes.Count(); i++) {
//Instead of calling drawmeshnoworlater, add pawn to the stack and call my special static method
if (codes[i].OperandIs(drawMeshNowOrLater)) {
yield return new CodeInstruction(OpCodes.Ldarg_0);
yield return new CodeInstruction(OpCodes.Ldfld, AccessTools.DeclaredField(typeof(PawnRenderer), "pawn"));
yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(AnimationUtility), nameof(AnimationUtility.RenderPawnHeadMeshInAnimation)));
}
else {
yield return codes[i];
}
}
}
}
}

View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using HarmonyLib;
using System.Reflection;
namespace Rimworld_Animations {
[StaticConstructorOnStartup]
public static class Harmony_PatchAll {
static Harmony_PatchAll() {
Harmony val = new Harmony("rimworldanim");
val.PatchAll(Assembly.GetExecutingAssembly());
}
}
}

View File

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RimWorld;
using Verse;
using rjw;
namespace Rimworld_Animations {
[HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "Start")]
static class HarmonyPatch_JobDriver_SexBaseInitiator_Start {
public static void Postfix(ref JobDriver_SexBaseInitiator __instance) {
if(__instance is JobDriver_JoinInBed) {
Log.Warning("Tried to start wrong JobDriver with Rimworld-Animations installed. If you see this warning soon after installing this mod, it's fine and animated sex will start soon. If you see this a long time after installing, that's a problem.");
return;
}
Pawn Target = __instance.Target;
Pawn pawn = __instance.pawn;
Building_Bed bed = __instance.Bed;
if (__instance is JobDriver_BestialityForFemale)
bed = (__instance as JobDriver_BestialityForFemale).Bed;
else if (__instance is JobDriver_WhoreIsServingVisitors) {
bed = (__instance as JobDriver_WhoreIsServingVisitors).Bed;
}
else if(__instance is JobDriver_SexCasualForAnimation) {
bed = (__instance as JobDriver_SexCasualForAnimation).Bed;
}
if (__instance.Target.jobs?.curDriver is JobDriver_SexBaseReciever) {
if (!(Target.jobs.curDriver as JobDriver_SexBaseReciever).parteners.Contains(pawn)) {
(Target.jobs.curDriver as JobDriver_SexBaseReciever).parteners.Add(pawn);
}
if (bed != null) {
RerollAnimations(Target, __instance.duration, bed as Thing);
}
else {
RerollAnimations(Target, __instance.duration);
}
}
}
public static void RerollAnimations(Pawn pawn, int duration, Thing bed = null) {
if(pawn == null || !(pawn.jobs?.curDriver is JobDriver_SexBaseReciever)) {
Log.Message("Error: Tried to reroll animations when pawn isn't sexing");
return;
}
List<Pawn> pawnsToAnimate = (pawn.jobs.curDriver as JobDriver_SexBaseReciever).parteners.ToList();
if (!pawnsToAnimate.Contains(pawn)) {
pawnsToAnimate = pawnsToAnimate.Append(pawn).ToList();
}
AnimationDef anim = AnimationUtility.tryFindAnimation(ref pawnsToAnimate);
if (anim != null) {
Log.Message("Now playing " + anim.defName);
bool mirror = GenTicks.TicksGame % 2 == 0;
for (int i = 0; i < pawnsToAnimate.Count; i++) {
if (bed != null)
pawnsToAnimate[i].TryGetComp<CompBodyAnimator>().setAnchor(bed);
else
pawnsToAnimate[i].TryGetComp<CompBodyAnimator>().setAnchor(pawn);
pawnsToAnimate[i].TryGetComp<CompBodyAnimator>().StartAnimation(anim, i, mirror);
(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).ticks_left = anim.animationTimeTicks;
(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).ticksLeftThisToil = anim.animationTimeTicks;
(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).duration = anim.animationTimeTicks;
(pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).ticks_remaining = anim.animationTimeTicks;
}
}
else {
Log.Message("Anim not found");
//if pawn isn't already animating,
if (!pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
(pawn.jobs.curDriver as JobDriver_SexBaseReciever).increase_time(duration);
//they'll just do the thrusting anim
}
}
}
}
[HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "End")]
static class HarmonyPatch_JobDriver_SexBaseInitiator_End {
public static void Postfix(ref JobDriver_SexBaseInitiator __instance) {
if (__instance.Target.jobs?.curDriver is JobDriver_SexBaseReciever) {
if (__instance.pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
List<Pawn> parteners = (__instance.Target.jobs.curDriver as JobDriver_SexBaseReciever).parteners;
for (int i = 0; i < parteners.Count; i++) {
//prevents pawns who started a new anim from stopping their new anim
if (!((parteners[i].jobs.curDriver as JobDriver_SexBaseInitiator) != null && (parteners[i].jobs.curDriver as JobDriver_SexBaseInitiator).Target != __instance.pawn))
parteners[i].TryGetComp<CompBodyAnimator>().isAnimating = false;
if (xxx.is_human(parteners[i])) {
parteners[i].Drawer.renderer.graphics.ResolveApparelGraphics();
PortraitsCache.SetDirty(parteners[i]);
}
}
__instance.Target.TryGetComp<CompBodyAnimator>().isAnimating = false;
if (xxx.is_human(__instance.Target)) {
__instance.Target.Drawer.renderer.graphics.ResolveApparelGraphics();
PortraitsCache.SetDirty(__instance.Target);
}
}
(__instance.Target.jobs.curDriver as JobDriver_SexBaseReciever).parteners.Remove(__instance.pawn);
}
if (xxx.is_human(__instance.pawn)) {
__instance.pawn.Drawer.renderer.graphics.ResolveApparelGraphics();
PortraitsCache.SetDirty(__instance.pawn);
}
}
}
}

View File

@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using rjw;
using Verse;
using Verse.AI;
using RimWorld;
using HarmonyLib;
namespace Rimworld_Animations {
[HarmonyPatch(typeof(JobGiver_JoinInBed), "TryGiveJob")]
public static class HarmonyPatch_JoinInBedGiveJob {
public static bool Prefix(ref Job __result, ref Pawn pawn) {
__result = null;
if (!RJWHookupSettings.HookupsEnabled)
return false;
if (pawn.Drafted)
return false;
if (!SexUtility.ReadyForHookup(pawn))
return false;
// We increase the time right away to prevent the fairly expensive check from happening too frequently
SexUtility.IncreaseTicksToNextHookup(pawn);
// If the pawn is a whore, or recently had sex, skip the job unless they're really horny
if (!xxx.is_frustrated(pawn) && (xxx.is_whore(pawn) || !SexUtility.ReadyForLovin(pawn)))
return false;
// This check attempts to keep groups leaving the map, like guests or traders, from turning around to hook up
if (pawn.mindState?.duty?.def == DutyDefOf.TravelOrLeave) {
// TODO: Some guest pawns keep the TravelOrLeave duty the whole time, I think the ones assigned to guard the pack animals.
// That's probably ok, though it wasn't the intention.
if (RJWSettings.DebugLogJoinInBed) Log.Message($"[RJW] JoinInBed.TryGiveJob:({xxx.get_pawnname(pawn)}): has TravelOrLeave, no time for lovin!");
return false;
}
if (pawn.CurJob == null || pawn.CurJob.def == JobDefOf.LayDown) {
//--Log.Message(" checking pawn and abilities");
if (xxx.can_fuck(pawn) || xxx.can_be_fucked(pawn)) {
//--Log.Message(" finding partner");
Pawn partner = JobGiver_JoinInBed.find_pawn_to_fuck(pawn, pawn.Map);
//--Log.Message(" checking partner");
if (partner == null)
return false;
// Can never be null, since find checks for bed.
Building_Bed bed = partner.CurrentBed();
// Interrupt current job.
if (pawn.CurJob != null && pawn.jobs.curDriver != null)
pawn.jobs.curDriver.EndJobWith(JobCondition.InterruptForced);
__result = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("JoinInBedAnimation", true), partner, bed);
return false;
}
}
return false;
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib;
using RimWorld;
using Verse;
using rjw;
using Verse.Sound;
namespace Rimworld_Animations {
[HarmonyPatch(typeof(xxx), "sexTick")]
public static class HarmonyPatch_SexTick {
public static bool Prefix(ref Pawn pawn, ref Pawn partner, ref bool enablerotation, ref bool pawnnude, ref bool partnernude) {
if (enablerotation) {
pawn.rotationTracker.Face(((Thing)partner).DrawPos);
partner.rotationTracker.Face(((Thing)pawn).DrawPos);
}
if (RJWSettings.sounds_enabled && !pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
SoundDef.Named("Sex").PlayOneShot(new TargetInfo(pawn.Position, pawn.Map));
}
pawn.Drawer.Notify_MeleeAttackOn((Thing)(object)partner);
if (enablerotation) {
pawn.rotationTracker.FaceCell(partner.Position);
}
if (pawnnude && !xxx.has_quirk(pawn, "Endytophile")) {
xxx.DrawNude(pawn);
}
if (partnernude && !xxx.has_quirk(pawn, "Endytophile")) {
xxx.DrawNude(partner);
}
return false;
}
}
}

View File

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using rjw;
using HarmonyLib;
using Verse;
using RimWorld;
using Verse.AI;
namespace Rimworld_Animations {
[HarmonyPatch(typeof(WorkGiver_Sex), "JobOnThing")]
public static class HarmonyPatch_WorkGiverSex {
public static bool Prefix(ref Job __result, ref Thing t) {
Building_Bed bed = RestUtility.CurrentBed(t as Pawn);
if (bed == null) {
return false;
}
__result = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("JoinInBedAnimation", true), t as Pawn, bed);
return false;
}
}
}

@ -1 +0,0 @@
Subproject commit ab9a1d98883cb78d0d40a6bd93d85de7e34b132b