From ef81b715058c0ea5ebe7ccb6914c6c602bb10aff Mon Sep 17 00:00:00 2001 From: James Billy Date: Sun, 14 Apr 2024 18:30:29 +0200 Subject: [PATCH] Done... --- Defs/Jobs.xml | 2 +- .../AMRetextureSupport.csproj | 2 +- Source/AlienRacesPatch/AlienRacesPatch.csproj | 2 +- Source/AnimationMod.sln | 7 + Source/AnimationMod/AnimRenderer.cs | 33 ++-- Source/AnimationMod/AnimationMod.csproj | 4 +- .../AutoDuel/AutoFriendlyDuelMapComp.cs | 10 +- .../AutoDuel/JoyGiver_FriendlyDuel.cs | 4 +- Source/AnimationMod/GameComp.cs | 1 - Source/AnimationMod/Grappling/GrappleFlyer.cs | 4 +- Source/AnimationMod/Heads/HeadInstance.cs | 15 +- Source/AnimationMod/Outcome/OutcomeUtility.cs | 3 +- .../Patches/Patch_Corpse_DrawAt.cs | 32 ++-- .../Patches/Patch_HediffSet_HasHead.cs | 22 +++ .../Patch_PawnRenderer_LayingFacing.cs | 31 ---- .../Patch_PawnRenderer_RenderPawnAt.cs | 117 ++++++++++++-- .../Patch_PawnRenderer_RenderPawnInternal.cs | 148 ------------------ .../Patches/Patch_PawnUtility_IsInvisible.cs | 4 +- Source/CAI5000Patch/CAI5000Patch.csproj | 4 +- .../CAI5000Patch/FixCustomRenderInAnimator.cs | 6 +- .../CombatExtendedPatch.csproj | 4 +- .../FacialAnimationPatch - Backup.csproj | 13 ++ .../FacialAnimationPatch.csproj | 51 ++++++ Source/FacialAnimationPatch/PatchCore.cs | 23 +++ ...ch_DrawFaceGraphicsComp_CompRenderNodes.cs | 44 ++++++ .../refs/FacialAnimation.dll | Bin 0 -> 87552 bytes Source/LightsaberPatch/LightsaberPatch.csproj | 4 +- Source/ModRequestAPI/ModRequestAPI.csproj | 2 +- .../PerformanceOptimizerPatch.csproj | 4 +- Source/TacticowlPatch/TacticowlPatch.csproj | 2 +- 30 files changed, 348 insertions(+), 250 deletions(-) create mode 100644 Source/AnimationMod/Patches/Patch_HediffSet_HasHead.cs delete mode 100644 Source/AnimationMod/Patches/Patch_PawnRenderer_LayingFacing.cs delete mode 100644 Source/AnimationMod/Patches/Patch_PawnRenderer_RenderPawnInternal.cs create mode 100644 Source/FacialAnimationPatch/FacialAnimationPatch - Backup.csproj create mode 100644 Source/FacialAnimationPatch/FacialAnimationPatch.csproj create mode 100644 Source/FacialAnimationPatch/PatchCore.cs create mode 100644 Source/FacialAnimationPatch/Patch_DrawFaceGraphicsComp_CompRenderNodes.cs create mode 100644 Source/FacialAnimationPatch/refs/FacialAnimation.dll 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 0000000000000000000000000000000000000000..76264684d8ed449407c971590f7973d9bd2878ef GIT binary patch literal 87552 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 - + 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 - +