using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("EndingKeepsakes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("EndingKeepsakes")]
[assembly: AssemblyTitle("EndingKeepsakes")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace EndingKeepsakes
{
internal sealed class AppearanceSnapshot : IDisposable
{
internal sealed class Piece
{
internal string Bone;
internal string Name;
internal Vector3 Position;
internal Vector3 Scale;
internal Quaternion Rotation;
internal Mesh Mesh;
internal Material[] Materials;
internal MaterialPropertyBlock Properties;
}
internal sealed class AttachResult
{
internal readonly List<GameObject> Roots = new List<GameObject>();
internal int Meshes;
internal int MissingBones;
}
private readonly List<Piece> pieces = new List<Piece>();
private readonly List<Object> owned = new List<Object>();
private int arrows;
private int thorns;
private int items;
private int backpacks;
private int ticks;
private int stickyItems;
internal string Summary => $"arrows={arrows}, thorns={thorns}, heldItems={items}, backpacks={backpacks}, ticks={ticks}, stickyItems={stickyItems}, meshes={pieces.Count}";
internal static AppearanceSnapshot Capture(Character character)
{
//IL_024d: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
//IL_0206: Unknown result type (might be due to invalid IL or missing references)
//IL_0135: Unknown result type (might be due to invalid IL or missing references)
//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
AppearanceSnapshot appearanceSnapshot = new AppearanceSnapshot();
try
{
HashSet<Transform> bones = new HashSet<Transform>(character.refs.mainRenderer.bones.Where((Transform b) => Object.op_Implicit((Object)(object)b)).ToArray());
Bodypart[] componentsInChildren = ((Component)character).GetComponentsInChildren<Bodypart>(true);
ThornOnMe[] componentsInChildren2 = ((Component)character).GetComponentsInChildren<ThornOnMe>(true);
if (Plugin.Instance.KeepThorns.Value)
{
foreach (ThornOnMe item in componentsInChildren2.Where((ThornOnMe t) => t.stuckIn))
{
Transform node = ((Component)item).transform;
Matrix4x4 world = ((Component)item).transform.localToWorldMatrix;
if (Plugin.Instance.thornOrigins.TryGetValue(item, out var value) && Object.op_Implicit((Object)(object)value.Parent))
{
node = value.Parent;
world = value.Parent.localToWorldMatrix * value.Local;
}
Transform val = FindAnchor(node, bones);
if (!Object.op_Implicit((Object)(object)val))
{
Plugin.Warn("No source bone for " + ((Object)item).name);
continue;
}
appearanceSnapshot.AddVisual(((Component)item).transform, val, world, forceRoot: true);
if (item.isArrow)
{
appearanceSnapshot.arrows++;
}
else
{
appearanceSnapshot.thorns++;
}
}
}
Bodypart val2 = ((IEnumerable<Bodypart>)componentsInChildren).FirstOrDefault((Func<Bodypart, bool>)((Bodypart p) => (int)p.partType == 10));
Item currentItem = character.data.currentItem;
if (Plugin.Instance.KeepHeldItem.Value && Object.op_Implicit((Object)(object)currentItem) && Object.op_Implicit((Object)(object)val2))
{
Transform val3 = FindAnchor(val2.transform, bones);
if (Object.op_Implicit((Object)(object)val3))
{
Matrix4x4 localToWorldMatrix = currentItem.transform.localToWorldMatrix;
appearanceSnapshot.AddVisual(currentItem.transform, val3, localToWorldMatrix, forceRoot: true);
appearanceSnapshot.items++;
}
}
if (Plugin.Instance.KeepBackpack.Value && (Object)(object)character.player != (Object)null && (int)character.player.backpackSlot.backpackType != 0 && (!character.refs.items.currentSelectedSlot.IsSome || character.refs.items.currentSelectedSlot.Value != 3))
{
BackpackOnBackVisuals activeBackpackVisuals = character.refs.backpackHandler.activeBackpackVisuals;
if (Object.op_Implicit((Object)(object)activeBackpackVisuals) && Object.op_Implicit((Object)(object)activeBackpackVisuals.view))
{
Transform val4 = FindAnchor(((Component)activeBackpackVisuals).transform, bones) ?? FindAnchor(character.refs.backpackTransform, bones);
if (Object.op_Implicit((Object)(object)val4))
{
appearanceSnapshot.AddVisual(((Component)activeBackpackVisuals).transform, val4, ((Component)activeBackpackVisuals).transform.localToWorldMatrix, forceRoot: true, activeBackpackVisuals.view.transform);
appearanceSnapshot.backpacks++;
}
}
}
appearanceSnapshot.CaptureAttachedObjects(character, componentsInChildren, bones);
return appearanceSnapshot;
}
catch
{
appearanceSnapshot.Dispose();
throw;
}
}
private void CaptureAttachedObjects(Character character, Bodypart[] bodyparts, HashSet<Transform> bones)
{
//IL_01df: Unknown result type (might be due to invalid IL or missing references)
//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
//IL_0275: Unknown result type (might be due to invalid IL or missing references)
if (Plugin.Instance.KeepTicks.Value)
{
Bodypart val = ((IEnumerable<Bodypart>)bodyparts).FirstOrDefault((Func<Bodypart, bool>)((Bodypart p) => (int)p.partType == 15));
KeyValuePair<Bugfix, Character>[] array = Bugfix.AllAttachedBugs.ToArray();
for (int num = 0; num < array.Length; num++)
{
KeyValuePair<Bugfix, Character> keyValuePair = array[num];
Bugfix key = keyValuePair.Key;
if (!Object.op_Implicit((Object)(object)key) || (Object)(object)keyValuePair.Value != (Object)(object)character || !((Component)key).gameObject.activeInHierarchy)
{
continue;
}
Transform val2 = (Object.op_Implicit((Object)(object)val) ? FindAnchor(val.transform, bones) : null);
if (!Object.op_Implicit((Object)(object)val2))
{
Plugin.Warn("No source leg bone for attached tick.");
continue;
}
try
{
int count = pieces.Count;
AddVisual(((Component)key).transform, val2, ((Component)key).transform.localToWorldMatrix, forceRoot: false);
if (pieces.Count > count)
{
ticks++;
}
}
catch (Exception ex)
{
Plugin.Warn("Could not capture one attached tick: " + ex);
}
}
}
if (!Plugin.Instance.KeepStickyItems.Value)
{
return;
}
HashSet<StickyItemComponent> hashSet = new HashSet<StickyItemComponent>();
StickyItemComponent[] array2 = StickyItemComponent.ALL_STUCK_ITEMS.ToArray();
foreach (StickyItemComponent sticky in array2)
{
if (!Object.op_Implicit((Object)(object)sticky) || !hashSet.Add(sticky) || (Object)(object)sticky.stuckToCharacter != (Object)(object)character || !((Component)sticky).gameObject.activeInHierarchy)
{
continue;
}
Item item = ((ItemComponent)sticky).item;
if (!Object.op_Implicit((Object)(object)item) || (int)item.itemState != 0 || (Object)(object)item == (Object)(object)character.data.currentItem)
{
continue;
}
Bodypart val3 = ((IEnumerable<Bodypart>)bodyparts).FirstOrDefault((Func<Bodypart, bool>)((Bodypart p) => p.partType == sticky.stuckToBodypart));
Transform val4 = (Object.op_Implicit((Object)(object)val3) ? FindAnchor(val3.transform, bones) : null);
if (!Object.op_Implicit((Object)(object)val4))
{
Plugin.Warn("No source bone for stuck item " + ((Object)sticky).name);
continue;
}
try
{
int count2 = pieces.Count;
AddVisual(item.transform, val4, item.transform.localToWorldMatrix, forceRoot: false);
if (pieces.Count > count2)
{
stickyItems++;
}
}
catch (Exception ex2)
{
Plugin.Warn("Could not capture one stuck item: " + ex2);
}
}
}
private static Transform FindAnchor(Transform node, HashSet<Transform> bones)
{
while (Object.op_Implicit((Object)(object)node))
{
if (bones.Contains(node))
{
return node;
}
node = node.parent;
}
return null;
}
private void AddVisual(Transform root, Transform anchor, Matrix4x4 world, bool forceRoot, Transform forceExtra = null)
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Invalid comparison between Unknown and I4
//IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Expected O, but got Unknown
//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
//IL_0103: Unknown result type (might be due to invalid IL or missing references)
//IL_0108: Unknown result type (might be due to invalid IL or missing references)
//IL_0109: Unknown result type (might be due to invalid IL or missing references)
//IL_010e: Unknown result type (might be due to invalid IL or missing references)
//IL_0110: Unknown result type (might be due to invalid IL or missing references)
//IL_0115: Unknown result type (might be due to invalid IL or missing references)
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_0131: Expected O, but got Unknown
//IL_0182: Unknown result type (might be due to invalid IL or missing references)
//IL_0187: Unknown result type (might be due to invalid IL or missing references)
//IL_018c: Unknown result type (might be due to invalid IL or missing references)
//IL_0194: Unknown result type (might be due to invalid IL or missing references)
//IL_0199: Unknown result type (might be due to invalid IL or missing references)
//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(true);
foreach (Renderer val in componentsInChildren)
{
if (!val.enabled || (int)val.shadowCastingMode == 3 || !VisibleUnder(((Component)val).transform, root, forceRoot, forceExtra))
{
continue;
}
Mesh val2;
if (val is MeshRenderer)
{
MeshFilter component = ((Component)val).GetComponent<MeshFilter>();
if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component.sharedMesh))
{
continue;
}
val2 = component.sharedMesh;
}
else
{
SkinnedMeshRenderer val3 = (SkinnedMeshRenderer)(object)((val is SkinnedMeshRenderer) ? val : null);
if (val3 == null || !Object.op_Implicit((Object)(object)val3.sharedMesh))
{
continue;
}
val2 = new Mesh
{
name = "KeepsakeBakedMesh"
};
owned.Add((Object)(object)val2);
val3.BakeMesh(val2);
}
Matrix4x4 val4 = Matrix4x4.identity;
Transform val5 = ((Component)val).transform;
while ((Object)(object)val5 != (Object)(object)root)
{
val4 = Matrix4x4.TRS(val5.localPosition, val5.localRotation, val5.localScale) * val4;
val5 = val5.parent;
}
Matrix4x4 val6 = anchor.worldToLocalMatrix * world * val4;
Material[] materials = CopyMaterials(val.sharedMaterials, owned);
MaterialPropertyBlock val7 = new MaterialPropertyBlock();
val.GetPropertyBlock(val7);
val7.SetFloat("_Interactable", 0f);
val7.SetFloat("_VertexGhost", 0f);
pieces.Add(new Piece
{
Name = ((Object)val).name,
Bone = ((Object)anchor).name,
Position = Vector4.op_Implicit(((Matrix4x4)(ref val6)).GetColumn(3)),
Rotation = ((Matrix4x4)(ref val6)).rotation,
Scale = ((Matrix4x4)(ref val6)).lossyScale,
Mesh = val2,
Materials = materials,
Properties = val7
});
}
}
private static Material[] CopyMaterials(Material[] source, List<Object> owned)
{
return ((IEnumerable<Material>)source).Select((Func<Material, Material>)delegate(Material material)
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Expected O, but got Unknown
if (!Object.op_Implicit((Object)(object)material))
{
return (Material)null;
}
Material val = new Material(material);
if (val.HasProperty("_Interactable"))
{
val.SetFloat("_Interactable", 0f);
}
if (val.HasProperty("_VertexGhost"))
{
val.SetFloat("_VertexGhost", 0f);
}
owned.Add((Object)(object)val);
return val;
}).ToArray();
}
private static bool VisibleUnder(Transform node, Transform root, bool forceRoot, Transform forceExtra = null)
{
while (Object.op_Implicit((Object)(object)node))
{
if (!((Component)node).gameObject.activeSelf && !((Object)(object)node == (Object)(object)root && forceRoot) && (Object)(object)node != (Object)(object)forceExtra)
{
return false;
}
if ((Object)(object)node == (Object)(object)root)
{
return true;
}
node = node.parent;
}
return false;
}
internal AttachResult Attach(Transform target, int layer)
{
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_010a: Unknown result type (might be due to invalid IL or missing references)
//IL_0113: Expected O, but got Unknown
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_013c: Unknown result type (might be due to invalid IL or missing references)
//IL_014e: Unknown result type (might be due to invalid IL or missing references)
AttachResult attachResult = new AttachResult();
Dictionary<string, Transform> dictionary = (from t in ((Component)target).GetComponentsInChildren<Transform>(true)
group t by ((Object)t).name into g
where g.Count() == 1
select g).ToDictionary((IGrouping<string, Transform> g) => g.Key, (IGrouping<string, Transform> g) => g.First());
foreach (Piece piece in pieces)
{
if (!dictionary.TryGetValue(piece.Bone, out var value))
{
attachResult.MissingBones++;
Plugin.Warn("Missing/ambiguous ending bone: " + piece.Bone + " for " + piece.Name);
continue;
}
GameObject val = new GameObject("Keepsake_" + piece.Name)
{
layer = layer
};
val.transform.SetParent(value, false);
val.transform.localPosition = piece.Position;
val.transform.localRotation = piece.Rotation;
val.transform.localScale = piece.Scale;
val.AddComponent<KeepsakeMarker>();
val.AddComponent<MeshFilter>().sharedMesh = piece.Mesh;
MeshRenderer obj = val.AddComponent<MeshRenderer>();
((Renderer)obj).sharedMaterials = piece.Materials;
((Renderer)obj).SetPropertyBlock(piece.Properties);
attachResult.Roots.Add(val);
attachResult.Meshes++;
}
return attachResult;
}
public void Dispose()
{
foreach (Object item in owned)
{
if (Object.op_Implicit(item))
{
Object.Destroy(item);
}
}
owned.Clear();
pieces.Clear();
}
}
[BepInPlugin("ritsukage.peak.endingkeepsakes", "Ending Keepsakes", "1.0.0")]
public sealed class Plugin : BaseUnityPlugin
{
[HarmonyPatch(typeof(PeakHandler), "PrepareEndCutscene")]
private static class CapturePatch
{
[HarmonyPrefix]
[HarmonyPriority(800)]
private static void Prefix()
{
try
{
Instance.CaptureAll();
}
catch (Exception ex)
{
Warn("Ending capture failed: " + ex);
}
}
}
[HarmonyPatch(typeof(PeakHandler), "SetCosmetic")]
private static class ApplyPatch
{
[HarmonyPostfix]
private static void Postfix(Character character, CustomizationRefs customizationRef)
{
try
{
Instance.Apply(character, customizationRef);
}
catch (Exception ex)
{
Warn("Ending attachment failed: " + ex);
}
}
}
[HarmonyPatch(typeof(CharacterAfflictions), "InitThorns")]
private static class ThornOriginPatch
{
[HarmonyPrefix]
private static void Prefix(CharacterAfflictions __instance)
{
try
{
ThornOnMe[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<ThornOnMe>(true);
foreach (ThornOnMe val in componentsInChildren)
{
if (!Instance.thornOrigins.ContainsKey(val))
{
Instance.thornOrigins.Add(val, new ThornOrigin(((Component)val).transform));
}
}
}
catch (Exception ex)
{
Warn("Could not record original thorn anchors: " + ex);
}
}
}
public const string Id = "ritsukage.peak.endingkeepsakes";
internal static Plugin Instance;
internal ConfigEntry<bool> KeepThorns;
internal ConfigEntry<bool> KeepHeldItem;
internal ConfigEntry<bool> KeepBackpack;
internal ConfigEntry<bool> KeepTicks;
internal ConfigEntry<bool> KeepStickyItems;
private ConfigEntry<bool> enabledSetting;
private Harmony harmony;
private readonly Dictionary<Character, AppearanceSnapshot> snapshots = new Dictionary<Character, AppearanceSnapshot>();
private readonly List<GameObject> attachments = new List<GameObject>();
internal readonly Dictionary<ThornOnMe, ThornOrigin> thornOrigins = new Dictionary<ThornOnMe, ThornOrigin>();
private void Awake()
{
//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
//IL_00dc: Expected O, but got Unknown
Instance = this;
enabledSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Preserve equipment visuals in the ending cinematic.");
KeepThorns = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "ArrowsAndThorns", true, "Show arrows and thorns still stuck in each character.");
KeepHeldItem = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "HeldItem", true, "Show the item held when the cinematic starts.");
KeepBackpack = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "Backpack", true, "Show the worn backpack and its existing item visuals.");
KeepTicks = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "Ticks", true, "Show attached ticks at their current size.");
KeepStickyItems = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "StickyItems", true, "Show cactus balls and other items stuck to the character.");
harmony = new Harmony("ritsukage.peak.endingkeepsakes");
harmony.PatchAll();
SceneManager.sceneUnloaded += OnSceneUnloaded;
((BaseUnityPlugin)this).Logger.LogInfo((object)"Ending Keepsakes 1.0.0 loaded. Ending appearance patches installed.");
}
internal void CaptureAll()
{
ClearEnding();
if (!enabledSetting.Value)
{
return;
}
Character[] array = Character.AllCharacters.ToArray();
foreach (Character val in array)
{
if (Object.op_Implicit((Object)(object)val))
{
try
{
AppearanceSnapshot appearanceSnapshot = AppearanceSnapshot.Capture(val);
snapshots.Add(val, appearanceSnapshot);
LogInfo($"Captured character {((Object)val).GetInstanceID()}: {appearanceSnapshot.Summary}");
}
catch (Exception ex)
{
Warn("Capture failed for one character: " + ex);
}
}
}
}
internal void Apply(Character character, CustomizationRefs target)
{
if (enabledSetting.Value && Object.op_Implicit((Object)(object)character) && Object.op_Implicit((Object)(object)target) && snapshots.TryGetValue(character, out var value))
{
KeepsakeMarker[] componentsInChildren = ((Component)target).GetComponentsInChildren<KeepsakeMarker>(true);
foreach (KeepsakeMarker obj in componentsInChildren)
{
((Component)obj).gameObject.SetActive(false);
Object.Destroy((Object)(object)((Component)obj).gameObject);
}
AppearanceSnapshot.AttachResult attachResult = value.Attach(((Component)target).transform, Object.op_Implicit((Object)(object)target.mainRenderer) ? ((Component)target.mainRenderer).gameObject.layer : ((Component)target).gameObject.layer);
attachments.AddRange(attachResult.Roots);
LogInfo($"Applied to {((Object)target).name}: meshes={attachResult.Meshes}, missingBones={attachResult.MissingBones}");
}
}
private void ClearEnding()
{
foreach (GameObject attachment in attachments)
{
if (Object.op_Implicit((Object)(object)attachment))
{
attachment.SetActive(false);
Object.Destroy((Object)(object)attachment);
}
}
attachments.Clear();
foreach (AppearanceSnapshot value in snapshots.Values)
{
value.Dispose();
}
snapshots.Clear();
}
private void OnSceneUnloaded(Scene scene)
{
ClearEnding();
ThornOnMe[] array = thornOrigins.Keys.Where((ThornOnMe t) => !Object.op_Implicit((Object)(object)t)).ToArray();
foreach (ThornOnMe key in array)
{
thornOrigins.Remove(key);
}
}
private void OnDestroy()
{
SceneManager.sceneUnloaded -= OnSceneUnloaded;
ClearEnding();
Harmony obj = harmony;
if (obj != null)
{
obj.UnpatchSelf();
}
if ((Object)(object)Instance == (Object)(object)this)
{
Instance = null;
}
}
internal static void LogInfo(string text)
{
((BaseUnityPlugin)Instance).Logger.LogInfo((object)text);
}
internal static void Warn(string text)
{
((BaseUnityPlugin)Instance).Logger.LogWarning((object)text);
}
}
public sealed class KeepsakeMarker : MonoBehaviour
{
}
internal sealed class ThornOrigin
{
internal readonly Transform Parent;
internal readonly Matrix4x4 Local;
internal ThornOrigin(Transform thorn)
{
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_001a: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
Parent = thorn.parent;
Local = Matrix4x4.TRS(thorn.localPosition, thorn.localRotation, thorn.localScale);
}
}
}