Compare commits

...

11 commits

Author SHA1 Message Date
Taleir
487cfb5bbf Merge branch 'har-patch-fix' into 'master'
Attempts to get the `DrawAddonsFinalHook` patch working.

See merge request c0ffeeeeeeee/rimworld-animations!10
2024-02-14 23:08:06 +00:00
c0ffeeeeeeee
15b0e2c9f6 Merge branch 'master' into 'master'
Fix LoopNeverending Error

See merge request c0ffeeeeeeee/rimworld-animations!18
2024-02-14 07:26:48 +00:00
Euclidean
c6241be3fc fix LoopNeverending error 2024-02-14 06:57:26 +00:00
c0ffeeeeeeee
33ebba94a7 Merge branch 'is-animating-optimization' into 'master'
Lower perfomance impact when not animating

See merge request c0ffeeeeeeee/rimworld-animations!15
2023-12-03 23:14:52 +00:00
amevarashi
3743248e9a Fast IsAnimating checks 2023-12-03 11:48:42 +05:00
Tory
69793e6be1 Removed anim patch from incompatible 2023-11-25 18:14:54 +00:00
c0ffee
6cffdfef95 versioning 2023-08-02 13:32:57 -07:00
c0ffee
af4851c1ef Merge branch 'master' of https://gitgud.io/c0ffeeeeeeee/rimworld-animations 2023-08-02 13:32:23 -07:00
c0ffee
6a271a78fe fixed dildo anim not loading in 1.4 2023-08-02 13:32:12 -07:00
Tory
06a3d90dbf Made animation patch incompatible 2023-08-02 18:39:42 +00:00
Taleir of Deynai
a106fd60f1 Attempts to get the DrawAddonsFinalHook patch working.
There's still something off about the offsets, but I'm not quite sure what.
2023-02-26 16:51:23 -08:00
19 changed files with 724 additions and 186 deletions

View file

