diff --git a/Defs/Jobs.xml b/Defs/Jobs.xml
index 97ae1715..76987b62 100644
--- a/Defs/Jobs.xml
+++ b/Defs/Jobs.xml
@@ -47,7 +47,7 @@
false
true
false
- false
+ true
false
true
false
diff --git a/Source/AMRetextureSupport/AMRetextureSupport.csproj b/Source/AMRetextureSupport/AMRetextureSupport.csproj
index d384917f..feb2b0c9 100644
--- a/Source/AMRetextureSupport/AMRetextureSupport.csproj
+++ b/Source/AMRetextureSupport/AMRetextureSupport.csproj
@@ -19,7 +19,7 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Source/AlienRacesPatch/AlienRacesPatch.csproj b/Source/AlienRacesPatch/AlienRacesPatch.csproj
index 36d97e45..009d98cf 100644
--- a/Source/AlienRacesPatch/AlienRacesPatch.csproj
+++ b/Source/AlienRacesPatch/AlienRacesPatch.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/Source/AnimationMod.sln b/Source/AnimationMod.sln
index 579d805a..c7a658a3 100644
--- a/Source/AnimationMod.sln
+++ b/Source/AnimationMod.sln
@@ -30,6 +30,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Patches", "Patches", "{7C1C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TacticowlPatch", "TacticowlPatch\TacticowlPatch.csproj", "{DEC2223C-6DD4-48EF-9438-8F4E97B6C542}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FacialAnimationPatch", "FacialAnimationPatch\FacialAnimationPatch.csproj", "{B5AC984E-8B51-426B-9F54-CC85006449F1}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -84,6 +86,10 @@ Global
{DEC2223C-6DD4-48EF-9438-8F4E97B6C542}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DEC2223C-6DD4-48EF-9438-8F4E97B6C542}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DEC2223C-6DD4-48EF-9438-8F4E97B6C542}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B5AC984E-8B51-426B-9F54-CC85006449F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B5AC984E-8B51-426B-9F54-CC85006449F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B5AC984E-8B51-426B-9F54-CC85006449F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B5AC984E-8B51-426B-9F54-CC85006449F1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -98,6 +104,7 @@ Global
{06C481CC-E8D0-46D6-B02B-127DD9E3A1AA} = {3549402C-9759-4ABC-938C-FC4F801C156F}
{7B881968-1527-40FC-9FB3-85A5A01BEE88} = {7C1CE274-810E-467E-B8EF-4AB2A83E17F9}
{DEC2223C-6DD4-48EF-9438-8F4E97B6C542} = {7C1CE274-810E-467E-B8EF-4AB2A83E17F9}
+ {B5AC984E-8B51-426B-9F54-CC85006449F1} = {7C1CE274-810E-467E-B8EF-4AB2A83E17F9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1478B48A-1088-4408-9427-E3B4EADB515C}
diff --git a/Source/AnimationMod/AnimRenderer.cs b/Source/AnimationMod/AnimRenderer.cs
index a4738a98..016c03a8 100644
--- a/Source/AnimationMod/AnimRenderer.cs
+++ b/Source/AnimationMod/AnimRenderer.cs
@@ -27,8 +27,8 @@ public class AnimRenderer : IExposable
public static readonly char[] Alphabet = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H' };
public static readonly List PostLoadPendingAnimators = new List();
- public static event Action PrePawnSpecialRender;
- public static event Action PostPawnSpecialRender;
+ public static Action PrePawnSpecialRender;
+ public static Action PostPawnSpecialRender;
public static Material DefaultCutout, DefaultTransparent;
public static IReadOnlyList ActiveRenderers => activeRenderers;
public static IReadOnlyCollection CapturedPawns => pawnToRenderer.Keys;
@@ -909,15 +909,16 @@ protected void DrawPawns(Action labelDraw = null)
{
// Regular pawn render.
Patch_PawnRenderer_RenderPawnAt.AllowNext = true;
- Patch_PawnRenderer_RenderPawnInternal.AllowNext = true;
- Patch_PawnRenderer_RenderPawnInternal.DoNotModify = true; // Don't use animation position/rotation.
- Patch_PawnRenderer_RenderPawnInternal.NextDrawMode = Patch_PawnRenderer_RenderPawnInternal.DrawMode.Full;
- PrePawnSpecialRender?.Invoke(pawn, this);
+ Patch_PawnRenderer_RenderPawnAt.DoNotModify = true; // Don't use animation position/rotation.
+ Patch_PawnRenderer_RenderPawnAt.NextDrawMode = Patch_PawnRenderer_RenderPawnAt.DrawMode.Full;
+ Patch_PawnUtility_IsInvisible.IsRendering = true;
+ PrePawnSpecialRender?.Invoke(pawn, this, Map);
pawn.DrawNowAt(pawn.DrawPosHeld ?? pawn.DrawPos);
- PostPawnSpecialRender?.Invoke(pawn, this);
- Patch_PawnRenderer_RenderPawnInternal.DoNotModify = false;
+ PostPawnSpecialRender?.Invoke(pawn, this, Map);
+ Patch_PawnRenderer_RenderPawnAt.DoNotModify = false;
+ Patch_PawnUtility_IsInvisible.IsRendering = false;
// Draw label.
Vector3 drawPos2 = pawn.DrawPos;
@@ -959,24 +960,26 @@ protected void DrawPawns(Action labelDraw = null)
// Head rotation needs to be sent manually because the head
// does not have a direction curve to sample.
- Patch_PawnRenderer_RenderPawnInternal.HeadRotation = dir; // Copy body facing direction.
+ Patch_PawnRenderer_RenderPawnAt.HeadRotation = dir; // Copy body facing direction.
}
// Render pawn in custom position using patches.
Patch_PawnRenderer_RenderPawnAt.AllowNext = true;
- Patch_PawnRenderer_RenderPawnInternal.AllowNext = true;
- Patch_PawnRenderer_RenderPawnInternal.NextDrawMode = !isBeheaded
- ? Patch_PawnRenderer_RenderPawnInternal.DrawMode.Full
- : i == 0 ? Patch_PawnRenderer_RenderPawnInternal.DrawMode.BodyOnly : Patch_PawnRenderer_RenderPawnInternal.DrawMode.HeadOnly;
+ Patch_PawnRenderer_RenderPawnAt.NextDrawMode = !isBeheaded
+ ? Patch_PawnRenderer_RenderPawnAt.DrawMode.Full
+ : i == 0 ? Patch_PawnRenderer_RenderPawnAt.DrawMode.BodyOnly : Patch_PawnRenderer_RenderPawnAt.DrawMode.HeadOnly;
Patch_PawnRenderer_DrawShadowInternal.Suppress = suppressShadow; // In 1.4 shadow rendering is baked into RenderPawnAt and may need to be prevented.
- PrePawnSpecialRender?.Invoke(pawn, this);
+ Patch_PawnUtility_IsInvisible.IsRendering = true;
+
+ PrePawnSpecialRender?.Invoke(pawn, this, Map);
pawn.Drawer.renderer.RenderPawnAt(pos, dir, true); // This direction here is not the final one.
- PostPawnSpecialRender?.Invoke(pawn, this);
+ PostPawnSpecialRender?.Invoke(pawn, this, Map);
Patch_PawnRenderer_DrawShadowInternal.Suppress = false;
+ Patch_PawnUtility_IsInvisible.IsRendering = false;
}
// Render shadow.
diff --git a/Source/AnimationMod/AnimationMod.csproj b/Source/AnimationMod/AnimationMod.csproj
index a8c3c8e3..8d8624ae 100644
--- a/Source/AnimationMod/AnimationMod.csproj
+++ b/Source/AnimationMod/AnimationMod.csproj
@@ -26,14 +26,14 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
runtime
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Source/AnimationMod/AutoDuel/AutoFriendlyDuelMapComp.cs b/Source/AnimationMod/AutoDuel/AutoFriendlyDuelMapComp.cs
index e26f46e1..641ef47e 100644
--- a/Source/AnimationMod/AutoDuel/AutoFriendlyDuelMapComp.cs
+++ b/Source/AnimationMod/AutoDuel/AutoFriendlyDuelMapComp.cs
@@ -155,7 +155,15 @@ public static bool CanPawnDuel(Pawn pawn)
return false;
// Check pawn is in recreation and not doing anything majorly important.
- if (pawn.timetable.CurrentAssignment != TimeAssignmentDefOf.Joy || !(pawn.CurJobDef?.playerInterruptible ?? true))
+ bool currentTimetableAllowsJob = pawn.timetable.CurrentAssignment.allowJoy;
+ bool currentTimetableIsSleep = pawn.timetable.CurrentAssignment == TimeAssignmentDefOf.Sleep;
+ if (currentTimetableIsSleep || !currentTimetableAllowsJob)
+ return false;
+
+ bool canInterruptJob = pawn.CurJobDef?.playerInterruptible ?? true;
+ bool isJobPlayerForced = pawn.CurJob?.playerForced ?? false;
+
+ if (!canInterruptJob || isJobPlayerForced)
return false;
return true;
diff --git a/Source/AnimationMod/AutoDuel/JoyGiver_FriendlyDuel.cs b/Source/AnimationMod/AutoDuel/JoyGiver_FriendlyDuel.cs
index 4c257ba2..5f4a79b2 100644
--- a/Source/AnimationMod/AutoDuel/JoyGiver_FriendlyDuel.cs
+++ b/Source/AnimationMod/AutoDuel/JoyGiver_FriendlyDuel.cs
@@ -27,12 +27,12 @@ public override Job TryGiveJob(Pawn pawn)
if (!AutoFriendlyDuelMapComp.CanPawnDuel(pawn))
return null;
- // Try get a duel partner.
+ // Try to get a duel partner.
var partner = comp.TryGetRandomDuelPartner(pawn);
if (partner == null)
return null;
- // Try get a duel spot for us two:
+ // Try to get a duel spot for us two:
var spot = comp.TryGetBestDuelSpotFor(pawn, partner);
if (spot == null)
return null;
diff --git a/Source/AnimationMod/GameComp.cs b/Source/AnimationMod/GameComp.cs
index 89eb5135..0950912e 100644
--- a/Source/AnimationMod/GameComp.cs
+++ b/Source/AnimationMod/GameComp.cs
@@ -113,7 +113,6 @@ public override void GameComponentTick()
GrabUtility.Tick();
Patch_Corpse_DrawAt.Tick();
- Patch_PawnRenderer_LayingFacing.Tick();
const float DT = 1 / 60f;
diff --git a/Source/AnimationMod/Grappling/GrappleFlyer.cs b/Source/AnimationMod/Grappling/GrappleFlyer.cs
index de457165..b6465ba0 100644
--- a/Source/AnimationMod/Grappling/GrappleFlyer.cs
+++ b/Source/AnimationMod/Grappling/GrappleFlyer.cs
@@ -89,7 +89,7 @@ public override void SpawnSetup(Map map, bool respawningAfterLoad)
ticksFlightTime = Mathf.Max(2, (int)(ticksFlightTime / (speed * Core.Settings.GrappleSpeed)));
}
- private void RecomputePosition()
+ private new void RecomputePosition()
{
if (this.positionLastComputedTick == this.ticksFlying)
{
@@ -158,7 +158,7 @@ public static void DrawBoundTexture(Pawn pawn, Vector3 drawLoc, Color ropeColor)
Graphics.DrawMesh(MeshPool.plane10, trs, mat, 0, null, 0, mpb);
}
- private void DrawShadow(Vector3 drawLoc, float height)
+ private new void DrawShadow(Vector3 drawLoc, float height)
{
Material shadowMaterial = this.ShadowMaterial;
if (shadowMaterial == null)
diff --git a/Source/AnimationMod/Heads/HeadInstance.cs b/Source/AnimationMod/Heads/HeadInstance.cs
index fedc995a..6d69354d 100644
--- a/Source/AnimationMod/Heads/HeadInstance.cs
+++ b/Source/AnimationMod/Heads/HeadInstance.cs
@@ -33,19 +33,24 @@ public bool Render()
return true;
//Render pawn in custom position using patches.
- Patch_PawnRenderer_RenderPawnInternal.NextDrawMode = Patch_PawnRenderer_RenderPawnInternal.DrawMode.HeadStandalone;
- Patch_PawnRenderer_RenderPawnInternal.HeadRotation = Direction;
- Patch_PawnRenderer_RenderPawnInternal.StandaloneHeadRotation = Rotation;
+ Patch_PawnRenderer_RenderPawnAt.NextDrawMode = Patch_PawnRenderer_RenderPawnAt.DrawMode.HeadStandalone;
+ Patch_PawnRenderer_RenderPawnAt.HeadRotation = Direction;
+ Patch_PawnRenderer_RenderPawnAt.StandaloneHeadAngle = Rotation;
+ Patch_PawnRenderer_RenderPawnAt.StandaloneHeadPosition = Position;
+ Patch_PawnRenderer_RenderPawnAt.AllowNext = true;
Patch_PawnRenderer_DrawShadowInternal.Suppress = true; // In 1.4 shadow rendering is baked into RenderPawnAt and may need to be prevented.
- Patch_PawnRenderer_RenderPawnInternal.AllowNext = true;
try
{
+ AnimRenderer.PrePawnSpecialRender?.Invoke(Pawn, null, Map);
+
Pawn.Drawer.renderer.RenderPawnAt(Position, Direction, true);
+
+ AnimRenderer.PostPawnSpecialRender?.Invoke(Pawn, null, Map);
}
finally
{
- Patch_PawnRenderer_RenderPawnInternal.NextDrawMode = Patch_PawnRenderer_RenderPawnInternal.DrawMode.Full;
+ Patch_PawnRenderer_RenderPawnAt.NextDrawMode = Patch_PawnRenderer_RenderPawnAt.DrawMode.Full;
Patch_PawnRenderer_DrawShadowInternal.Suppress = false;
}
return true;
diff --git a/Source/AnimationMod/Outcome/OutcomeUtility.cs b/Source/AnimationMod/Outcome/OutcomeUtility.cs
index 4c783181..7e4f660b 100644
--- a/Source/AnimationMod/Outcome/OutcomeUtility.cs
+++ b/Source/AnimationMod/Outcome/OutcomeUtility.cs
@@ -501,8 +501,7 @@ private static bool Kill(Pawn killer, Pawn pawn, in AdditionalArgs args)
{
// Do corpse interpolation - interpolates the corpse to the correct position, after the animated position.
Patch_Corpse_DrawAt.Interpolators[pawn.Corpse] = new CorpseInterpolate(pawn.Corpse, ss.GetWorldPosition());
-
- Patch_PawnRenderer_LayingFacing.OverrideRotations[pawn] = ss.GetWorldDirection();
+ Patch_Corpse_DrawAt.OverrideRotations[pawn] = ss.GetWorldDirection();
}
else if (!isDeathless)
{
diff --git a/Source/AnimationMod/Patches/Patch_Corpse_DrawAt.cs b/Source/AnimationMod/Patches/Patch_Corpse_DrawAt.cs
index d0f52d5c..9835d707 100644
--- a/Source/AnimationMod/Patches/Patch_Corpse_DrawAt.cs
+++ b/Source/AnimationMod/Patches/Patch_Corpse_DrawAt.cs
@@ -7,37 +7,50 @@
namespace AM.Patches;
-
-[HarmonyPatch(typeof(ThingWithComps), nameof(ThingWithComps.DrawAt))] // Corpse DrawAt was removed in 1.5
+[HarmonyPatch(typeof(PawnRenderer), nameof(PawnRenderer.ParallelPreRenderPawnAt))] // Corpse DrawAt was removed in 1.5
public static class Patch_Corpse_DrawAt
{
public static readonly Dictionary Interpolators = new Dictionary();
+ public static readonly Dictionary OverrideRotations = new Dictionary();
public static void Tick()
{
if(GenTicks.TicksGame % (60 * 30) == 0)
+ {
Interpolators.RemoveAll(p => !p.Key.Spawned);
+ OverrideRotations.RemoveAll(p => !p.Key.SpawnedOrAnyParentSpawned);
+ }
}
- [HarmonyPriority(Priority.First)]
- private static void Prefix(ThingWithComps __instance, ref Vector3 drawLoc)
+ [HarmonyPriority(Priority.Last)]
+ private static void Prefix(PawnRenderer __instance, ref Vector3 drawLoc, ref Rot4? rotOverride)
{
- if (__instance is not Corpse corpse)
+ var corpse = __instance.pawn.ParentHolder as Corpse;
+
+ if (corpse == null)
return;
- DoOffsetLogic(corpse, ref drawLoc);
+ DoOffsetLogic(corpse, ref drawLoc, ref rotOverride);
}
- private static void DoOffsetLogic(Corpse __instance, ref Vector3 drawLoc)
+ private static void DoOffsetLogic(Corpse corpse, ref Vector3 drawLoc, ref Rot4? rotOverride)
{
+ // Override facing direction of corpse:
+ if (OverrideRotations.TryGetValue(corpse.InnerPawn, out var foundRot))
+ {
+ rotOverride = foundRot;
+ }
+
+ // Override position and rotation of corpse...
+
if (Core.Settings.CorpseOffsetMode == CorpseOffsetMode.None)
return;
- if (!Interpolators.TryGetValue(__instance, out var found))
+ if (!Interpolators.TryGetValue(corpse, out var found))
return;
if (!found.Update(ref drawLoc))
- Interpolators.Remove(__instance);
+ Interpolators.Remove(corpse);
}
}
@@ -53,6 +66,7 @@ public class CorpseInterpolate
public CorpseInterpolate(Corpse corpse, Vector3 startPos)
{
TargetPosition = corpse.DrawPos;
+ startPos.y = TargetPosition.y;
InitialOffset = startPos - TargetPosition;
CurrentPosition = startPos;
}
diff --git a/Source/AnimationMod/Patches/Patch_HediffSet_HasHead.cs b/Source/AnimationMod/Patches/Patch_HediffSet_HasHead.cs
new file mode 100644
index 00000000..b2b863b0
--- /dev/null
+++ b/Source/AnimationMod/Patches/Patch_HediffSet_HasHead.cs
@@ -0,0 +1,22 @@
+using HarmonyLib;
+using Verse;
+
+namespace AM.Patches;
+
+[HarmonyPatch(typeof(HediffSet), nameof(HediffSet.HasHead), MethodType.Getter)]
+public static class Patch_HediffSet_HasHead
+{
+ public static bool? ForcedHasHeadValue;
+
+ [HarmonyPriority(Priority.First)]
+ public static bool Prefix(ref bool __result)
+ {
+ if (ForcedHasHeadValue != null)
+ {
+ __result = ForcedHasHeadValue.Value;
+ return false;
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/Source/AnimationMod/Patches/Patch_PawnRenderer_LayingFacing.cs b/Source/AnimationMod/Patches/Patch_PawnRenderer_LayingFacing.cs
deleted file mode 100644
index a6be1a15..00000000
--- a/Source/AnimationMod/Patches/Patch_PawnRenderer_LayingFacing.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using System.Collections.Generic;
-using HarmonyLib;
-using Verse;
-
-namespace AM.Patches;
-
-///
-/// Only used to override corpse direction.
-///
-[HarmonyPatch(typeof(PawnRenderer), nameof(PawnRenderer.LayingFacing))]
-public static class Patch_PawnRenderer_LayingFacing
-{
- public static readonly Dictionary OverrideRotations = new Dictionary();
-
- public static void Tick()
- {
- if (GenTicks.TicksGame % (60 * 30) == 0)
- OverrideRotations.RemoveAll(p => !p.Key.SpawnedOrAnyParentSpawned);
- }
-
- static void Postfix(Pawn ___pawn, ref Rot4 __result)
- {
- if (!___pawn.Dead)
- return;
-
- if (!OverrideRotations.TryGetValue(___pawn, out var found))
- return;
-
- __result = found;
- }
-}
\ No newline at end of file
diff --git a/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnAt.cs b/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnAt.cs
index 90015703..2b09b5be 100644
--- a/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnAt.cs
+++ b/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnAt.cs
@@ -1,11 +1,13 @@
using AM.Grappling;
using HarmonyLib;
+using RimWorld;
using UnityEngine;
using Verse;
namespace AM.Patches;
///
+/// This is the 'main' patch that actually gets pawns rendering in animations.
/// When a pawn is being animated, RenderPawnAt needs to be modified:
/// first, the private field 'results' needs to be modified in order to ensure that RenderPawnDynamic is called.
/// Next, call with the arguments that I want in order to modify how the pawn will actually be displayed.
@@ -14,6 +16,11 @@ namespace AM.Patches;
public static class Patch_PawnRenderer_RenderPawnAt
{
public static bool AllowNext;
+ public static bool DoNotModify = false;
+ public static DrawMode NextDrawMode = DrawMode.Full;
+ public static Rot4 HeadRotation; // Only used when NextDrawMode is HeadOnly or HeadStandalone.
+ public static float StandaloneHeadAngle;
+ public static Vector3 StandaloneHeadPosition;
[HarmonyPriority(Priority.First)]
public static bool Prefix(Pawn ___pawn, PawnRenderTree ___renderTree, ref PawnRenderer.PreRenderResults ___results, ref PawnRenderer.PreRenderResults? __state)
@@ -23,11 +30,16 @@ public static bool Prefix(Pawn ___pawn, PawnRenderTree ___renderTree, ref PawnRe
// Draw the ropes that bind the pawn when being grappled.
DrawGrappleRopeIfRequired(___pawn);
- // Try to get an active animator for this pawn.
- var animator = PatchMaster.GetAnimator(___pawn);
- if (animator == null)
+ // Standalone heads don't have or need animator.
+ AnimRenderer animator = null;
+ if (NextDrawMode != DrawMode.HeadStandalone)
{
- return true;
+ // Try to get an active animator for this pawn.
+ animator = PatchMaster.GetAnimator(___pawn);
+ if (animator == null)
+ {
+ return true;
+ }
}
// Ok, the pawn is in an active animation, but we want to suppress the regular draw call
@@ -57,8 +69,19 @@ public static bool Prefix(Pawn ___pawn, PawnRenderTree ___renderTree, ref PawnRe
// Most importantly, need to call ParallelPreRender on the renderTree to actually
// set up all the matrices and whatnot that gets used when renderTree draw is called.
+
+ // Because of the way that the stump and head renderer work, it can be necessary to force the IsHead property
+ // to return a certain value when it suits us.
+ if (NextDrawMode == DrawMode.HeadStandalone)
+ Patch_HediffSet_HasHead.ForcedHasHeadValue = true;
+
+ // Force RenderTree to recalculate with the new parameters.
+ ___renderTree.SetDirty();
+ ___renderTree.EnsureInitialized(___results.parms.flags);
___renderTree.ParallelPreDraw(___results.parms);
+ Patch_HediffSet_HasHead.ForcedHasHeadValue = null;
+
// Do run the regular RenderPawnAt method now.
return true;
}
@@ -83,31 +106,97 @@ private static void DrawGrappleRopeIfRequired(Pawn pawn)
}
}
- public static void MakeDrawArgs(AnimRenderer animator, Pawn pawn, ref PawnDrawParms parms)
+ private static void MakeDrawArgs(AnimRenderer animator, Pawn pawn, ref PawnDrawParms parms)
{
- // Try to get the pawn snapshot.
- var part = animator.GetPawnBody(pawn);
- var snapshot = animator.GetSnapshot(part);
+ // During certain special animations, the pawn is being animated by the regular draw is still
+ // used, such as the punt animation.
+ if (DoNotModify)
+ {
+ // However, still remove the invisible flag as it is not wanted.
+ parms.flags &= ~PawnRenderFlags.Invisible;
+ return;
+ }
- // Dead pawns should not be animated, but make sure that it is not dead just in case.
- parms.dead = false;
+ // Standalone heads get their own method.
+ if (NextDrawMode == DrawMode.HeadStandalone)
+ {
+ // It seems that pawn is not populated when the pawn is dead?
+ parms.pawn = pawn;
+ MakeDrawArgsForStandaloneHead(ref parms);
+ return;
+ }
+
+ // Try to get the pawn body or head snapshot, depending on the current mode.
+ var part = NextDrawMode == DrawMode.HeadOnly
+ ? animator.GetPawnHead(pawn)
+ : animator.GetPawnBody(pawn);
+ var snapshot = animator.GetSnapshot(part);
// Debug:
- parms.tint = Color.magenta;
+ //parms.tint = Color.magenta;
// Must be standing.
- parms.posture = RimWorld.PawnPosture.Standing;
+ parms.posture = PawnPosture.Standing;
// Remove invisible flag.
parms.flags &= ~PawnRenderFlags.Invisible;
// Set facing direction:
- parms.facing = snapshot.GetWorldDirection();
-
+ var facing = NextDrawMode == DrawMode.HeadOnly
+ ? HeadRotation
+ : snapshot.GetWorldDirection();
+ parms.facing = facing;
+
// New transform matrix calculation:
Vector3 worldPos = snapshot.GetWorldPosition();
float worldAngle = snapshot.GetWorldRotation();
Vector3 scale = snapshot.LocalScale; // Just use local part as scale, not currently used anyway but might be useful in future.
parms.matrix = Matrix4x4.TRS(worldPos + pawn.ageTracker.CurLifeStage.bodyDrawOffset, Quaternion.Euler(0, worldAngle, 0), scale);
+
+ // Some additional flags depending on the rendering mode:
+ switch (NextDrawMode)
+ {
+ case DrawMode.BodyOnly:
+ // When rendering a headless body, add a head stump.
+ // Note: do not skip the head using a flag, because that causes the stump to not be rendered.
+ parms.flags |= PawnRenderFlags.HeadStump;
+
+ // TODO known issue: head stump does not render because the render tree node checks for
+ // the presence of a head before drawing the stump, regardless of the flag. Fix!
+
+ break;
+
+ case DrawMode.HeadOnly:
+ // Don't render the body.
+ parms.skipFlags |= AM_DefOf.Body;
+ break;
+ }
+ }
+
+ private static void MakeDrawArgsForStandaloneHead(ref PawnDrawParms parms)
+ {
+ parms.skipFlags |= AM_DefOf.Body; // Don't draw the body, just the head.
+ parms.posture = PawnPosture.Standing; // Don't use the wrong mesh set.
+ parms.flipHead = false; // Don't flip the head round.
+ //parms.dead = false;
+ parms.tint = Color.white; // Don't tint transparent. Idk why it gets set to (0, 0, 0, 0) upon death, but it does...
+ parms.rotDrawMode = RotDrawMode.Fresh; // Force the head to not rot.
+ StandaloneHeadPosition.y = AltitudeLayer.Building.AltitudeFor(); // Force the head to be put on the ground, should be done elsewhere, but I'm lazy.
+
+ // Always render headgear and clothes, do not render head stump (head stump prevents head from drawing).
+ parms.flags |= PawnRenderFlags.Headgear | PawnRenderFlags.Clothes;
+ parms.flags &= ~PawnRenderFlags.HeadStump;
+
+ // Set correct position and angle for head.
+ parms.facing = HeadRotation;
+ parms.matrix = Matrix4x4.TRS(StandaloneHeadPosition, Quaternion.Euler(0, StandaloneHeadAngle, 0), Vector3.one);
+ }
+
+ public enum DrawMode
+ {
+ Full,
+ BodyOnly,
+ HeadOnly,
+ HeadStandalone
}
}
diff --git a/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnInternal.cs b/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnInternal.cs
deleted file mode 100644
index 7d6a72ff..00000000
--- a/Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnInternal.cs
+++ /dev/null
@@ -1,148 +0,0 @@
-using System;
-using HarmonyLib;
-using RimWorld;
-using UnityEngine;
-using Verse;
-
-namespace AM.Patches;
-
-///
-/// Overrides various parameters of the pawn rendering, specifically direction (north, east etc.)
-/// and body angle. Driven by the active animation.
-/// Also used to render severed heads.
-///
-//[HarmonyPatch(typeof(PawnRenderer), nameof(PawnRenderer.RenderPawnInternal))]
-static class Patch_PawnRenderer_RenderPawnInternal
-{
- /*
- * Steps to ensure that I can render exactly how I want:
- */
-
- public static bool AllowNext;
- public static bool DoNotModify = false;
- public static DrawMode NextDrawMode = DrawMode.Full;
- public static Rot4 HeadRotation; // Only used when NextDrawMode is HeadOnly or HeadStandalone.
-
- public static float StandaloneHeadRotation;
-
- public enum DrawMode
- {
- Full,
- BodyOnly,
- HeadOnly,
- HeadStandalone
- }
-
- [HarmonyPriority(Priority.Last)] // As late as possible. We want to be the last to modify results.
- [HarmonyAfter("com.yayo.yayoAni")] // Go away.
- [HarmonyBefore("rimworld.Nals.FacialAnimation")] // Must go before facial animation otherwise the face gets fucky.
- private static bool Prefix(PawnRenderer __instance, Pawn ___pawn, ref PawnDrawParms parms)
- {
- //parms.matrix
-
- __instance.renderTree.ParallelPreDraw(parms);
-
- return true;
-
- // Do not modify when the result will be stored in cache.
- if (parms.Cache)
- return true;
-
- float angle = parms.matrix.rotation.eulerAngles.y;
- float oldAngle = angle;
-
- bool renderBody = !parms.skipFlags.HasFlag(AM_DefOf.Body);
-
- ref Rot4 bodyFacing = ref parms.facing;
- ref PawnRenderFlags flags = ref parms.flags;
-
- if (AllowNext)
- {
- //parms.matrix *= Matrix4x4.Scale(0.5f * Vector3.one);
- //__instance.renderTree.SetDirty();
- //__instance.EnsureGraphicsInitialized();
- //parms.coveredInFoam = true;
- AllowNext = false;
- return true;
- }
-
-
- return false;
-
- bool result = ModifyRenderData(___pawn, ref bodyFacing, ref angle, ref flags, ref renderBody);
-
- if (renderBody)
- parms.skipFlags |= AM_DefOf.Body;
- else
- parms.skipFlags &= ~AM_DefOf.Body;
-
- float delta = oldAngle - angle;
- if (Math.Abs(delta) > 0.001f)
- {
- parms.matrix *= Matrix4x4.Rotate(Quaternion.Euler(45, delta, 0));
- }
-
- return result;
- }
-
- public static bool ModifyRenderData(Pawn ___pawn, ref Rot4 bodyFacing, ref float angle, ref PawnRenderFlags flags, ref bool renderBody)
- {
- // Do not affect portrait rendering:
- if (flags.HasFlag(PawnRenderFlags.Portrait))
- return true;
-
- // Standalone head (i.e. dropped head on ground after animation) gets a custom method that does things slightly differently.
- if (NextDrawMode == DrawMode.HeadStandalone)
- return RenderStandaloneHeadMode(ref bodyFacing, ref flags, ref angle, ref renderBody);
-
- // Get the animator for this pawn.
- var anim = PatchMaster.GetAnimator(___pawn);
- if (anim != null)
- {
- if (!DoNotModify)
- {
- //var part = NextDrawMode == DrawMode.HeadOnly ? anim.GetPawnHead(___pawn) : anim.GetPawnBody(___pawn);
- //var snapshot = anim.GetSnapshot(part);
- //angle = snapshot.GetWorldRotation();
-
- angle = 5;
- bodyFacing = Rot4.East;
- renderBody = false;
-
- //bodyFacing = NextDrawMode == DrawMode.HeadOnly ? HeadRotation : snapshot.GetWorldDirection();
-
- //switch (NextDrawMode)
- //{
- // case DrawMode.BodyOnly:
- // // Render head stump, do not render head gear.
- // flags |= PawnRenderFlags.HeadStump;
- // flags &= ~PawnRenderFlags.Headgear;
- // break;
-
- // case DrawMode.HeadOnly:
- // // Do not render body.
- // renderBody = false;
- // break;
- //}
- }
-
- if (!AllowNext)
- return false;
- }
-
- AllowNext = false;
- return true;
- }
-
- private static bool RenderStandaloneHeadMode(ref Rot4 bodyFacing, ref PawnRenderFlags flags, ref float angle, ref bool renderBody)
- {
- // Add headgear, remove head stump.
- flags |= PawnRenderFlags.Headgear | PawnRenderFlags.DrawNow;
- flags &= ~PawnRenderFlags.HeadStump;
-
- angle = StandaloneHeadRotation;
- bodyFacing = HeadRotation;
- renderBody = false;
- return true;
- }
-}
diff --git a/Source/AnimationMod/Patches/Patch_PawnUtility_IsInvisible.cs b/Source/AnimationMod/Patches/Patch_PawnUtility_IsInvisible.cs
index a626ab15..2e0c0c27 100644
--- a/Source/AnimationMod/Patches/Patch_PawnUtility_IsInvisible.cs
+++ b/Source/AnimationMod/Patches/Patch_PawnUtility_IsInvisible.cs
@@ -16,6 +16,8 @@ namespace AM.Patches;
[HarmonyPatch(typeof(InvisibilityUtility), nameof(InvisibilityUtility.IsPsychologicallyInvisible))]
public static class Patch_PawnUtility_IsInvisible
{
+ public static bool IsRendering;
+
[HarmonyPriority(Priority.First)]
public static bool Prefix(Pawn pawn, ref bool __result)
{
@@ -23,7 +25,7 @@ public static bool Prefix(Pawn pawn, ref bool __result)
return true;
var anim = PatchMaster.GetAnimator(pawn);
- if (anim != null)
+ if (anim != null && !IsRendering)
{
__result = true;
return false;
diff --git a/Source/CAI5000Patch/CAI5000Patch.csproj b/Source/CAI5000Patch/CAI5000Patch.csproj
index 562a1483..b0f4a007 100644
--- a/Source/CAI5000Patch/CAI5000Patch.csproj
+++ b/Source/CAI5000Patch/CAI5000Patch.csproj
@@ -21,14 +21,14 @@
False
all
-
+
runtime
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/Source/CAI5000Patch/FixCustomRenderInAnimator.cs b/Source/CAI5000Patch/FixCustomRenderInAnimator.cs
index 3faeb147..766d60d8 100644
--- a/Source/CAI5000Patch/FixCustomRenderInAnimator.cs
+++ b/Source/CAI5000Patch/FixCustomRenderInAnimator.cs
@@ -17,10 +17,8 @@ namespace AM.CAI5000Patch;
*/
public static class FixCustomRenderInAnimator
{
- public static void PreCustomPawnRender(Pawn pawn, AnimRenderer anim)
+ public static void PreCustomPawnRender(Pawn pawn, AnimRenderer anim, Map map)
{
- var map = anim.Map;
-
// This is CAI code:
if (Pawn_Patch.fogThings == null || Pawn_Patch.fogThings.map != map)
{
@@ -28,7 +26,7 @@ public static void PreCustomPawnRender(Pawn pawn, AnimRenderer anim)
}
}
- public static void PostCustomPawnRender(Pawn pawn, AnimRenderer anim)
+ public static void PostCustomPawnRender(Pawn pawn, AnimRenderer anim, Map map)
{
// CAI always sets fogThings to null after the map has rendered each frame.
// I do not know why, probably to make sure it can be GC'd once the player exits to the main menu.
diff --git a/Source/CombatExtendedPatch/CombatExtendedPatch.csproj b/Source/CombatExtendedPatch/CombatExtendedPatch.csproj
index d49ea916..8f592b76 100644
--- a/Source/CombatExtendedPatch/CombatExtendedPatch.csproj
+++ b/Source/CombatExtendedPatch/CombatExtendedPatch.csproj
@@ -16,7 +16,7 @@
-
+
runtime
@@ -28,7 +28,7 @@
False
all
-
+
diff --git a/Source/FacialAnimationPatch/FacialAnimationPatch - Backup.csproj b/Source/FacialAnimationPatch/FacialAnimationPatch - Backup.csproj
new file mode 100644
index 00000000..38dda25e
--- /dev/null
+++ b/Source/FacialAnimationPatch/FacialAnimationPatch - Backup.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/Source/FacialAnimationPatch/FacialAnimationPatch.csproj b/Source/FacialAnimationPatch/FacialAnimationPatch.csproj
new file mode 100644
index 00000000..257a6836
--- /dev/null
+++ b/Source/FacialAnimationPatch/FacialAnimationPatch.csproj
@@ -0,0 +1,51 @@
+
+
+
+ net48
+ Library
+ false
+ false
+ preview
+ false
+ false
+ AM.FacialAnimationPatch
+ AM.FacialAnimationPatch
+
+
+
+
+
+ runtime
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+ False
+ False
+ all
+
+
+
+
+
+ refs\FacialAnimation.dll
+ False
+ False
+ runtime
+
+
+
+
+ none
+ ..\..\Patch_FacialAnimation\$(RimworldVersion)\Assemblies\
+ true
+ TRACE
+
+
diff --git a/Source/FacialAnimationPatch/PatchCore.cs b/Source/FacialAnimationPatch/PatchCore.cs
new file mode 100644
index 00000000..47e313be
--- /dev/null
+++ b/Source/FacialAnimationPatch/PatchCore.cs
@@ -0,0 +1,23 @@
+using HarmonyLib;
+using JetBrains.Annotations;
+using Verse;
+
+namespace AM.FacialAnimationPatch;
+
+[UsedImplicitly]
+[HotSwapAll]
+public sealed class PatchCore : Mod
+{
+ public static void Log(string msg)
+ {
+ Core.Log($"[Facial Animation Patch] {msg}");
+ }
+
+ public PatchCore(ModContentPack content) : base(content)
+ {
+ var harmony = new Harmony(content.Name);
+ harmony.PatchAll();
+
+ Log("Successfully loaded Facial Animation patch.");
+ }
+}
\ No newline at end of file
diff --git a/Source/FacialAnimationPatch/Patch_DrawFaceGraphicsComp_CompRenderNodes.cs b/Source/FacialAnimationPatch/Patch_DrawFaceGraphicsComp_CompRenderNodes.cs
new file mode 100644
index 00000000..16c0a69a
--- /dev/null
+++ b/Source/FacialAnimationPatch/Patch_DrawFaceGraphicsComp_CompRenderNodes.cs
@@ -0,0 +1,44 @@
+using AM.Patches;
+using FacialAnimation;
+using HarmonyLib;
+
+namespace AM.FacialAnimationPatch;
+
+///
+/// DrawFaceGraphicsComp.CompRenderNodes draws all of the facial animation stuff, including a replacement head.
+/// The pawns are considered invisible inside animations, and facial animation will use the 'invisible'
+/// shader if it detects this.
+/// This patch makes it so that during the facial animation rendering, the patch to make pawns be considered invisible is disabled.
+///
+[HarmonyPatch(typeof(DrawFaceGraphicsComp), nameof(DrawFaceGraphicsComp.CompRenderNodes))]
+public static class Patch_DrawFaceGraphicsComp_CompRenderNodes
+{
+ private static void Prefix(DrawFaceGraphicsComp __instance, ref bool __state)
+ {
+ __state = false;
+
+ // Don't bother checking if invisible pawns are not enabled.
+ if (!Core.Settings.AllowInvisiblePawns)
+ return;
+
+ var pawn = __instance.pawn;
+ if (pawn == null)
+ return;
+
+ var animator = pawn.TryGetAnimator();
+ if (animator != null)
+ {
+ __state = true;
+ __instance.SetDirty();
+ Patch_PawnUtility_IsInvisible.IsRendering = true;
+ }
+ }
+
+ private static void Postfix(bool __state)
+ {
+ if (__state)
+ {
+ Patch_PawnUtility_IsInvisible.IsRendering = false;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Source/FacialAnimationPatch/refs/FacialAnimation.dll b/Source/FacialAnimationPatch/refs/FacialAnimation.dll
new file mode 100644
index 00000000..76264684
Binary files /dev/null and b/Source/FacialAnimationPatch/refs/FacialAnimation.dll differ
diff --git a/Source/LightsaberPatch/LightsaberPatch.csproj b/Source/LightsaberPatch/LightsaberPatch.csproj
index bbf91bc2..55b10a0e 100644
--- a/Source/LightsaberPatch/LightsaberPatch.csproj
+++ b/Source/LightsaberPatch/LightsaberPatch.csproj
@@ -13,7 +13,7 @@
-
+
runtime
@@ -21,7 +21,7 @@
False
all
-
+
diff --git a/Source/ModRequestAPI/ModRequestAPI.csproj b/Source/ModRequestAPI/ModRequestAPI.csproj
index 60f3d390..8737ecad 100644
--- a/Source/ModRequestAPI/ModRequestAPI.csproj
+++ b/Source/ModRequestAPI/ModRequestAPI.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/Source/PerformanceOptimizerPatch/PerformanceOptimizerPatch.csproj b/Source/PerformanceOptimizerPatch/PerformanceOptimizerPatch.csproj
index ba34e89c..6200b194 100644
--- a/Source/PerformanceOptimizerPatch/PerformanceOptimizerPatch.csproj
+++ b/Source/PerformanceOptimizerPatch/PerformanceOptimizerPatch.csproj
@@ -16,7 +16,7 @@
-
+
runtime
@@ -29,7 +29,7 @@
all
-
+
diff --git a/Source/TacticowlPatch/TacticowlPatch.csproj b/Source/TacticowlPatch/TacticowlPatch.csproj
index 77e71ccf..b6299fed 100644
--- a/Source/TacticowlPatch/TacticowlPatch.csproj
+++ b/Source/TacticowlPatch/TacticowlPatch.csproj
@@ -21,7 +21,7 @@
False
all
-
+