using System; using System.Collections.Generic; using System.Linq; 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) { /* These particular jobs need special code don't play anim for now */ if(__instance is JobDriver_Masturbate || __instance is JobDriver_ViolateCorpse) { return; } if(!AnimationSettings.PlayAnimForNonsexualActs && NonSexualAct(__instance)) { return; } Pawn pawn = __instance.pawn; Building_Bed bed = __instance.Bed; if ((__instance.Target as Pawn)?.jobs?.curDriver is JobDriver_SexBaseReciever) { Pawn Target = __instance.Target as Pawn; bool quickie = (__instance is JobDriver_SexQuick) && AnimationSettings.fastAnimForQuickie; int preAnimDuration = __instance.duration; int AnimationTimeTicks = 0; if (bed != null) { RerollAnimations(Target, out AnimationTimeTicks, bed as Thing, __instance.Sexprops.sexType, quickie, sexProps: __instance.Sexprops); } else { RerollAnimations(Target, out AnimationTimeTicks, sexType: __instance.Sexprops.sexType, fastAnimForQuickie: quickie, sexProps: __instance.Sexprops); } //Modify Orgasm ticks to only orgasm as many times as RJW stock orgasm allows if(AnimationTimeTicks != 0) { __instance.orgasmstick = preAnimDuration * __instance.orgasmstick / AnimationTimeTicks; } } } public static void RerollAnimations(Pawn pawn, out int AnimationTimeTicks, Thing bed = null, xxx.rjwSextype sexType = xxx.rjwSextype.None, bool fastAnimForQuickie = false, rjw.SexProps sexProps = null) { AnimationTimeTicks = 0; if(pawn == null || !(pawn.jobs?.curDriver is JobDriver_SexBaseReciever)) { Log.Error("Error: Tried to reroll animations when pawn isn't sexing"); return; } List pawnsToAnimate = (pawn.jobs.curDriver as JobDriver_SexBaseReciever).parteners.ToList(); if (!pawnsToAnimate.Contains(pawn)) { pawnsToAnimate = pawnsToAnimate.Append(pawn).ToList(); } for(int i = 0; i < pawnsToAnimate.Count; i++) { if(pawnsToAnimate[i].TryGetComp() == null) { Log.Error("Error: " + pawnsToAnimate[i].Name + " of race " + pawnsToAnimate[i].def.defName + " does not have CompBodyAnimator attached!"); break; } } AnimationDef anim = AnimationUtility.tryFindAnimation(ref pawnsToAnimate, sexType, sexProps); if (anim != null) { bool mirror = GenTicks.TicksGame % 2 == 0; IntVec3 pos = pawn.Position; for (int i = 0; i < anim.actors.Count; i++) { pawnsToAnimate[i].TryGetComp().isAnimating = false; } for (int i = 0; i < pawnsToAnimate.Count; i++) { if (bed != null) pawnsToAnimate[i].TryGetComp().setAnchor(bed); else { pawnsToAnimate[i].TryGetComp().setAnchor(pos); } bool shiver = pawnsToAnimate[i].jobs.curDriver is JobDriver_SexBaseRecieverRaped; pawnsToAnimate[i].TryGetComp().StartAnimation(anim, pawnsToAnimate, i, mirror, shiver, fastAnimForQuickie); int animTicks = anim.animationTimeTicks - (fastAnimForQuickie ? anim.animationStages[0].playTimeTicks : 0); (pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).ticks_left = animTicks; (pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).sex_ticks = animTicks; (pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).orgasmStartTick = animTicks; (pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).duration = animTicks; AnimationTimeTicks = animTicks; if(!AnimationSettings.hearts) { (pawnsToAnimate[i].jobs.curDriver as JobDriver_Sex).ticks_between_hearts = Int32.MaxValue; } } } else { Log.Message("No animation found"); /* //if pawn isn't already animating, if (!pawn.TryGetComp().isAnimating) { (pawn.jobs.curDriver as JobDriver_SexBaseReciever).increase_time(duration); //they'll just do the thrusting anim } */ } } static IEnumerable NonSexActRulePackDefNames = new String[] { "MutualHandholdingRP", "MutualMakeoutRP", }; public static bool NonSexualAct(JobDriver_SexBaseInitiator sexBaseInitiator) { if(NonSexActRulePackDefNames.Contains(sexBaseInitiator.Sexprops.rulePack)) { return true; } return false; } } [HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "End")] static class HarmonyPatch_JobDriver_SexBaseInitiator_End { public static void Postfix(ref JobDriver_SexBaseInitiator __instance) { Pawn reciever = __instance.Target as Pawn; if (reciever?.jobs?.curDriver is JobDriver_SexBaseReciever recieverJobDriver) { if (CompBodyAnimator.IsAnimating(__instance.pawn)) { List parteners = recieverJobDriver.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().isAnimating = false; } __instance.Target.TryGetComp().isAnimating = false; if (xxx.is_human(reciever)) { reciever?.Drawer.renderer.graphics.ResolveApparelGraphics(); PortraitsCache.SetDirty(reciever); } } recieverJobDriver?.parteners.Remove(__instance.pawn); } if (xxx.is_human(__instance.pawn)) { __instance.pawn.Drawer.renderer.graphics.ResolveApparelGraphics(); PortraitsCache.SetDirty(__instance.pawn); } } } }