diff --git a/About/Manifest.xml b/About/Manifest.xml
index 99a4330..755ff76 100644
--- a/About/Manifest.xml
+++ b/About/Manifest.xml
@@ -1,5 +1,5 @@
Rimworld-Animations
- 1.3.3
+ 1.3.4
diff --git a/Patch_HumanoidAlienRaces/Patch_HumanoidAlienRaces.csproj b/Patch_HumanoidAlienRaces/Patch_HumanoidAlienRaces.csproj
index e34e96e..e495f73 100644
--- a/Patch_HumanoidAlienRaces/Patch_HumanoidAlienRaces.csproj
+++ b/Patch_HumanoidAlienRaces/Patch_HumanoidAlienRaces.csproj
@@ -1,4 +1,4 @@
-
+
@@ -12,6 +12,7 @@
v4.8
512
true
+ 9
false
diff --git a/Patch_HumanoidAlienRaces/Source/HarmonyPatch_AlienRace.cs b/Patch_HumanoidAlienRaces/Source/HarmonyPatch_AlienRace.cs
index 8ab0255..1d3e26c 100644
--- a/Patch_HumanoidAlienRaces/Source/HarmonyPatch_AlienRace.cs
+++ b/Patch_HumanoidAlienRaces/Source/HarmonyPatch_AlienRace.cs
@@ -6,48 +6,158 @@ using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading.Tasks;
+using System.Runtime.CompilerServices;
using UnityEngine;
using Verse;
using AlienRace;
-namespace Rimworld_Animations {
-
-
+namespace Rimworld_Animations
+{
[StaticConstructorOnStartup]
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()
{
- (new Harmony("rjwanim")).Patch(AccessTools.Method(AccessTools.TypeByName("AlienRace.HarmonyPatches"), "DrawAddons"),
- prefix: new HarmonyMethod(AccessTools.Method(typeof(HarmonyPatch_AlienRace), "Prefix_AnimateHeadAddons")));
+ AccessForPawn = null;
+ AccessForRotation = null;
+ InnerDrawAddon = AccessTools.FirstMethod(AlienRace_HarmonyPatches, (mi) =>
+ {
+ if (mi.GetCustomAttribute() 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 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)
- {
- CompBodyAnimator animator = pawn.TryGetComp();
+ public static bool ShouldForceDrawForBody(Pawn pawn, AlienPartGenerator.BodyAddon addon)
+ {
+ if (pawn.def is not ThingDef_AlienRace alienProps) return false;
- if (animator == null || !animator.isAnimating)
- {
- return;
- }
+ if (alienProps.defName.Contains("Orassan") && addon.path.ToLower().Contains("tail"))
+ return true;
- if(addon.alignWithHead || addon.drawnInBed)
- {
- rot = animator.headFacing;
+ return false;
+ }
+
+ // 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() 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() is not { } animator) return;
+ if (!animator.isAnimating) return;
+
+ var forceDrawForBody = ShouldForceDrawForBody(pawn, addon);
+
+ if ((addon.drawnInBed && !forceDrawForBody) || addon.alignWithHead)
+ {
angle = animator.headAngle;
- offsetVector += animator.deltaPos + animator.bodyAngle * animator.headBob;
-
+ offsetVector += animator.deltaPos + animator.headBob;
}
else
- {
- rot = animator.bodyFacing;
- angle = animator.bodyAngle;
+ {
+ if (AnimationSettings.controlGenitalRotation && addon.path.ToLower().Contains("penis"))
+ angle = animator.genitalAngle;
+ else
+ angle = animator.bodyAngle;
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)
{
@@ -64,14 +174,14 @@ namespace Rimworld_Animations {
for (int i = 0; i < addons.Count; i++)
{
AlienPartGenerator.BodyAddon ba = addons[index: i];
-
+
if (!ba.CanDrawAddon(pawn: pawn)) continue;
bool forceDrawForBody = false;
if (alienProps.defName.Contains("Orassan") && ba.path.ToLower().Contains("tail"))
- {
+ {
forceDrawForBody = true;
- }
+ }
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;
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)
{
-
+
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),
quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * addonRotation, mat: addonGraphic.MatAt(rot: pawnAnimator.headFacing), renderFlags.FlagSet(PawnRenderFlags.DrawNow));
-
+
}
else
{
Quaternion addonRotation;
if (AnimationSettings.controlGenitalRotation && ba.path.ToLower().Contains("penis"))
- {
+ {
addonRotation = Quaternion.AngleAxis(pawnAnimator.genitalAngle, Vector3.up);
}
else
- {
+ {
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")))
{
- 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),
- quat: Quaternion.AngleAxis(angle: pawnAnimator.genitalAngle, axis: Vector3.up), mat: addonGraphic.MatAt(rot: rotation), renderFlags.FlagSet(PawnRenderFlags.DrawNow));
+ 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),
+ quat: Quaternion.AngleAxis(angle: pawnAnimator.genitalAngle, axis: Vector3.up),
+ mat: addonGraphic.MatAt(rot: rotation),
+ drawNow: renderFlags.FlagSet(PawnRenderFlags.DrawNow)
+ );
}
-
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),
- quat: Quaternion.AngleAxis(angle: num, axis: Vector3.up) * addonRotation, mat: addonGraphic.MatAt(rot: rotation), renderFlags.FlagSet(PawnRenderFlags.DrawNow));
+ {
+ 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),
+ 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")]
- public static class HarmonyPatch_AlienRace {
+ [HarmonyPatch(typeof(AlienRace.HarmonyPatches), "DrawAddons")]
+ 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)
- {
+ {
CompBodyAnimator pawnAnimator = pawn.TryGetComp();
AlienPartGenerator.AlienComp comp = pawn.GetComp();
if (pawnAnimator != null && pawnAnimator.isAnimating)
- {
+ {
if((bodyAddon.drawnInBed || bodyAddon.alignWithHead))
- {
+ {
AlienPartGenerator.RotationOffset offset = bodyAddon.defaultOffsets.GetOffset(rotation);
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);
}
else
- {
+ {
AlienPartGenerator.RotationOffset offset = bodyAddon.defaultOffsets.GetOffset(rotation);
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 ((pawn.def as ThingDef_AlienRace).defName == "Alien_Orassan")
- {
+ {
orassan = true;
if(bodyAddon.path.Contains("closed"))
- {
+ {
return;
- }
+ }
if (bodyAddon.bodyPart.Contains("ear"))
@@ -239,10 +358,10 @@ namespace Rimworld_Animations {
orassanv.y += 1f;
if(bodyAddon.bodyPart.Contains("left"))
- {
+ {
orassanv.x += 0.03f;
- } else
- {
+ } else
+ {
orassanv.x -= 0.03f;
}
@@ -256,7 +375,7 @@ namespace Rimworld_Animations {
orassanv.x = 0.1f;
}
else
- {
+ {
orassanv.z -= 0.1f;
orassanv.y += 1f;
@@ -282,7 +401,7 @@ namespace Rimworld_Animations {
}
else
- {
+ {
}
@@ -291,16 +410,16 @@ namespace Rimworld_Animations {
public static IEnumerable Transpiler(IEnumerable instructions)
- {
+ {
List ins = instructions.ToList();
for (int i = 0; i < ins.Count; i++)
- {
+ {
Type[] type = new Type[] { typeof(Mesh), typeof(Vector3), typeof(Quaternion), typeof(Material), typeof(bool) };
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)4); //bodyAddon
@@ -313,28 +432,28 @@ namespace Rimworld_Animations {
yield return new CodeInstruction(OpCodes.Call, AccessTools.DeclaredMethod(typeof(HarmonyPatch_AlienRace), "RenderHeadAddonInAnimation"));
- }
+ }
else
- {
+ {
yield return ins[i];
}
- }
- }
+ }
+ }
public static bool Prefix(PawnRenderFlags renderFlags, ref Vector3 vector, ref Vector3 headOffset, Pawn pawn, ref Quaternion quat, ref Rot4 rotation)
{
if(pawn == null)
- {
+ {
return true;
}
CompBodyAnimator anim = pawn.TryGetComp();
if(anim == null)
- {
+ {
return true;
- }
+ }
if (anim != null && !renderFlags.FlagSet(PawnRenderFlags.Portrait) && anim.isAnimating)
{