@ -1,18 +1,24 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using RimWorld; using RimWorld;
using rjw; using rjw;
using UnityEngine; using UnityEngine;
using Verse; using Verse;
using Verse.AI;
using Verse.Sound; using Verse.Sound;
namespace Rimworld_Animations { namespace Rimworld_Animations {
public class CompBodyAnimator : ThingComp public class CompBodyAnimator : ThingComp
{ {
/// <summary>
/// Cache of the currently animated pawns for the very fast isAnimating checks
/// </summary>
private static readonly HashSet<Pawn> animatingPawns = new HashSet<Pawn>();
/// <summary>
/// Check if the <paramref name="pawn"/> is currently animated by this comp
/// </summary>
public static bool IsAnimating(Pawn pawn) => animatingPawns.Contains(pawn);
public Pawn pawn => base.parent as Pawn; public Pawn pawn => base.parent as Pawn;
public PawnGraphicSet Graphics; public PawnGraphicSet Graphics;
@ -27,9 +33,11 @@ namespace Rimworld_Animations {
if (value == true) { if (value == true) {
SexUtility.DrawNude(pawn); SexUtility.DrawNude(pawn);
animatingPawns.Add(pawn);
} else { } else {
pawn.Drawer.renderer.graphics.ResolveAllGraphics(); pawn.Drawer.renderer.graphics.ResolveAllGraphics();
actorsInCurrentAnimation = null; actorsInCurrentAnimation = null;
animatingPawns.Remove(pawn);
} }
PortraitsCache.SetDirty(pawn); PortraitsCache.SetDirty(pawn);
@ -506,9 +514,15 @@ namespace Rimworld_Animations {
public bool LoopNeverending() public bool LoopNeverending()
{ {
if(pawn?.jobs?.curDriver != null && JobDriver jobDriver = pawn?.jobs?.curDriver;
(pawn.jobs.curDriver is JobDriver_Sex) && (pawn.jobs.curDriver as JobDriver_Sex).neverendingsex || if (jobDriver == null) return false;
(pawn.jobs.curDriver is JobDriver_SexBaseReciever) && (pawn.jobs.curDriver as JobDriver_Sex).Partner?.jobs?.curDriver != null && ((pawn.jobs.curDriver as JobDriver_Sex).Partner.jobs.curDriver as JobDriver_Sex).neverendingsex)
if (jobDriver is JobDriver_Sex sexDriver && sexDriver.neverendingsex)
{
return true;
}
if (jobDriver is JobDriver_SexBaseReciever sexReceiverDriver && sexReceiverDriver.Partner?.jobs?.curDriver is JobDriver_Sex partnerSexDriver && partnerSexDriver.neverendingsex)
{ {
return true; return true;
} }

View file

@ -1,9 +1,6 @@
using System; using RimWorld;
using System.Collections.Generic;
using RimWorld;
using UnityEngine; using UnityEngine;
using Verse; using Verse;
using Rimworld_Animations;
namespace Rimworld_Animations { namespace Rimworld_Animations {
@ -17,9 +14,9 @@ namespace Rimworld_Animations {
if (!flags.FlagSet(PawnRenderFlags.Portrait) && layer == PawnOverlayDrawer.OverlayLayer.Head) if (!flags.FlagSet(PawnRenderFlags.Portrait) && layer == PawnOverlayDrawer.OverlayLayer.Head)
{ {
CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>(); if (CompBodyAnimator.IsAnimating(pawn) && pawn.Drawer.renderer.graphics.headGraphic != null)
if (pawnAnimator != null && pawnAnimator.isAnimating && pawn.Drawer.renderer.graphics.headGraphic != null)
{ {
CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();
pawnRot = pawnAnimator.headFacing; pawnRot = pawnAnimator.headFacing;
quat = Quaternion.AngleAxis(angle: pawnAnimator.headAngle, axis: Vector3.up); quat = Quaternion.AngleAxis(angle: pawnAnimator.headAngle, axis: Vector3.up);
float y = drawLoc.y; float y = drawLoc.y;

View file

@ -1,8 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse; using Verse;
using RimWorld; using RimWorld;
using UnityEngine; using UnityEngine;
@ -25,14 +21,13 @@ namespace Rimworld_Animations {
listingStandard.GapLine(); listingStandard.GapLine();
if (Find.Selector.SingleSelectedThing is Pawn) { if (Find.Selector.SingleSelectedThing is Pawn curPawn) {
Pawn curPawn = Find.Selector.SingleSelectedThing as Pawn; if (CompBodyAnimator.IsAnimating(curPawn)) {
if (curPawn.TryGetComp<CompBodyAnimator>().isAnimating) { CompBodyAnimator compBodyAnimator = curPawn.TryGetComp<CompBodyAnimator>();
AnimationDef def = compBodyAnimator.CurrentAnimation;
AnimationDef def = curPawn.TryGetComp<CompBodyAnimator>().CurrentAnimation; int ActorIndex = compBodyAnimator.ActorIndex;
int ActorIndex = curPawn.TryGetComp<CompBodyAnimator>().ActorIndex;
float offsetX = 0, offsetZ = 0, rotation = 0; float offsetX = 0, offsetZ = 0, rotation = 0;
string bodyTypeDef = (curPawn.story?.bodyType != null) ? curPawn.story.bodyType.ToString() : ""; string bodyTypeDef = (curPawn.story?.bodyType != null) ? curPawn.story.bodyType.ToString() : "";
@ -57,8 +52,6 @@ namespace Rimworld_Animations {
listingStandard.Label("Warning--You generally don't want to change human offsets, only alien offsets"); listingStandard.Label("Warning--You generally don't want to change human offsets, only alien offsets");
} }
bool mirrored = curPawn.TryGetComp<CompBodyAnimator>().Mirror;
float.TryParse(listingStandard.TextEntryLabeled("X Offset: ", offsetX.ToString()), out offsetX); float.TryParse(listingStandard.TextEntryLabeled("X Offset: ", offsetX.ToString()), out offsetX);
offsetX = listingStandard.Slider(offsetX, -2, 2); offsetX = listingStandard.Slider(offsetX, -2, 2);

View file

@ -23,17 +23,16 @@ namespace Rimworld_Animations {
} }
}))(); }))();
} }
catch (TypeLoadException ex) { catch (TypeLoadException) {
} }
} }
public static bool Prefix(ref Pawn ___pawn, ref Rot4 headFacing, ref Vector3 headOrigin, ref Quaternion quaternion, ref bool portrait) { public static bool Prefix(Pawn ___pawn, ref Rot4 headFacing, ref Vector3 headOrigin, ref Quaternion quaternion, bool portrait) {
CompBodyAnimator bodyAnim = ___pawn.TryGetComp<CompBodyAnimator>(); if (!portrait && CompBodyAnimator.IsAnimating(___pawn)) {
if (bodyAnim != null && bodyAnim.isAnimating && !portrait) {
CompBodyAnimator bodyAnim = ___pawn.TryGetComp<CompBodyAnimator>();
headFacing = bodyAnim.headFacing; headFacing = bodyAnim.headFacing;
headOrigin = new Vector3(bodyAnim.getPawnHeadPosition().x, headOrigin.y, bodyAnim.getPawnHeadPosition().z); headOrigin = new Vector3(bodyAnim.getPawnHeadPosition().x, headOrigin.y, bodyAnim.getPawnHeadPosition().z);
quaternion = Quaternion.AngleAxis(bodyAnim.headAngle, Vector3.up); quaternion = Quaternion.AngleAxis(bodyAnim.headAngle, Vector3.up);

View file

@ -1,11 +1,5 @@
using HarmonyLib; using HarmonyLib;
using rjw; using rjw;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
namespace Rimworld_Animations namespace Rimworld_Animations
{ {
@ -14,7 +8,7 @@ namespace Rimworld_Animations
{ {
public static bool Prefix(JobDriver_Sex __instance) public static bool Prefix(JobDriver_Sex __instance)
{ {
if (__instance.pawn.TryGetComp<CompBodyAnimator>().isAnimating) if (CompBodyAnimator.IsAnimating(__instance.pawn))
{ {
return false; return false;
} }

View file

@ -1,13 +1,9 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib; using HarmonyLib;
using RimWorld; using RimWorld;
using Verse; using Verse;
using rjw; using rjw;
using System.Reflection.Emit;
using Verse.AI; using Verse.AI;
namespace Rimworld_Animations namespace Rimworld_Animations
@ -27,11 +23,7 @@ namespace Rimworld_Animations
} }
return true; return true;
} }
} }
[HarmonyPatch(typeof(JobDriver_JoinInBed), "MakeNewToils")] [HarmonyPatch(typeof(JobDriver_JoinInBed), "MakeNewToils")]
@ -66,19 +58,13 @@ namespace Rimworld_Animations
toils[3].AddPreTickAction(() => toils[3].AddPreTickAction(() =>
{ {
if (!__instance.Partner.TryGetComp<CompBodyAnimator>().isAnimating) if (!CompBodyAnimator.IsAnimating(__instance.Partner))
{ {
__instance.pawn.TryGetComp<CompBodyAnimator>().isAnimating = false; __instance.pawn.TryGetComp<CompBodyAnimator>().isAnimating = false;
} }
}); });
__result = toils.AsEnumerable(); __result = toils.AsEnumerable();
} }
} }
} }

View file

@ -1,8 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HarmonyLib; using HarmonyLib;
using RimWorld; using RimWorld;
using Verse; using Verse;
@ -158,10 +156,12 @@ namespace Rimworld_Animations {
public static void Postfix(ref JobDriver_SexBaseInitiator __instance) { public static void Postfix(ref JobDriver_SexBaseInitiator __instance) {
if ((__instance.Target as Pawn)?.jobs?.curDriver is JobDriver_SexBaseReciever) { Pawn reciever = __instance.Target as Pawn;
if (__instance.pawn.TryGetComp<CompBodyAnimator>().isAnimating) {
List<Pawn> parteners = ((__instance.Target as Pawn)?.jobs.curDriver as JobDriver_SexBaseReciever).parteners; if (reciever?.jobs?.curDriver is JobDriver_SexBaseReciever recieverJobDriver) {
if (CompBodyAnimator.IsAnimating(__instance.pawn)) {
List<Pawn> parteners = recieverJobDriver.parteners;
for (int i = 0; i < parteners.Count; i++) { for (int i = 0; i < parteners.Count; i++) {
@ -173,13 +173,13 @@ namespace Rimworld_Animations {
__instance.Target.TryGetComp<CompBodyAnimator>().isAnimating = false; __instance.Target.TryGetComp<CompBodyAnimator>().isAnimating = false;
if (xxx.is_human((__instance.Target as Pawn))) { if (xxx.is_human(reciever)) {
(__instance.Target as Pawn)?.Drawer.renderer.graphics.ResolveApparelGraphics(); reciever?.Drawer.renderer.graphics.ResolveApparelGraphics();
PortraitsCache.SetDirty((__instance.Target as Pawn)); PortraitsCache.SetDirty(reciever);
} }
} }
((__instance.Target as Pawn)?.jobs.curDriver as JobDriver_SexBaseReciever).parteners.Remove(__instance.pawn); recieverJobDriver?.parteners.Remove(__instance.pawn);
} }

View file

@ -1,4 +1,4 @@
using HarmonyLib; /*using HarmonyLib;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -19,4 +19,4 @@ namespace Rimworld_Animations
} }
} }
} }*/

View file

@ -5,7 +5,6 @@ using HarmonyLib;
using RimWorld; using RimWorld;
using Verse; using Verse;
using UnityEngine; using UnityEngine;
using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
namespace Rimworld_Animations { namespace Rimworld_Animations {
@ -31,12 +30,10 @@ namespace Rimworld_Animations {
PawnGraphicSet graphics = __instance.graphics; PawnGraphicSet graphics = __instance.graphics;
Pawn pawn = graphics.pawn; Pawn pawn = graphics.pawn;
CompBodyAnimator bodyAnim = pawn.TryGetComp<CompBodyAnimator>();
if (CompBodyAnimator.IsAnimating(pawn) && pawn.Map == Find.CurrentMap)
if (bodyAnim != null && bodyAnim.isAnimating && pawn.Map == Find.CurrentMap)
{ {
bodyAnim.animatePawnBody(ref rootLoc, ref angle, ref bodyFacing); pawn.TryGetComp<CompBodyAnimator>().animatePawnBody(ref rootLoc, ref angle, ref bodyFacing);
} }
@ -75,7 +72,7 @@ namespace Rimworld_Animations {
else else
{ {
yield return ins[i]; yield return ins[i];
} }
} }
} }
} }

View file

@ -1,29 +1,23 @@
using HarmonyLib; using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse; using Verse;
namespace Rimworld_Animations { namespace Rimworld_Animations {
[HarmonyPatch(typeof(Thing), "Rotation", MethodType.Getter)] [HarmonyPatch(typeof(Thing), nameof(Thing.Rotation), MethodType.Getter)]
public static class HarmonyPatch_PawnRotation { public static class HarmonyPatch_PawnRotation {
public static bool Prefix(Thing __instance, ref Rot4 __result) { public static bool Prefix(Thing __instance, ref Rot4 __result) {
if(!(__instance is Pawn) || (__instance as Pawn)?.TryGetComp<CompBodyAnimator>() == null || !(__instance as Pawn).TryGetComp<CompBodyAnimator>().isAnimating) { if (!(__instance is Pawn pawn)) {
return true;
}
if (!CompBodyAnimator.IsAnimating(pawn)) {
return true; return true;
} }
Pawn pawn = (__instance as Pawn);
__result = pawn.TryGetComp<CompBodyAnimator>().bodyFacing; __result = pawn.TryGetComp<CompBodyAnimator>().bodyFacing;
return false; return false;
} }
} }
} }

View file

@ -1,9 +1,4 @@
using HarmonyLib; using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine; using UnityEngine;
using Verse; using Verse;
@ -13,10 +8,9 @@ namespace Rimworld_Animations {
public static class HarmonyPatch_Pawn_DrawTracker { public static class HarmonyPatch_Pawn_DrawTracker {
public static bool Prefix(ref Pawn ___pawn, ref Vector3 __result) { public static bool Prefix(ref Pawn ___pawn, ref Vector3 __result) {
CompBodyAnimator bodyAnim = ___pawn.TryGetComp<CompBodyAnimator>(); if (CompBodyAnimator.IsAnimating(___pawn)) {
CompBodyAnimator bodyAnim = ___pawn.TryGetComp<CompBodyAnimator>();
if (bodyAnim != null && bodyAnim.isAnimating) { __result = bodyAnim.anchor + bodyAnim.deltaPos;
__result = ___pawn.TryGetComp<CompBodyAnimator>().anchor + ___pawn.TryGetComp<CompBodyAnimator>().deltaPos;
return false; return false;
} }

View file

@ -1,9 +1,4 @@
using HarmonyLib; using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse; using Verse;
namespace Rimworld_Animations namespace Rimworld_Animations
@ -13,8 +8,7 @@ namespace Rimworld_Animations
{ {
public static bool Prefix(ref Pawn ___pawn) public static bool Prefix(ref Pawn ___pawn)
{ {
if (CompBodyAnimator.IsAnimating(___pawn))
if (___pawn.TryGetComp<CompBodyAnimator>() != null && ___pawn.TryGetComp<CompBodyAnimator>().isAnimating)
{ {
return false; return false;
} }

View file

@ -1,12 +1,6 @@
using HarmonyLib; using HarmonyLib;
using RimWorld; using RimWorld;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using Verse; using Verse;
namespace Rimworld_Animations namespace Rimworld_Animations
@ -16,18 +10,16 @@ namespace Rimworld_Animations
{ {
static bool ClearCache(Pawn pawn) static bool ClearCache(Pawn pawn)
{ {
return pawn.IsInvisible() || (pawn.TryGetComp<CompBodyAnimator>() != null && pawn.TryGetComp<CompBodyAnimator>().isAnimating); return pawn.IsInvisible() || CompBodyAnimator.IsAnimating(pawn);
} }
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{ {
var list = instructions.ToList();
foreach (CodeInstruction i in instructions) foreach (CodeInstruction i in instructions)
{ {
if (i.OperandIs(AccessTools.Method(typeof(PawnUtility), "IsInvisible"))) if (i.Calls(AccessTools.Method(typeof(PawnUtility), nameof(PawnUtility.IsInvisible))))
{ {
yield return new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(PawnRenderer_RenderPawnAt_Patch), "ClearCache")); yield return CodeInstruction.Call(typeof(PawnRenderer_RenderPawnAt_Patch), nameof(ClearCache));
} }
else else
{ {

View file

@ -169,11 +169,10 @@ namespace Rimworld_Animations {
return; return;
} }
CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>(); if (!CompBodyAnimator.IsAnimating(pawn)) {
if (pawnAnimator == null || !pawnAnimator.isAnimating) {
GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, drawNow); GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, drawNow);
} else { } else {
CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();
Vector3 pawnHeadPosition = pawnAnimator.getPawnHeadPosition(); Vector3 pawnHeadPosition = pawnAnimator.getPawnHeadPosition();
pawnHeadPosition.y = loc.y; pawnHeadPosition.y = loc.y;
GenDraw.DrawMeshNowOrLater(MeshPool.humanlikeHeadSet.MeshAt(pawnAnimator.headFacing), pawnHeadPosition, Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up), material, true); GenDraw.DrawMeshNowOrLater(MeshPool.humanlikeHeadSet.MeshAt(pawnAnimator.headFacing), pawnHeadPosition, Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up), material, true);
@ -184,14 +183,13 @@ namespace Rimworld_Animations {
{ {
if (flags.FlagSet(PawnRenderFlags.Portrait)) return; if (flags.FlagSet(PawnRenderFlags.Portrait)) return;
CompBodyAnimator anim = pawn.TryGetComp<CompBodyAnimator>(); if (CompBodyAnimator.IsAnimating(pawn))
if (anim.isAnimating)
{ {
CompBodyAnimator anim = pawn.TryGetComp<CompBodyAnimator>();
bodyFacing = anim.headFacing; bodyFacing = anim.headFacing;
angle = anim.headAngle; angle = anim.headAngle;
quat = Quaternion.AngleAxis(anim.headAngle, Vector3.up); quat = Quaternion.AngleAxis(anim.headAngle, Vector3.up);
pos = anim.getPawnHeadOffset(); pos = anim.getPawnHeadOffset();
} }
} }
@ -202,12 +200,12 @@ namespace Rimworld_Animations {
return; return;
} }
CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();
if (pawnAnimator == null || !pawnAnimator.isAnimating || portrait) { if (!CompBodyAnimator.IsAnimating(pawn) || portrait) {
GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait); GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
} }
else { else {
CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();
Vector3 pawnHeadPosition = pawnAnimator.getPawnHeadPosition(); Vector3 pawnHeadPosition = pawnAnimator.getPawnHeadPosition();
pawnHeadPosition.x *= bodySizeFactor; pawnHeadPosition.x *= bodySizeFactor;
pawnHeadPosition.x *= bodySizeFactor; pawnHeadPosition.x *= bodySizeFactor;

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Manifest> <Manifest>
<identifier>Rimworld-Animations</identifier> <identifier>Rimworld-Animations</identifier>
<version>1.3.4</version> <version>1.3.7</version>
</Manifest> </Manifest>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup> <PropertyGroup>
@ -12,6 +12,7 @@
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion> <TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment> <FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic> <Deterministic>true</Deterministic>
<LangVersion>9</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>false</DebugSymbols> <DebugSymbols>false</DebugSymbols>

View file

@ -6,52 +6,162 @@ using System.Reflection;
using System.Reflection.Emit; using System.Reflection.Emit;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Runtime.CompilerServices;
using UnityEngine; using UnityEngine;
using Verse; using Verse;
using AlienRace; using AlienRace;
namespace Rimworld_Animations { namespace Rimworld_Animations
{
[StaticConstructorOnStartup] [StaticConstructorOnStartup]
public static class HarmonyPatch_AlienRace public static class HarmonyPatch_AlienRace
{ {
static readonly Type AlienRace_HarmonyPatches = AccessTools.TypeByName("AlienRace.HarmonyPatches");
static readonly MethodInfo InnerDrawAddon;
static readonly FieldInfo AccessForPawn;
static readonly FieldInfo AccessForRotation;
static HarmonyPatch_AlienRace() static HarmonyPatch_AlienRace()
{ {
(new Harmony("rjwanim")).Patch(AccessTools.Method(AccessTools.TypeByName("AlienRace.HarmonyPatches"), "DrawAddons"), AccessForPawn = null;
prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_AlienRace), "Prefix_AnimateHeadAddons"))); AccessForRotation = null;
InnerDrawAddon = AccessTools.FirstMethod(AlienRace_HarmonyPatches, (mi) =>
{
if (mi.GetCustomAttribute<CompilerGeneratedAttribute>() is null) return false;
if (mi.ReturnType != typeof(void)) return false;
if (!mi.Name.Contains("DrawAddon")) return false;
var parameters = mi.GetParameters();
if (parameters.Length != 4) return false;
if (parameters[0].ParameterType != typeof(AlienPartGenerator.BodyAddon)) return false;
if (parameters[1].ParameterType != typeof(Graphic)) return false;
if (parameters[2].ParameterType != typeof(Vector2)) return false;
return true;
});
if (InnerDrawAddon is null)
{
Log.Error("[rjwanim] Failed to apply Alien Race patches: could not find local `DrawAddon` method.");
return;
}
// Extract the closure struct. This is passed with `ref`, so we have
// to also pull the element type to get rid of that.
var displayClassType = InnerDrawAddon.GetParameters()[3].ParameterType.GetElementType();
if (displayClassType is null)
{
Log.Error("[rjwanim] Failed to apply Alien Race patches: could not get type of `DrawAddon` closure.");
return;
}
AccessForPawn = AccessTools.Field(displayClassType, "pawn");
if (AccessForPawn is null)
{
Log.Error("[rjwanim] Failed to apply Alien Race patches: could not find field `pawn` of closure.");
return;
}
AccessForRotation = AccessTools.Field(displayClassType, "rotation");
if (AccessForRotation is null)
{
Log.Error("[rjwanim] Failed to apply Alien Race patches: could not find field `rotation` of closure.");
return;
}
// Got access to everything. It should be safe to setup the patches.
var harmonyInstance = new Harmony("rjwanim");
harmonyInstance.Patch(
InnerDrawAddon,
prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_AlienRace), nameof(Prefix_FixDrawAddonRotation))),
postfix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_AlienRace), nameof(Postfix_FixDrawAddonRotation)))
);
harmonyInstance.Patch(
AccessTools.Method(AlienRace_HarmonyPatches, "DrawAddonsFinalHook"),
postfix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_AlienRace), nameof(Postfix_DrawAddonsFinalHook)))
);
} }
/* todo: replace jank prefix with this public static bool ShouldForceDrawForBody(Pawn pawn, AlienPartGenerator.BodyAddon addon)
public static void Prefix_DrawAddonsFinalHook(ref Pawn pawn, ref AlienPartGenerator.BodyAddon addon, ref Rot4 rot, ref Graphic graphic, ref Vector3 offsetVector, ref float angle, ref Material mat) {
{ if (pawn.def is not ThingDef_AlienRace alienProps) return false;
CompBodyAnimator animator = pawn.TryGetComp<CompBodyAnimator>();
if (animator == null || !animator.isAnimating) if (alienProps.defName.Contains("Orassan") && addon.path.ToLower().Contains("tail"))
{ return true;
return;
}
if(addon.alignWithHead || addon.drawnInBed) return false;
{ }
rot = animator.headFacing;
// The parameter `__3` is the compiler generated closure struct.
// Even though `__3` is a value type passed by reference, receiving it as
// an object boxes the underlying reference, so we don't need `ref` here.
// In fact, if you try to add it, RimWorld will crash outright!
public static void Prefix_FixDrawAddonRotation(AlienPartGenerator.BodyAddon ba, object __3, out Rot4 __state)
{
// Store the original rotation so we can restore it later.
__state = (Rot4)AccessForRotation.GetValue(__3);
if (ba is null) return;
var pawn = (Pawn)AccessForPawn.GetValue(__3);
if (pawn.TryGetComp<CompBodyAnimator>() is not { } animator) return;
if (!animator.isAnimating) return;
var forceDrawForBody = ShouldForceDrawForBody(pawn, ba);
// Set the rotation according to the animation.
if ((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead)
AccessForRotation.SetValue(__3, animator.headFacing);
else
AccessForRotation.SetValue(__3, animator.bodyFacing);
}
public static void Postfix_FixDrawAddonRotation(object __3, Rot4 __state)
{
// Restore the original value, since we're in a loop and the next
// part may need the previous rotation. Just being safe.
AccessForRotation.SetValue(__3, __state);
}
public static void Postfix_DrawAddonsFinalHook(Pawn pawn, AlienPartGenerator.BodyAddon addon, Rot4 rot, ref Vector3 offsetVector, ref float angle)
{
if (pawn.TryGetComp<CompBodyAnimator>() is not { } animator) return;
if (!animator.isAnimating) return;
var forceDrawForBody = ShouldForceDrawForBody(pawn, addon);
if ((addon.drawnInBed && !forceDrawForBody) || addon.alignWithHead)
{
angle = animator.headAngle; angle = animator.headAngle;
offsetVector += animator.deltaPos + animator.bodyAngle * animator.headBob; offsetVector += animator.deltaPos + animator.headBob;
} }
else else
{ {
rot = animator.bodyFacing; if (AnimationSettings.controlGenitalRotation && addon.path.ToLower().Contains("penis"))
angle = animator.bodyAngle; angle = animator.genitalAngle;
else
angle = animator.bodyAngle;
offsetVector += animator.deltaPos; offsetVector += animator.deltaPos;
} }
} if (rot == Rot4.North && addon.layerInvert)
*/ {
offsetVector.y = -offsetVector.y;
}
if (rot == Rot4.East)
{
angle *= -1f;
offsetVector.x = -offsetVector.x;
}
}
public static bool Prefix_AnimateHeadAddons(PawnRenderFlags renderFlags, Vector3 vector, Vector3 headOffset, Pawn pawn, Quaternion quat, Rot4 rotation) public static bool Prefix_AnimateHeadAddons(PawnRenderFlags renderFlags, Vector3 vector, Vector3 headOffset, Pawn pawn, Quaternion quat, Rot4 rotation)
{ {
if (renderFlags.FlagSet(PawnRenderFlags.Portrait) || pawn.TryGetComp<CompBodyAnimator>() == null || !pawn.TryGetComp<CompBodyAnimator>().isAnimating) return true; if (renderFlags.FlagSet(PawnRenderFlags.Portrait) || !CompBodyAnimator.IsAnimating(pawn)) return true;
if (!(pawn.def is ThingDef_AlienRace alienProps) || renderFlags.FlagSet(PawnRenderFlags.Invisible)) return false; if (!(pawn.def is ThingDef_AlienRace alienProps) || renderFlags.FlagSet(PawnRenderFlags.Invisible)) return false;
List<AlienPartGenerator.BodyAddon> addons = alienProps.alienRace.generalSettings.alienPartGenerator.bodyAddons; List<AlienPartGenerator.BodyAddon> addons = alienProps.alienRace.generalSettings.alienPartGenerator.bodyAddons;
@ -64,14 +174,14 @@ namespace Rimworld_Animations {
for (int i = 0; i < addons.Count; i++) for (int i = 0; i < addons.Count; i++)
{ {
AlienPartGenerator.BodyAddon ba = addons[index: i]; AlienPartGenerator.BodyAddon ba = addons[index: i];
if (!ba.CanDrawAddon(pawn: pawn)) continue; if (!ba.CanDrawAddon(pawn: pawn)) continue;
bool forceDrawForBody = false; bool forceDrawForBody = false;
if (alienProps.defName.Contains("Orassan") && ba.path.ToLower().Contains("tail")) if (alienProps.defName.Contains("Orassan") && ba.path.ToLower().Contains("tail"))
{ {
forceDrawForBody = true; forceDrawForBody = true;
} }
AlienPartGenerator.RotationOffset offset = ba.defaultOffsets.GetOffset((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing); AlienPartGenerator.RotationOffset offset = ba.defaultOffsets.GetOffset((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing);
Vector3 a = (offset != null) ? offset.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, pawn.story.headType) : Vector3.zero; Vector3 a = (offset != null) ? offset.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, pawn.story.headType) : Vector3.zero;
AlienPartGenerator.RotationOffset offset2 = ba.offsets.GetOffset((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing); AlienPartGenerator.RotationOffset offset2 = ba.offsets.GetOffset((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead ? pawnAnimator.headFacing : pawnAnimator.bodyFacing);
@ -97,37 +207,46 @@ namespace Rimworld_Animations {
if ((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead) if ((ba.drawnInBed && !forceDrawForBody) || ba.alignWithHead)
{ {
Quaternion addonRotation = Quaternion.AngleAxis(pawnAnimator.headAngle < 0 ? 360 - (360 % pawnAnimator.headAngle) : pawnAnimator.headAngle, Vector3.up); Quaternion addonRotation = Quaternion.AngleAxis(pawnAnimator.headAngle < 0 ? 360 - (360 % pawnAnimator.headAngle) : pawnAnimator.headAngle, Vector3.up);
GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: pawnAnimator.headFacing), loc: vector + (ba.alignWithHead ? headOffset : headOffset - addonRotation * pawn.Drawer.renderer.BaseHeadOffsetAt(pawnAnimator.headFacing)) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f), GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: pawnAnimator.headFacing), loc: vector + (ba.alignWithHead ? headOffset : headOffset - addonRotation * pawn.Drawer.renderer.BaseHeadOffsetAt(pawnAnimator.headFacing)) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f),
quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * addonRotation, mat: addonGraphic.MatAt(rot: pawnAnimator.headFacing), renderFlags.FlagSet(PawnRenderFlags.DrawNow)); quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * addonRotation, mat: addonGraphic.MatAt(rot: pawnAnimator.headFacing), renderFlags.FlagSet(PawnRenderFlags.DrawNow));
} }
else else
{ {
Quaternion addonRotation; Quaternion addonRotation;
if (AnimationSettings.controlGenitalRotation && ba.path.ToLower().Contains("penis")) if (AnimationSettings.controlGenitalRotation && ba.path.ToLower().Contains("penis"))
{ {
addonRotation = Quaternion.AngleAxis(pawnAnimator.genitalAngle, Vector3.up); addonRotation = Quaternion.AngleAxis(pawnAnimator.genitalAngle, Vector3.up);
} }
else else
{ {
addonRotation = Quaternion.AngleAxis(pawnAnimator.bodyAngle, Vector3.up); addonRotation = Quaternion.AngleAxis(pawnAnimator.bodyAngle, Vector3.up);
} }
if (AnimationSettings.controlGenitalRotation && pawnAnimator.controlGenitalAngle && ba?.hediffGraphics != null && ba.hediffGraphics.Count != 0 && ba.hediffGraphics[0]?.path != null && (ba.hediffGraphics[0].path.Contains("Penis") || ba.hediffGraphics[0].path.Contains("penis"))) if (AnimationSettings.controlGenitalRotation && pawnAnimator.controlGenitalAngle && ba?.hediffGraphics != null && ba.hediffGraphics.Count != 0 && ba.hediffGraphics[0]?.path != null && (ba.hediffGraphics[0].path.Contains("Penis") || ba.hediffGraphics[0].path.Contains("penis")))
{ {
GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: rotation), loc: vector + (ba.alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f), GenDraw.DrawMeshNowOrLater(
quat: Quaternion.AngleAxis(angle: pawnAnimator.genitalAngle, axis: Vector3.up), mat: addonGraphic.MatAt(rot: rotation), renderFlags.FlagSet(PawnRenderFlags.DrawNow)); mesh: addonGraphic.MeshAt(rot: rotation),
loc: vector + (ba.alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f),
quat: Quaternion.AngleAxis(angle: pawnAnimator.genitalAngle, axis: Vector3.up),
mat: addonGraphic.MatAt(rot: rotation),
drawNow: renderFlags.FlagSet(PawnRenderFlags.DrawNow)
);
} }
else else
{ {
GenDraw.DrawMeshNowOrLater(mesh: addonGraphic.MeshAt(rot: rotation), loc: vector + (ba.alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f), GenDraw.DrawMeshNowOrLater(
quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * addonRotation, mat: addonGraphic.MatAt(rot: rotation), renderFlags.FlagSet(PawnRenderFlags.DrawNow)); mesh: addonGraphic.MeshAt(rot: rotation),
loc: vector + (ba.alignWithHead ? headOffset : Vector3.zero) + vector2.RotatedBy(angle: Mathf.Acos(f: Quaternion.Dot(a: Quaternion.identity, b: addonRotation)) * 2f * 57.29578f),
quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * addonRotation,
mat: addonGraphic.MatAt(rot: rotation),
drawNow: renderFlags.FlagSet(PawnRenderFlags.DrawNow)
);
} }
} }
@ -143,20 +262,20 @@ namespace Rimworld_Animations {
/* /*
[HarmonyPatch(typeof(AlienRace.HarmonyPatches), "DrawAddons")] [HarmonyPatch(typeof(AlienRace.HarmonyPatches), "DrawAddons")]
public static class HarmonyPatch_AlienRace { public static class HarmonyPatch_AlienRace {
public static void RenderHeadAddonInAnimation(Mesh mesh, Vector3 loc, Quaternion quat, Material mat, bool drawNow, Graphic graphic, AlienPartGenerator.BodyAddon bodyAddon, Vector3 v, Vector3 headOffset, Pawn pawn, PawnRenderFlags renderFlags, Vector3 vector, Rot4 rotation) public static void RenderHeadAddonInAnimation(Mesh mesh, Vector3 loc, Quaternion quat, Material mat, bool drawNow, Graphic graphic, AlienPartGenerator.BodyAddon bodyAddon, Vector3 v, Vector3 headOffset, Pawn pawn, PawnRenderFlags renderFlags, Vector3 vector, Rot4 rotation)
{ {
CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>(); CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();
AlienPartGenerator.AlienComp comp = pawn.GetComp<AlienPartGenerator.AlienComp>(); AlienPartGenerator.AlienComp comp = pawn.GetComp<AlienPartGenerator.AlienComp>();
if (pawnAnimator != null && pawnAnimator.isAnimating) if (pawnAnimator != null && pawnAnimator.isAnimating)
{ {
if((bodyAddon.drawnInBed || bodyAddon.alignWithHead)) if((bodyAddon.drawnInBed || bodyAddon.alignWithHead))
{ {
AlienPartGenerator.RotationOffset offset = bodyAddon.defaultOffsets.GetOffset(rotation); AlienPartGenerator.RotationOffset offset = bodyAddon.defaultOffsets.GetOffset(rotation);
Vector3 a = (offset != null) ? offset.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, comp.crownType) : Vector3.zero; Vector3 a = (offset != null) ? offset.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, comp.crownType) : Vector3.zero;
@ -184,7 +303,7 @@ namespace Rimworld_Animations {
mat = graphic.MatAt(rot: pawnAnimator.headFacing); mat = graphic.MatAt(rot: pawnAnimator.headFacing);
} }
else else
{ {
AlienPartGenerator.RotationOffset offset = bodyAddon.defaultOffsets.GetOffset(rotation); AlienPartGenerator.RotationOffset offset = bodyAddon.defaultOffsets.GetOffset(rotation);
Vector3 a = (offset != null) ? offset.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, comp.crownType) : Vector3.zero; Vector3 a = (offset != null) ? offset.GetOffset(renderFlags.FlagSet(PawnRenderFlags.Portrait), pawn.story.bodyType, comp.crownType) : Vector3.zero;
@ -215,17 +334,17 @@ namespace Rimworld_Animations {
/* /*
if (pawnAnimator != null && !renderFlags.FlagSet(PawnRenderFlags.Portrait) && pawnAnimator.isAnimating && (bodyAddon.drawnInBed || bodyAddon.alignWithHead)) if (pawnAnimator != null && !renderFlags.FlagSet(PawnRenderFlags.Portrait) && pawnAnimator.isAnimating && (bodyAddon.drawnInBed || bodyAddon.alignWithHead))
{ {
if ((pawn.def as ThingDef_AlienRace).defName == "Alien_Orassan") if ((pawn.def as ThingDef_AlienRace).defName == "Alien_Orassan")
{ {
orassan = true; orassan = true;
if(bodyAddon.path.Contains("closed")) if(bodyAddon.path.Contains("closed"))
{ {
return; return;
} }
if (bodyAddon.bodyPart.Contains("ear")) if (bodyAddon.bodyPart.Contains("ear"))
@ -239,10 +358,10 @@ namespace Rimworld_Animations {
orassanv.y += 1f; orassanv.y += 1f;
if(bodyAddon.bodyPart.Contains("left")) if(bodyAddon.bodyPart.Contains("left"))
{ {
orassanv.x += 0.03f; orassanv.x += 0.03f;
} else } else
{ {
orassanv.x -= 0.03f; orassanv.x -= 0.03f;
} }
@ -256,7 +375,7 @@ namespace Rimworld_Animations {
orassanv.x = 0.1f; orassanv.x = 0.1f;
} }
else else
{ {
orassanv.z -= 0.1f; orassanv.z -= 0.1f;
orassanv.y += 1f; orassanv.y += 1f;
@ -282,7 +401,7 @@ namespace Rimworld_Animations {
} }
else else
{ {
} }
@ -291,16 +410,16 @@ namespace Rimworld_Animations {
public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{ {
List<CodeInstruction> ins = instructions.ToList(); List<CodeInstruction> ins = instructions.ToList();
for (int i = 0; i < ins.Count; i++) for (int i = 0; i < ins.Count; i++)
{ {
Type[] type = new Type[] { typeof(Mesh), typeof(Vector3), typeof(Quaternion), typeof(Material), typeof(bool) }; Type[] type = new Type[] { typeof(Mesh), typeof(Vector3), typeof(Quaternion), typeof(Material), typeof(bool) };
if (ins[i].OperandIs(AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater", type))) if (ins[i].OperandIs(AccessTools.Method(typeof(GenDraw), "DrawMeshNowOrLater", type)))
{ {
yield return new CodeInstruction(OpCodes.Ldloc, (object)7); //graphic yield return new CodeInstruction(OpCodes.Ldloc, (object)7); //graphic
yield return new CodeInstruction(OpCodes.Ldloc, (object)4); //bodyAddon yield return new CodeInstruction(OpCodes.Ldloc, (object)4); //bodyAddon
@ -313,28 +432,28 @@ namespace Rimworld_Animations {
yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(HarmonyPatch_AlienRace), "RenderHeadAddonInAnimation")); yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(HarmonyPatch_AlienRace), "RenderHeadAddonInAnimation"));
} }
else else
{ {
yield return ins[i]; yield return ins[i];
} }
} }
} }
public static bool Prefix(PawnRenderFlags renderFlags, ref Vector3 vector, ref Vector3 headOffset, Pawn pawn, ref Quaternion quat, ref Rot4 rotation) public static bool Prefix(PawnRenderFlags renderFlags, ref Vector3 vector, ref Vector3 headOffset, Pawn pawn, ref Quaternion quat, ref Rot4 rotation)
{ {
if(pawn == null) if(pawn == null)
{ {
return true; return true;
} }
CompBodyAnimator anim = pawn.TryGetComp<CompBodyAnimator>(); CompBodyAnimator anim = pawn.TryGetComp<CompBodyAnimator>();
if(anim == null) if(anim == null)
{ {
return true; return true;
} }
if (anim != null && !renderFlags.FlagSet(PawnRenderFlags.Portrait) && anim.isAnimating) if (anim != null && !renderFlags.FlagSet(PawnRenderFlags.Portrait) && anim.isAnimating)
{ {

View file

@ -0,0 +1,465 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<Patch_SexToysMasturbation.SexToyAnimationDef>
<defName>Masturbation_DildoVaginal</defName>
<label>dildo masturbation</label>
<requiredBodyPart>vagina</requiredBodyPart>
<sounds>true</sounds>
<sexTypes>
<li>Masturbation</li>
</sexTypes>
<actors>
<li>
<defNames>
<li>Human</li>
</defNames>
<isFucked>true</isFucked>
</li>
</actors>
<animationStages>
<li>
<stageName>Masturbating</stageName>
<isLooping>true</isLooping>
<playTimeTicks>917</playTimeTicks>
<stageIndex>0</stageIndex>
<animationClips>
<li Class="Rimworld_Animations.PawnAnimationClip">
<layer>LayingPawn</layer>
<keyframes>
<li>
<!--out-->
<tickDuration>40</tickDuration>
<bodyAngle>73.01611</bodyAngle>
<headAngle>40.0739746</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.054543376</bodyOffsetZ>
<bodyOffsetX>0.112624526</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<!--almost out-->
<soundEffect>Slimy</soundEffect>
<tickDuration>30</tickDuration>
<bodyAngle>76.4867554</bodyAngle>
<headAngle>45.3887634</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.0506898165</bodyOffsetZ>
<bodyOffsetX>0.08564949</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<!-- in -->
<tickDuration>30</tickDuration>
<bodyAngle>78.22131</bodyAngle>
<headAngle>48.0072327</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.039129138</bodyOffsetZ>
<bodyOffsetX>0.07794231</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<!--almost out-->
<tickDuration>30</tickDuration>
<bodyAngle>76.4867554</bodyAngle>
<headAngle>45.3887634</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.0506898165</bodyOffsetZ>
<bodyOffsetX>0.08564949</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<!--out-->
<tickDuration>1</tickDuration>
<bodyAngle>73.01611</bodyAngle>
<headAngle>40.0739746</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.054543376</bodyOffsetZ>
<bodyOffsetX>0.112624526</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
</keyframes>
</li>
<li Class="Rimworld_Animations.ThingAnimationClip">
<keyframes>
<li>
<!--out-->
<tickDuration>40</tickDuration>
<positionX>-0.359264076</positionX>
<positionZ>-0.00901746</positionZ>
<rotation>114.011215</rotation>
</li>
<li>
<!--almost out-->
<tickDuration>30</tickDuration>
<positionX>-0.2783391</positionX>
<positionZ>-0.0514066666</positionZ>
<rotation>81.16443</rotation>
</li>
<li>
<!--in-->
<tickDuration>30</tickDuration>
<positionX>-0.1704393</positionX>
<positionZ>-0.0668209046</positionZ>
<rotation>72.8611145</rotation>
</li>
<li>
<!--almost out-->
<tickDuration>30</tickDuration>
<positionX>-0.2783391</positionX>
<positionZ>-0.0514066666</positionZ>
<rotation>81.16443</rotation>
</li>
<li>
<!--out-->
<tickDuration>1</tickDuration>
<positionX>-0.359264076</positionX>
<positionZ>-0.00901746</positionZ>
<rotation>114.011215</rotation>
</li>
</keyframes>
</li>
</animationClips>
</li>
<li>
<stageName>GettingIntoPosition</stageName>
<isLooping>false</isLooping>
<stageIndex>0</stageIndex>
<animationClips>
<li Class="Rimworld_Animations.PawnAnimationClip">
<layer>LayingPawn</layer>
<keyframes>
<li>
<!--out-->
<tickDuration>50</tickDuration>
<bodyAngle>73.01611</bodyAngle>
<headAngle>40.0739746</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.054543376</bodyOffsetZ>
<bodyOffsetX>0.112624526</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<soundEffect>Slimy</soundEffect>
<tickDuration>1</tickDuration>
<bodyAngle>81.65927</bodyAngle>
<headAngle>58.8843079</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.03912908</bodyOffsetZ>
<bodyOffsetX>0.08950315</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
</keyframes>
</li>
<li Class="Rimworld_Animations.ThingAnimationClip">
<keyframes>
<li>
<!--out-->
<tickDuration>50</tickDuration>
<positionX>-0.359264076</positionX>
<positionZ>-0.00901746</positionZ>
<rotation>114.011215</rotation>
</li>
<li>
<!--out-->
<tickDuration>1</tickDuration>
<positionX>-0.2899</positionX>
<positionZ>-0.0282852575</positionZ>
<rotation>98.13748</rotation>
</li>
</keyframes>
</li>
</animationClips>
</li>
<li>
<stageName>FastMasturbation</stageName>
<isLooping>true</isLooping>
<stageIndex>0</stageIndex>
<playTimeTicks>1610</playTimeTicks>
<animationClips>
<li Class="Rimworld_Animations.PawnAnimationClip">
<layer>LayingPawn</layer>
<keyframes>
<li>
<tickDuration>20</tickDuration>
<bodyAngle>81.65927</bodyAngle>
<headAngle>58.8843079</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.03912908</bodyOffsetZ>
<bodyOffsetX>0.08950315</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<soundEffect>Slimy</soundEffect>
<tickDuration>25</tickDuration>
<bodyAngle>85.17255</bodyAngle>
<headAngle>58.0615845</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.03527552</bodyOffsetZ>
<bodyOffsetX>0.0471138731</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<tickDuration>1</tickDuration>
<bodyAngle>81.65927</bodyAngle>
<headAngle>58.8843079</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.03912908</bodyOffsetZ>
<bodyOffsetX>0.08950315</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
</keyframes>
</li>
<li Class="Rimworld_Animations.ThingAnimationClip">
<keyframes>
<li>
<!--out-->
<tickDuration>25</tickDuration>
<positionX>-0.2899</positionX>
<positionZ>-0.0282852575</positionZ>
<rotation>98.13748</rotation>
</li>
<li>
<!--out-->
<tickDuration>20</tickDuration>
<positionX>-0.178146541</positionX>
<positionZ>-0.01672452</positionZ>
<rotation>96.95889</rotation>
</li>
<li>
<!--out-->
<tickDuration>1</tickDuration>
<positionX>-0.2899</positionX>
<positionZ>-0.0282852575</positionZ>
<rotation>98.13748</rotation>
</li>
</keyframes>
</li>
</animationClips>
</li>
<li>
<stageName>VeryFastMasturbation</stageName>
<isLooping>true</isLooping>
<stageIndex>0</stageIndex>
<playTimeTicks>225</playTimeTicks>
<animationClips>
<li Class="Rimworld_Animations.PawnAnimationClip">
<layer>LayingPawn</layer>
<keyframes>
<li>
<tickDuration>6</tickDuration>
<bodyAngle>81.65927</bodyAngle>
<headAngle>58.8843079</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.03912908</bodyOffsetZ>
<bodyOffsetX>0.08950315</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<soundEffect>Slimy</soundEffect>
<tickDuration>8</tickDuration>
<bodyAngle>85.17255</bodyAngle>
<headAngle>58.0615845</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.03527552</bodyOffsetZ>
<bodyOffsetX>0.0471138731</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<tickDuration>1</tickDuration>
<bodyAngle>81.65927</bodyAngle>
<headAngle>58.8843079</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.03912908</bodyOffsetZ>
<bodyOffsetX>0.08950315</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
</keyframes>
</li>
<li Class="Rimworld_Animations.ThingAnimationClip">
<keyframes>
<li>
<!--out-->
<tickDuration>6</tickDuration>
<positionX>-0.2899</positionX>
<positionZ>-0.0282852575</positionZ>
<rotation>98.13748</rotation>
</li>
<li>
<!--out-->
<tickDuration>8</tickDuration>
<positionX>-0.178146541</positionX>
<positionZ>-0.01672452</positionZ>
<rotation>96.95889</rotation>
</li>
<li>
<!--out-->
<tickDuration>1</tickDuration>
<positionX>-0.2899</positionX>
<positionZ>-0.0282852575</positionZ>
<rotation>98.13748</rotation>
</li>
</keyframes>
</li>
</animationClips>
</li>
<li>
<stageName>Orgasm</stageName>
<isLooping>false</isLooping>
<stageIndex>0</stageIndex>
<animationClips>
<li Class="Rimworld_Animations.PawnAnimationClip">
<layer>LayingPawn</layer>
<keyframes>
<li>
<soundEffect>Slimy</soundEffect>
<tickDuration>6</tickDuration>
<bodyAngle>81.65927</bodyAngle>
<headAngle>58.8843079</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.03912908</bodyOffsetZ>
<bodyOffsetX>0.08950315</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<quiver>True</quiver>
<soundEffect>Cum</soundEffect>
<tickDuration>80</tickDuration>
<bodyAngle>85.17255</bodyAngle>
<headAngle>58.0615845</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.03527552</bodyOffsetZ>
<bodyOffsetX>0.0471138731</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<quiver>True</quiver>
<soundEffect>Cum</soundEffect>
<tickDuration>90</tickDuration>
<bodyAngle>92.15109</bodyAngle>
<headAngle>96.34238</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.0237147212</bodyOffsetZ>
<bodyOffsetX>0.0432603136</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<quiver>True</quiver>
<soundEffect>Cum</soundEffect>
<tickDuration>70</tickDuration>
<bodyAngle>92.15109</bodyAngle>
<headAngle>96.34238</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.0237147212</bodyOffsetZ>
<bodyOffsetX>0.0432603136</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<tickDuration>70</tickDuration>
<bodyAngle>92.15109</bodyAngle>
<headAngle>96.34238</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.0237147212</bodyOffsetZ>
<bodyOffsetX>0.0432603136</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
<li>
<tickDuration>1</tickDuration>
<bodyAngle>81.65927</bodyAngle>
<headAngle>58.8843079</headAngle>
<genitalAngle>0</genitalAngle>
<bodyOffsetZ>0.03912908</bodyOffsetZ>
<bodyOffsetX>0.08950315</bodyOffsetX>
<headBob>0</headBob>
<bodyFacing>3</bodyFacing>
<headFacing>3</headFacing>
</li>
</keyframes>
</li>
<li Class="Rimworld_Animations.ThingAnimationClip">
<keyframes>
<li>
<!--out-->
<tickDuration>6</tickDuration>
<positionX>-0.2899</positionX>
<positionZ>-0.0282852575</positionZ>
<rotation>98.13748</rotation>
</li>
<li>
<!--out-->
<tickDuration>80</tickDuration>
<positionX>-0.178146541</positionX>
<positionZ>-0.01672452</positionZ>
<rotation>96.95889</rotation>
</li>
<li>
<!--out-->
<tickDuration>90</tickDuration>
<positionX>-0.178146541</positionX>
<positionZ>-0.01672452</positionZ>
<rotation>96.95889</rotation>
</li>
<li>
<!--out-->
<tickDuration>70</tickDuration>
<positionX>-0.178146541</positionX>
<positionZ>-0.01672452</positionZ>
<rotation>96.95889</rotation>
</li>
<li>
<!--out-->
<tickDuration>70</tickDuration>
<positionX>-0.178146541</positionX>
<positionZ>-0.01672452</positionZ>
<rotation>96.95889</rotation>
</li>
<li>
<!--out-->
<tickDuration>1</tickDuration>
<positionX>-0.2899</positionX>
<positionZ>-0.0282852575</positionZ>
<rotation>98.13748</rotation>
</li>
</keyframes>
</li>
</animationClips>
</li>
</animationStages>
</Patch_SexToysMasturbation.SexToyAnimationDef>
</Defs>

View file

@ -83,6 +83,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="1.3\Defs\AnimationDefs\Animations_Dildo.xml" /> <Content Include="1.3\Defs\AnimationDefs\Animations_Dildo.xml" />
<Content Include="1.4\Defs\AnimationDefs\Animations_Dildo.xml" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>