using System; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BombRushMP.Common.Networking; using BombRushMP.Plugin; using Reptile; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.0.0.0")] namespace BRCPluginOnlyAfterimages; [BepInPlugin("com.brc.afterimage3clone", "After Images", "1.1.7")] public sealed class Plugin : BaseUnityPlugin { private struct RemotePresence { public bool Enabled; public float LastSeen; public RemotePresence(bool enabled, float lastSeen) { Enabled = enabled; LastSeen = lastSeen; } } private struct SyncedColors { public Color c1; public Color c2; public Color c3; public SyncedColors(Color a, Color b, Color c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) c1 = a; c2 = b; c3 = c; } } private const string AcnPacketId = "AfterImages.ColorSync.v1"; private const string PresencePacketId = "AfterImages.Presence.v1"; private const ushort UnknownOwnerId = ushort.MaxValue; private const float PresenceTimeoutSeconds = 8f; internal static Plugin Instance; internal static ConfigEntry Enabled; internal static ConfigEntry ShowOtherPlayersAfterimages; internal static ConfigEntry Clone1Delay; internal static ConfigEntry Clone2Delay; internal static ConfigEntry Clone3Delay; internal static ConfigEntry MinimumVisibleDistance; internal static ConfigEntry MinimumVisibleDistanceHysteresis; internal static ConfigEntry SampleInterval; internal static ConfigEntry CloneUpdateInterval; internal static ConfigEntry PositionSmoothing; internal static ConfigEntry RotationSmoothing; internal static ConfigEntry CopyScale; internal static ConfigEntry CopyBlendShapes; internal static ConfigEntry Clone1Color; internal static ConfigEntry Clone2Color; internal static ConfigEntry Clone3Color; internal static ConfigEntry RainbowMode; internal static ConfigEntry RainbowSpeed; internal static ConfigEntry RainbowSaturation; internal static ConfigEntry MirrorRendererToggles; internal static ConfigEntry MirrorRendererActiveState; internal static ConfigEntry MirrorMeshChanges; internal static ConfigEntry ParticleSystemsVisible; internal static ConfigEntry TrailRenderersVisible; internal static ConfigEntry ScanInterval; internal static ConfigEntry DisableCloneShadows; internal static ConfigEntry CloneUpdateWhenOffscreen; private readonly Dictionary drivers = new Dictionary(); private readonly Dictionary remoteColors = new Dictionary(); private readonly Dictionary remotePresence = new Dictionary(); private float nextScan; private float nextColorBroadcast; private float nextPresenceBroadcast; private Color lastSent1 = new Color(-1f, -1f, -1f, -1f); private Color lastSent2 = new Color(-1f, -1f, -1f, -1f); private Color lastSent3 = new Color(-1f, -1f, -1f, -1f); private void Awake() { //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03ab: Expected O, but got Unknown //IL_03df: Unknown result type (might be due to invalid IL or missing references) //IL_03e9: Expected O, but got Unknown Instance = this; Enabled = ((BaseUnityPlugin)this).Config.Bind("General", "Enabled", true, "Master toggle for runtime afterimages."); ShowOtherPlayersAfterimages = ((BaseUnityPlugin)this).Config.Bind("Multiplayer", "ShowOtherPlayersAfterimages", true, "Show afterimage clones for other ACN players. Turn this OFF to keep your own afterimages while completely removing remote players' clone rigs for better multiplayer FPS."); Clone1Delay = ((BaseUnityPlugin)this).Config.Bind("Clones", "Clone1Delay", 0.05f, ""); Clone2Delay = ((BaseUnityPlugin)this).Config.Bind("Clones", "Clone2Delay", 0.1f, ""); Clone3Delay = ((BaseUnityPlugin)this).Config.Bind("Clones", "Clone3Delay", 0.15f, ""); MinimumVisibleDistance = ((BaseUnityPlugin)this).Config.Bind("Clones", "MinimumVisibleDistance", 0.08f, "Hide an afterimage while its delayed world position is closer than this many meters to the live character. Prevents overlapping/clipping ghosts while standing still or moving very slowly."); MinimumVisibleDistanceHysteresis = ((BaseUnityPlugin)this).Config.Bind("Clones", "MinimumVisibleDistanceHysteresis", 0.03f, "Extra distance a hidden clone must travel before becoming visible again. Prevents rapid on/off flicker around MinimumVisibleDistance. Example: with 0.08 minimum and 0.03 hysteresis, a visible clone hides below 0.08m and stays hidden until it reaches 0.11m."); SampleInterval = ((BaseUnityPlugin)this).Config.Bind("Pose History", "SampleInterval", 0f, "0 = capture pose every rendered frame (recommended). Higher values cap sampling."); CloneUpdateInterval = ((BaseUnityPlugin)this).Config.Bind("Performance", "CloneUpdateInterval", 0f, "0 = update all three afterimages every rendered frame (recommended)."); PositionSmoothing = ((BaseUnityPlugin)this).Config.Bind("Smoothing", "PositionSmoothing", 16f, ""); RotationSmoothing = ((BaseUnityPlugin)this).Config.Bind("Smoothing", "RotationSmoothing", 16f, ""); CopyScale = ((BaseUnityPlugin)this).Config.Bind("Smoothing", "CopyScale", true, ""); CopyBlendShapes = ((BaseUnityPlugin)this).Config.Bind("Performance", "CopyBlendShapes", false, "Copies blendshape weights from the live CBB. Leave OFF for best FPS."); DisableCloneShadows = ((BaseUnityPlugin)this).Config.Bind("Performance", "DisableCloneShadows", true, ""); CloneUpdateWhenOffscreen = ((BaseUnityPlugin)this).Config.Bind("Performance", "CloneUpdateWhenOffscreen", false, ""); MirrorRendererToggles = ((BaseUnityPlugin)this).Config.Bind("Skin Switch / Toggles", "MirrorRendererToggles", true, "Mirrors source SkinnedMeshRenderer.enabled onto all 3 runtime clones."); MirrorRendererActiveState = ((BaseUnityPlugin)this).Config.Bind("Skin Switch / Toggles", "MirrorRendererActiveState", true, "Hides a clone mesh when the matching source mesh object is inactive."); MirrorMeshChanges = ((BaseUnityPlugin)this).Config.Bind("Skin Switch / Toggles", "MirrorMeshChanges", true, "Updates clone meshes when CharacterDefinition / CBB swaps a SkinnedMeshRenderer mesh."); ParticleSystemsVisible = ((BaseUnityPlugin)this).Config.Bind("Character Effects", "ParticleSystemsVisible", true, "Legacy setting retained for config compatibility. After Images no longer changes ParticleSystems on the original character; all ParticleSystems found under generated clone hierarchies are always suppressed."); TrailRenderersVisible = ((BaseUnityPlugin)this).Config.Bind("Character Effects", "TrailRenderersVisible", true, "Finds TrailRenderer components on the current character and lets you show/hide them."); Clone1Color = ((BaseUnityPlugin)this).Config.Bind("Colors", "Clone1Color", new Color(0.6f, 67f / 75f, 1f, 0.2509804f), "Clone 1 RGBA. Config Manager shows R/G/B/A sliders and Hex."); Clone2Color = ((BaseUnityPlugin)this).Config.Bind("Colors", "Clone2Color", new Color(0.59180313f, 0.8012104f, 93f / 106f, 0.2509804f), "Clone 2 RGBA."); Clone3Color = ((BaseUnityPlugin)this).Config.Bind("Colors", "Clone3Color", new Color(0.6123621f, 99f / 106f, 0.8760166f, 0.2509804f), "Clone 3 RGBA."); RainbowMode = ((BaseUnityPlugin)this).Config.Bind("Rainbow Mode", "Enabled", false, "Continuously cycles all three afterimages through a pastel hue spectrum. Manual clone RGB colors are used again when disabled. Each clone keeps the alpha from its configured color."); RainbowSpeed = ((BaseUnityPlugin)this).Config.Bind("Rainbow Mode", "Speed", 0.12f, new ConfigDescription("Rainbow cycles per second. 0.12 is a little over 8 seconds for a full hue loop.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 2f), new object[0])); RainbowSaturation = ((BaseUnityPlugin)this).Config.Bind("Rainbow Mode", "Saturation", 0.45f, new ConfigDescription("Pastel strength. Lower values are paler/whiter; higher values are more vivid.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), new object[0])); ScanInterval = ((BaseUnityPlugin)this).Config.Bind("Advanced", "ScanInterval", 0.5f, ""); SceneManager.sceneLoaded += SceneLoaded; try { ClientController.RegisterCustomPacketHandler("AfterImages.ColorSync.v1", (Action)OnAcnColorPacket); ClientController.RegisterCustomPacketHandler("AfterImages.Presence.v1", (Action)OnAcnPresencePacket); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("ACN custom packet registration failed: " + ex.Message)); } nextScan = Time.unscaledTime + 0.5f; nextColorBroadcast = Time.unscaledTime + 1f; nextPresenceBroadcast = Time.unscaledTime + 0.25f; ((BaseUnityPlugin)this).Logger.LogInfo((object)"After Images 1.1.3 loaded. Added multiplayer remote-afterimage visibility toggle."); } internal static Material CreateExactCloneMaterial(int cloneIndex, Color color) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) Shader val = Shader.Find("Particles/Standard Unlit"); if ((Object)(object)val == (Object)null) { val = Shader.Find("Particles/Standard Unlit2"); } if ((Object)(object)val == (Object)null) { val = Shader.Find("Sprites/Default"); } if ((Object)(object)val == (Object)null) { return null; } Material val2 = new Material(val); ((Object)val2).name = cloneIndex switch { 1 => "clone 6", 0 => "clone 3", _ => "clone 7", } + " (After Images Runtime)"; val2.SetOverrideTag("RenderType", "Transparent"); val2.renderQueue = 3000; if (val2.HasProperty("_Mode")) { val2.SetFloat("_Mode", (cloneIndex < 2) ? 4f : 2f); } if (val2.HasProperty("_SrcBlend")) { val2.SetFloat("_SrcBlend", 5f); } if (val2.HasProperty("_DstBlend")) { val2.SetFloat("_DstBlend", (cloneIndex < 2) ? 1f : 10f); } if (val2.HasProperty("_ZWrite")) { val2.SetFloat("_ZWrite", 0f); } if (val2.HasProperty("_Cull")) { val2.SetFloat("_Cull", 2f); } if (val2.HasProperty("_Color")) { val2.SetColor("_Color", color); } val2.DisableKeyword("_ALPHATEST_ON"); val2.EnableKeyword("_ALPHABLEND_ON"); val2.DisableKeyword("_ALPHAPREMULTIPLY_ON"); return val2; } private void OnDestroy() { SceneManager.sceneLoaded -= SceneLoaded; try { ClientController.UnregisterCustomPacketHandler("AfterImages.ColorSync.v1"); ClientController.UnregisterCustomPacketHandler("AfterImages.Presence.v1"); } catch { } CleanupAll(); } private void SceneLoaded(Scene scene, LoadSceneMode mode) { CleanupAll(); remotePresence.Clear(); nextScan = Time.unscaledTime + 0.5f; nextPresenceBroadcast = Time.unscaledTime + 0.25f; } private void CleanupAll() { foreach (KeyValuePair driver in drivers) { if ((Object)(object)driver.Value != (Object)null) { Object.Destroy((Object)(object)driver.Value); } } drivers.Clear(); } private void Update() { UpdateAcnColorSync(); UpdateAcnPresenceSync(); ushort num = ushort.MaxValue; bool flag = false; try { ClientController instance = ClientController.Instance; flag = (Object)(object)instance != (Object)null && instance.Connected; if (flag) { num = instance.LocalID; } } catch { } List list = null; foreach (KeyValuePair driver in drivers) { Driver value = driver.Value; if ((Object)(object)value == (Object)null || value.PlayerGone) { if (list == null) { list = new List(); } list.Add(driver.Key); } else if (!((!flag) ? Enabled.Value : ((value.OwnerPlayerId == num) ? Enabled.Value : ((!IsConfirmedRemoteOwner(value.OwnerPlayerId)) ? Enabled.Value : (ShowOtherPlayersAfterimages.Value && IsRemotePresenceEnabled(value.OwnerPlayerId)))))) { if (list == null) { list = new List(); } list.Add(driver.Key); } else { value.SetPluginEnabled(value: true); } } if (list != null) { for (int i = 0; i < list.Count; i++) { if (drivers.TryGetValue(list[i], out var value2) && (Object)(object)value2 != (Object)null) { Object.Destroy((Object)(object)value2); } drivers.Remove(list[i]); } } if (!(Time.unscaledTime < nextScan)) { ScanPlayers(); nextScan = Time.unscaledTime + Mathf.Max(0.15f, ScanInterval.Value); } } private void ScanPlayers() { Player[] array = Object.FindObjectsOfType(); ClientController val = null; bool flag = false; ushort num = ushort.MaxValue; try { val = ClientController.Instance; flag = (Object)(object)val != (Object)null && val.Connected; if (flag) { num = val.LocalID; } } catch { } foreach (Player val2 in array) { if ((Object)(object)val2 == (Object)null || !((Component)val2).gameObject.activeInHierarchy) { continue; } int instanceID = ((Object)((Component)val2).gameObject).GetInstanceID(); if (drivers.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null) { value.RefreshAcnOwner(ResolveAcnOwner(val2)); value.RefreshVisualRootIfNeeded(); continue; } ushort num2 = ResolveAcnOwner(val2); if (flag) { if (num2 == num) { if (!Enabled.Value) { continue; } } else if (IsConfirmedRemoteOwner(num2)) { if (!ShowOtherPlayersAfterimages.Value || !IsRemotePresenceEnabled(num2)) { continue; } } else if (!Enabled.Value) { continue; } } else if (!Enabled.Value) { continue; } Transform val3 = FindVisualRoot(((Component)val2).transform); if (!((Object)(object)val3 == (Object)null)) { Driver driver = ((Component)val2).gameObject.AddComponent(); drivers[instanceID] = driver; try { driver.Initialize(val2, val3, num2); ((BaseUnityPlugin)this).Logger.LogInfo((object)("After Images 1.1.4 overlap-safe clones created for '" + ((Object)val3).name + "' (player object " + instanceID + ", ACN owner " + driver.OwnerPlayerId + "). Built-in Particles/Standard Unlit material path.")); } catch (Exception ex) { ((Behaviour)driver).enabled = false; ((BaseUnityPlugin)this).Logger.LogError((object)("After Images clone build failed once for '" + ((Object)val3).name + "': " + ex.Message)); } } } } private static Transform FindVisualRoot(Transform playerRoot) { if ((Object)(object)playerRoot == (Object)null) { return null; } SkinnedMeshRenderer[] componentsInChildren = ((Component)playerRoot).GetComponentsInChildren(true); if (componentsInChildren == null || componentsInChildren.Length == 0) { return null; } Dictionary dictionary = new Dictionary(); foreach (SkinnedMeshRenderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy && !IsOurRuntimeObject(((Component)val).transform) && !IsLegacyAfterimageObject(((Component)val).transform)) { Transform val2 = GetImmediateChildUnder(playerRoot, ((Component)val).transform); if ((Object)(object)val2 == (Object)null) { val2 = playerRoot; } dictionary.TryGetValue(val2, out var value); dictionary[val2] = value + 1; } } Transform result = null; int num = 0; foreach (KeyValuePair item in dictionary) { if ((Object)(object)item.Key != (Object)null && item.Value > num) { result = item.Key; num = item.Value; } } return result; } private static Transform GetImmediateChildUnder(Transform root, Transform t) { if ((Object)(object)root == (Object)null || (Object)(object)t == (Object)null) { return null; } Transform val = t; while ((Object)(object)val.parent != (Object)null && (Object)(object)val.parent != (Object)(object)root) { val = val.parent; } if (!((Object)(object)val.parent == (Object)(object)root)) { return root; } return val; } internal static bool IsOurRuntimeObject(Transform t) { Transform val = t; while ((Object)(object)val != (Object)null) { if (((Object)val).name.StartsWith("AFTERIMAGE_RUNTIME_3CLONES")) { return true; } val = val.parent; } return false; } internal static bool LooksLikeLegacyCloneContainer(Transform t) { if ((Object)(object)t == (Object)null || ((Object)t).name != "clones") { return false; } bool flag = false; bool flag2 = false; bool result = false; for (int i = 0; i < t.childCount; i++) { switch (((Object)t.GetChild(i)).name) { case "clone_01": flag = true; break; case "clone_02": flag2 = true; break; case "clone_03": result = true; break; } } if (flag && flag2) { return result; } return false; } internal static bool IsLegacyAfterimageObject(Transform t) { Transform val = t; while ((Object)(object)val != (Object)null) { if (LooksLikeLegacyCloneContainer(val)) { return true; } val = val.parent; } return false; } private ushort ResolveAcnOwner(Player player) { try { ClientController instance = ClientController.Instance; if ((Object)(object)instance == (Object)null) { return 0; } foreach (KeyValuePair player2 in instance.Players) { MPPlayer value = player2.Value; if (value != null && !((Object)(object)value.Player == (Object)null)) { if ((Object)(object)value.Player == (Object)(object)player) { return player2.Key; } Transform transform = ((Component)value.Player).transform; Transform transform2 = ((Component)player).transform; if ((Object)(object)transform == (Object)(object)transform2 || transform.IsChildOf(transform2) || transform2.IsChildOf(transform)) { return player2.Key; } } } return ushort.MaxValue; } catch { return ushort.MaxValue; } } internal bool TryGetSyncedColors(ushort playerId, out Color c1, out Color c2, out Color c3) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (remoteColors.TryGetValue(playerId, out var value)) { c1 = value.c1; c2 = value.c2; c3 = value.c3; return true; } c1 = Color.white; c2 = Color.white; c3 = Color.white; return false; } private void UpdateAcnPresenceSync() { try { ClientController instance = ClientController.Instance; if (!((Object)(object)instance == (Object)null) && instance.Connected) { remotePresence[instance.LocalID] = new RemotePresence(Enabled.Value, Time.unscaledTime); if (!(Time.unscaledTime < nextPresenceBroadcast)) { instance.BroadcastCustomPacket(new byte[1] { (byte)(Enabled.Value ? 1 : 0) }, "AfterImages.Presence.v1", (SendModes)2); nextPresenceBroadcast = Time.unscaledTime + 2f; } } } catch { } } private void OnAcnPresencePacket(ushort sender, byte[] data) { if (data != null && data.Length >= 1) { remotePresence[sender] = new RemotePresence(data[0] != 0, Time.unscaledTime); if (data[0] == 0) { nextScan = 0f; } } } private bool IsConfirmedRemoteOwner(ushort ownerId) { try { ClientController instance = ClientController.Instance; if ((Object)(object)instance == (Object)null || !instance.Connected) { return false; } if (ownerId == ushort.MaxValue || ownerId == instance.LocalID) { return false; } return instance.Players != null && instance.Players.ContainsKey(ownerId); } catch { return false; } } private bool IsRemotePresenceEnabled(ushort ownerId) { if (ownerId == ushort.MaxValue) { return false; } if (!remotePresence.TryGetValue(ownerId, out var value)) { return false; } if (!value.Enabled) { return false; } return Time.unscaledTime - value.LastSeen <= 8f; } internal static void GetLocalEffectiveColors(out Color c1, out Color c2, out Color c3) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //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_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: 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_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) c1 = Clone1Color.Value; c2 = Clone2Color.Value; c3 = Clone3Color.Value; if (RainbowMode != null && RainbowMode.Value) { float num = ((RainbowSpeed != null) ? Mathf.Max(0f, RainbowSpeed.Value) : 0.12f); float num2 = ((RainbowSaturation != null) ? Mathf.Clamp01(RainbowSaturation.Value) : 0.45f); float num3 = Mathf.Repeat(Time.unscaledTime * num, 1f); Color val = Color.HSVToRGB(num3, num2, 1f); Color val2 = Color.HSVToRGB(Mathf.Repeat(num3 + 1f / 3f, 1f), num2, 1f); Color val3 = Color.HSVToRGB(Mathf.Repeat(num3 + 2f / 3f, 1f), num2, 1f); val.a = c1.a; val2.a = c2.a; val3.a = c3.a; c1 = val; c2 = val2; c3 = val3; } } private void UpdateAcnColorSync() { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) try { ClientController instance = ClientController.Instance; if ((Object)(object)instance == (Object)null || !instance.Connected) { return; } GetLocalEffectiveColors(out var c, out var c2, out var c3); bool flag = RainbowMode != null && RainbowMode.Value; bool flag2 = !SameColor(c, lastSent1) || !SameColor(c2, lastSent2) || !SameColor(c3, lastSent3); if (flag) { if (Time.unscaledTime < nextColorBroadcast) { return; } } else if (!flag2 && Time.unscaledTime < nextColorBroadcast) { return; } byte[] array = PackColors(c, c2, c3); instance.BroadcastCustomPacket(array, "AfterImages.ColorSync.v1", (SendModes)2); lastSent1 = c; lastSent2 = c2; lastSent3 = c3; nextColorBroadcast = Time.unscaledTime + (flag ? 0.1f : 3f); remoteColors[instance.LocalID] = new SyncedColors(c, c2, c3); } catch { } } private void OnAcnColorPacket(ushort sender, byte[] data) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (data == null || data.Length < 12) { return; } remoteColors[sender] = new SyncedColors(ReadColor(data, 0), ReadColor(data, 4), ReadColor(data, 8)); foreach (KeyValuePair driver in drivers) { Driver value = driver.Value; if ((Object)(object)value != (Object)null && value.OwnerPlayerId == sender) { value.ForceColorRefresh(); } } } private static byte[] PackColors(Color c1, Color c2, Color c3) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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) byte[] array = new byte[12]; WriteColor(array, 0, c1); WriteColor(array, 4, c2); WriteColor(array, 8, c3); return array; } private static void WriteColor(byte[] d, int o, Color c) { d[o] = (byte)Mathf.RoundToInt(Mathf.Clamp01(c.r) * 255f); d[o + 1] = (byte)Mathf.RoundToInt(Mathf.Clamp01(c.g) * 255f); d[o + 2] = (byte)Mathf.RoundToInt(Mathf.Clamp01(c.b) * 255f); d[o + 3] = (byte)Mathf.RoundToInt(Mathf.Clamp01(c.a) * 255f); } private static Color ReadColor(byte[] d, int o) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) return new Color((float)(int)d[o] * 0.003921569f, (float)(int)d[o + 1] * 0.003921569f, (float)(int)d[o + 2] * 0.003921569f, (float)(int)d[o + 3] * 0.003921569f); } private static bool SameColor(Color a, Color b) { if (Mathf.Approximately(a.r, b.r) && Mathf.Approximately(a.g, b.g) && Mathf.Approximately(a.b, b.b)) { return Mathf.Approximately(a.a, b.a); } return false; } } [DefaultExecutionOrder(32000)] public sealed class Driver : MonoBehaviour { private sealed class RendererMap { public SkinnedMeshRenderer source; public readonly List clones = new List(); } private struct EffectRendererState { public Renderer renderer; public bool originalEnabled; public EffectRendererState(Renderer r, bool enabled) { renderer = r; originalEnabled = enabled; } } private Player player; private Transform visualRoot; private GameObject runtimeRoot; private Transform[][] cloneBones = new Transform[3][]; private Transform[] sourceBones = (Transform[])(object)new Transform[0]; private string[] sourcePaths = new string[0]; private readonly List rendererMaps = new List(); private readonly List ownedMaterials = new List(); private readonly List particleStates = new List(); private readonly List trailStates = new List(); private Vector3[][] posHistory; private Quaternion[][] rotHistory; private Vector3[][] scaleHistory; private float[] timeHistory; private int historyCapacity; private int historyCount; private int historyHead; private float nextSample; private float nextCloneUpdate; private float nextRendererSync; private float nextParticleSuppressionScan; private float nextStructureCheck; private int visualStructureSignature; private int pendingStructureSignature; private int pendingStructureMatches; private readonly bool[] cloneHistoryReady = new bool[3]; private readonly bool[] cloneDistanceReady = new bool[3] { true, true, true }; private Color lastColor1 = new Color(-1f, -1f, -1f, -1f); private Color lastColor2 = new Color(-1f, -1f, -1f, -1f); private Color lastColor3 = new Color(-1f, -1f, -1f, -1f); private bool lastParticleVisible = true; private bool lastTrailVisible = true; public ushort OwnerPlayerId { get; private set; } internal bool PlayerGone { get { if (!((Object)(object)player == (Object)null)) { return (Object)(object)((Component)player).gameObject == (Object)null; } return true; } } public void RefreshAcnOwner(ushort ownerId) { if (ownerId != ushort.MaxValue) { OwnerPlayerId = ownerId; } } internal void Initialize(Player p, Transform root, ushort owner) { player = p; visualRoot = root; OwnerPlayerId = owner; CleanupOldAfterimageObjects(); RebuildRuntimeClones(); } private void CleanupOldAfterimageObjects() { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return; } int instanceID = ((Object)player).GetInstanceID(); string text = "AFTERIMAGE_RUNTIME_3CLONES_" + instanceID; Transform[] componentsInChildren = ((Component)player).GetComponentsInChildren(true); for (int num = componentsInChildren.Length - 1; num >= 0; num--) { Transform val = componentsInChildren[num]; if (!((Object)(object)val == (Object)null)) { bool flag = ((Object)val).name.StartsWith("AFTERIMAGE_RUNTIME_3CLONES"); bool flag2 = Plugin.LooksLikeLegacyCloneContainer(val); if (flag || flag2) { ((Component)val).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)val).gameObject); } } } Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); foreach (GameObject val2 in rootGameObjects) { if (!((Object)(object)val2 == (Object)null)) { string name = ((Object)val2).name; bool flag3 = name == text; bool flag4 = name == "AFTERIMAGE_RUNTIME_3CLONES"; if (flag3 || flag4) { val2.SetActive(false); Object.Destroy((Object)(object)val2); } } } } internal void SetPluginEnabled(bool value) { if ((Object)(object)runtimeRoot != (Object)null && runtimeRoot.activeSelf != value) { runtimeRoot.SetActive(value); } } internal void ForceColorRefresh() { //IL_0015: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) lastColor1 = new Color(-1f, -1f, -1f, -1f); lastColor2 = new Color(-1f, -1f, -1f, -1f); lastColor3 = new Color(-1f, -1f, -1f, -1f); } internal void RefreshVisualRootIfNeeded() { if (!((Object)(object)player == (Object)null)) { Transform val = FindVisualRootForDriver(((Component)player).transform); if (!((Object)(object)val == (Object)null) && ((Object)(object)visualRoot == (Object)null || (Object)(object)visualRoot != (Object)(object)val || !((Component)visualRoot).gameObject.activeInHierarchy)) { DestroyRuntime(); CleanupOldAfterimageObjects(); visualRoot = val; RebuildRuntimeClones(); } } } private static Transform FindVisualRootForDriver(Transform playerRoot) { SkinnedMeshRenderer[] componentsInChildren = ((Component)playerRoot).GetComponentsInChildren(true); Dictionary dictionary = new Dictionary(); foreach (SkinnedMeshRenderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && ((Component)val).gameObject.activeInHierarchy && !Plugin.IsOurRuntimeObject(((Component)val).transform) && !Plugin.IsLegacyAfterimageObject(((Component)val).transform)) { Transform val2 = ((Component)val).transform; while ((Object)(object)val2.parent != (Object)null && (Object)(object)val2.parent != (Object)(object)playerRoot) { val2 = val2.parent; } if ((Object)(object)val2.parent != (Object)(object)playerRoot) { val2 = playerRoot; } dictionary.TryGetValue(val2, out var value); dictionary[val2] = value + 1; } } Transform result = null; int num = 0; foreach (KeyValuePair item in dictionary) { if ((Object)(object)item.Key != (Object)null && item.Value > num) { result = item.Key; num = item.Value; } } return result; } private void RebuildRuntimeClones() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: 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) DestroyRuntime(); if ((Object)(object)visualRoot == (Object)null || !((Component)visualRoot).gameObject.activeInHierarchy) { return; } CleanupOldAfterimageObjects(); BuildSourceBoneList(); if (sourceBones.Length != 0) { runtimeRoot = new GameObject("AFTERIMAGE_RUNTIME_3CLONES_" + ((Object)player).GetInstanceID()); runtimeRoot.transform.SetParent((Transform)null, false); runtimeRoot.transform.position = Vector3.zero; runtimeRoot.transform.rotation = Quaternion.identity; runtimeRoot.transform.localScale = Vector3.one; cloneBones[0] = BuildOneClone("clone_01", 0, Plugin.Clone1Color.Value); cloneBones[1] = BuildOneClone("clone_02", 1, Plugin.Clone2Color.Value); cloneBones[2] = BuildOneClone("clone_03", 2, Plugin.Clone3Color.Value); HardDisableCloneParticleSystems(); BuildEffectToggleLists(); ClearPoseHistory(); AllocateHistory(); if (historyCapacity > 0) { CapturePose(Time.unscaledTime); } cloneHistoryReady[0] = false; cloneHistoryReady[1] = false; cloneHistoryReady[2] = false; cloneDistanceReady[0] = false; cloneDistanceReady[1] = false; cloneDistanceReady[2] = false; visualStructureSignature = ComputeVisualStructureSignature(); pendingStructureSignature = visualStructureSignature; pendingStructureMatches = 0; nextStructureCheck = 0f; nextSample = 0f; nextCloneUpdate = 0f; nextRendererSync = 0f; nextParticleSuppressionScan = 0f; ForceColorRefresh(); } } private void BuildSourceBoneList() { Transform[] componentsInChildren = ((Component)visualRoot).GetComponentsInChildren(true); List list = new List(); foreach (Transform val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !Plugin.IsOurRuntimeObject(val) && !Plugin.IsLegacyAfterimageObject(val)) { list.Add(val); } } sourceBones = list.ToArray(); sourcePaths = new string[sourceBones.Length]; for (int j = 0; j < sourceBones.Length; j++) { sourcePaths[j] = RelativePath(visualRoot, sourceBones[j]); } } private Transform[] BuildOneClone(string cloneName, int cloneIndex, Color initialColor) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Expected O, but got Unknown //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(cloneName); val.transform.SetParent(runtimeRoot.transform, false); Transform[] array = (Transform[])(object)new Transform[sourceBones.Length]; Dictionary dictionary = new Dictionary(); for (int i = 0; i < sourceBones.Length; i++) { Transform val2 = sourceBones[i]; if (!((Object)(object)val2 == (Object)null)) { GameObject val3 = new GameObject((i == 0) ? (((Object)visualRoot).name + "_AFTERIMAGE") : ((Object)val2).name); val3.layer = ((Component)val2).gameObject.layer; Transform transform = val3.transform; Transform val4 = val.transform; if ((Object)(object)val2 != (Object)(object)visualRoot && (Object)(object)val2.parent != (Object)null && dictionary.TryGetValue(val2.parent, out var value)) { val4 = value; } transform.SetParent(val4, false); if ((Object)(object)val2 == (Object)(object)visualRoot) { transform.position = val2.position; transform.rotation = val2.rotation; transform.localScale = val2.localScale; } else { transform.localPosition = val2.localPosition; transform.localRotation = val2.localRotation; transform.localScale = val2.localScale; } array[i] = transform; dictionary[val2] = transform; } } SkinnedMeshRenderer[] componentsInChildren = ((Component)visualRoot).GetComponentsInChildren(true); for (int j = 0; j < componentsInChildren.Length; j++) { SkinnedMeshRenderer val5 = componentsInChildren[j]; if ((Object)(object)val5 == (Object)null || (Object)(object)val5.sharedMesh == (Object)null || Plugin.IsOurRuntimeObject(((Component)val5).transform) || Plugin.IsLegacyAfterimageObject(((Component)val5).transform)) { continue; } string name = ((Object)val5.sharedMesh).name; if ((string.IsNullOrEmpty(name) || name.IndexOf("sphere", StringComparison.OrdinalIgnoreCase) < 0) && dictionary.TryGetValue(((Component)val5).transform, out var value2)) { GameObject val6 = new GameObject("__AI_RENDERER_" + j.ToString("D2") + "_" + ((Object)((Component)val5).gameObject).name); val6.layer = ((Component)val5).gameObject.layer; Transform transform2 = val6.transform; transform2.SetParent(value2, false); transform2.localPosition = Vector3.zero; transform2.localRotation = Quaternion.identity; transform2.localScale = Vector3.one; SkinnedMeshRenderer val7 = val6.AddComponent(); if (!((Object)(object)val7 == (Object)null)) { CopyRenderer(val5, val7, dictionary, cloneIndex, initialColor); RendererMap rendererMap = FindOrCreateRendererMap(val5); rendererMap.clones.Add(val7); } } } return array; } private RendererMap FindOrCreateRendererMap(SkinnedMeshRenderer src) { for (int i = 0; i < rendererMaps.Count; i++) { if ((Object)(object)rendererMaps[i].source == (Object)(object)src) { return rendererMaps[i]; } } RendererMap rendererMap = new RendererMap(); rendererMap.source = src; rendererMaps.Add(rendererMap); return rendererMap; } private void CopyRenderer(SkinnedMeshRenderer src, SkinnedMeshRenderer dst, Dictionary cloneBySource, int cloneIndex, Color color) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)src == (Object)null || (Object)(object)dst == (Object)null || (Object)(object)src.sharedMesh == (Object)null) { return; } dst.sharedMesh = src.sharedMesh; ((Renderer)dst).enabled = ((Renderer)src).enabled; dst.updateWhenOffscreen = true; ((Renderer)dst).localBounds = ((Renderer)src).localBounds; ((Renderer)dst).sortingLayerID = ((Renderer)src).sortingLayerID; ((Renderer)dst).sortingOrder = ((Renderer)src).sortingOrder; if (Plugin.DisableCloneShadows.Value) { ((Renderer)dst).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)dst).receiveShadows = false; } else { ((Renderer)dst).shadowCastingMode = ((Renderer)src).shadowCastingMode; ((Renderer)dst).receiveShadows = ((Renderer)src).receiveShadows; } ((Renderer)dst).lightProbeUsage = (LightProbeUsage)0; ((Renderer)dst).reflectionProbeUsage = (ReflectionProbeUsage)0; ((Renderer)dst).motionVectorGenerationMode = (MotionVectorGenerationMode)2; if ((Object)(object)src.rootBone != (Object)null && cloneBySource.TryGetValue(src.rootBone, out var value)) { dst.rootBone = value; } Transform[] bones = src.bones; Transform[] array = (Transform[])(object)new Transform[bones.Length]; for (int i = 0; i < bones.Length; i++) { if (!((Object)(object)bones[i] == (Object)null) && cloneBySource.TryGetValue(bones[i], out var value2)) { array[i] = value2; } } dst.bones = array; Material[] sharedMaterials = ((Renderer)src).sharedMaterials; int num = ((sharedMaterials == null || sharedMaterials.Length <= 0) ? 1 : sharedMaterials.Length); Material[] array2 = (Material[])(object)new Material[num]; for (int j = 0; j < array2.Length; j++) { Material val = Plugin.CreateExactCloneMaterial(cloneIndex, color); if (!((Object)(object)val == (Object)null)) { array2[j] = val; ownedMaterials.Add(val); } } ((Renderer)dst).sharedMaterials = array2; } private void HardDisableCloneParticleSystems() { if ((Object)(object)runtimeRoot == (Object)null) { return; } ParticleSystem[] componentsInChildren = runtimeRoot.GetComponentsInChildren(true); foreach (ParticleSystem val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { val.Stop(true, (ParticleSystemStopBehavior)0); val.Clear(true); GameObject gameObject = ((Component)val).gameObject; if ((Object)(object)gameObject != (Object)null && (Object)(object)gameObject.GetComponent() == (Object)null) { gameObject.SetActive(false); } } } ParticleSystemRenderer[] componentsInChildren2 = runtimeRoot.GetComponentsInChildren(true); foreach (ParticleSystemRenderer val2 in componentsInChildren2) { if ((Object)(object)val2 != (Object)null) { ((Renderer)val2).enabled = false; } } } private void BuildEffectToggleLists() { particleStates.Clear(); trailStates.Clear(); TrailRenderer[] componentsInChildren = ((Component)visualRoot).GetComponentsInChildren(true); foreach (TrailRenderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !Plugin.IsOurRuntimeObject(((Component)val).transform) && !Plugin.IsLegacyAfterimageObject(((Component)val).transform)) { trailStates.Add(new EffectRendererState((Renderer)(object)val, ((Renderer)val).enabled)); } } lastParticleVisible = !Plugin.ParticleSystemsVisible.Value; lastTrailVisible = !Plugin.TrailRenderersVisible.Value; ApplyEffectToggles(); } private void ApplyEffectToggles() { bool value = Plugin.ParticleSystemsVisible.Value; bool value2 = Plugin.TrailRenderersVisible.Value; if (value != lastParticleVisible) { lastParticleVisible = value; } if (value2 == lastTrailVisible) { return; } for (int i = 0; i < trailStates.Count; i++) { Renderer renderer = trailStates[i].renderer; if ((Object)(object)renderer != (Object)null) { renderer.enabled = value2 && trailStates[i].originalEnabled; } } lastTrailVisible = value2; } private void AllocateHistory() { if (sourceBones == null || sourceBones.Length == 0) { historyCapacity = 0; historyCount = 0; historyHead = -1; posHistory = null; rotHistory = null; scaleHistory = null; timeHistory = null; return; } float num = Mathf.Max(Plugin.Clone1Delay.Value, Mathf.Max(Plugin.Clone2Delay.Value, Plugin.Clone3Delay.Value)); float value = Plugin.SampleInterval.Value; float num2 = ((value <= 0f) ? 0.004166667f : Mathf.Max(0.001f, value)); float num3 = Mathf.Max(0.35f, num + 0.25f); historyCapacity = Mathf.Clamp(Mathf.CeilToInt(num3 / num2) + 12, 32, 768); posHistory = new Vector3[historyCapacity][]; rotHistory = new Quaternion[historyCapacity][]; scaleHistory = new Vector3[historyCapacity][]; timeHistory = new float[historyCapacity]; for (int i = 0; i < historyCapacity; i++) { posHistory[i] = (Vector3[])(object)new Vector3[sourceBones.Length]; rotHistory[i] = (Quaternion[])(object)new Quaternion[sourceBones.Length]; scaleHistory[i] = (Vector3[])(object)new Vector3[sourceBones.Length]; } historyCount = 0; historyHead = -1; nextSample = 0f; } private void LateUpdate() { if ((Object)(object)player == (Object)null) { return; } float unscaledTime = Time.unscaledTime; if ((Object)(object)runtimeRoot != (Object)null && unscaledTime >= nextParticleSuppressionScan) { nextParticleSuppressionScan = unscaledTime + 0.25f; HardDisableCloneParticleSystems(); } if (unscaledTime >= nextStructureCheck) { nextStructureCheck = unscaledTime + 0.05f; int num = ComputeVisualStructureSignature(); if (num != visualStructureSignature) { if (num == pendingStructureSignature) { pendingStructureMatches++; } else { pendingStructureSignature = num; pendingStructureMatches = 1; } if (pendingStructureMatches >= 2) { RebuildRuntimeClones(); return; } } else { pendingStructureSignature = visualStructureSignature; pendingStructureMatches = 0; } } ApplyEffectToggles(); ApplyNetworkOrLocalColors(); if (unscaledTime >= nextRendererSync) { MirrorRendererStates(); nextRendererSync = unscaledTime + 0.1f; } if (!Plugin.Enabled.Value) { return; } if (historyCapacity <= 0) { AllocateHistory(); if (historyCapacity <= 0) { return; } } float value = Plugin.SampleInterval.Value; if (value <= 0f || unscaledTime >= nextSample) { CapturePose(unscaledTime); nextSample = ((value > 0f) ? (unscaledTime + Mathf.Max(0.001f, value)) : unscaledTime); } float num2 = Mathf.Max(0f, Plugin.CloneUpdateInterval.Value); if (!(num2 > 0f) || !(unscaledTime < nextCloneUpdate)) { nextCloneUpdate = ((num2 > 0f) ? (unscaledTime + num2) : unscaledTime); bool flag = ApplyDelayedPose(cloneBones[0], unscaledTime - Mathf.Max(0f, Plugin.Clone1Delay.Value)); bool flag2 = ApplyDelayedPose(cloneBones[1], unscaledTime - Mathf.Max(0f, Plugin.Clone2Delay.Value)); bool flag3 = ApplyDelayedPose(cloneBones[2], unscaledTime - Mathf.Max(0f, Plugin.Clone3Delay.Value)); SetCloneDistanceReady(0, flag); SetCloneDistanceReady(1, flag2); SetCloneDistanceReady(2, flag3); SetCloneHistoryReady(0, flag); SetCloneHistoryReady(1, flag2); SetCloneHistoryReady(2, flag3); if (Plugin.CopyBlendShapes.Value) { CopyBlendShapes(); } } } private void CapturePose(float now) { //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: 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_0141: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) if (sourceBones == null || sourceBones.Length == 0) { return; } if (historyCapacity <= 0 || posHistory == null || rotHistory == null || scaleHistory == null || timeHistory == null) { AllocateHistory(); if (historyCapacity <= 0 || posHistory == null || rotHistory == null || scaleHistory == null || timeHistory == null) { return; } } historyHead = (historyHead + 1) % historyCapacity; if (historyCount < historyCapacity) { historyCount++; } Vector3[] array = posHistory[historyHead]; Quaternion[] array2 = rotHistory[historyHead]; Vector3[] array3 = scaleHistory[historyHead]; for (int i = 0; i < sourceBones.Length; i++) { Transform val = sourceBones[i]; if (!((Object)(object)val == (Object)null)) { if (i == 0) { ref Vector3 reference = ref array[i]; reference = val.position; ref Quaternion reference2 = ref array2[i]; reference2 = val.rotation; ref Vector3 reference3 = ref array3[i]; reference3 = val.localScale; } else { ref Vector3 reference4 = ref array[i]; reference4 = val.localPosition; ref Quaternion reference5 = ref array2[i]; reference5 = val.localRotation; ref Vector3 reference6 = ref array3[i]; reference6 = val.localScale; } } } timeHistory[historyHead] = now; } private bool ApplyDelayedPose(Transform[] clone, float targetTime) { //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: 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_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) if (clone == null || historyCount == 0) { return false; } int num = (historyHead - historyCount + 1 + historyCapacity) % historyCapacity; if (targetTime < timeHistory[num]) { return false; } FindFrames(targetTime, out var a, out var b, out var lerp); Vector3[] array = posHistory[a]; Vector3[] array2 = posHistory[b]; Quaternion[] array3 = rotHistory[a]; Quaternion[] array4 = rotHistory[b]; Vector3[] array5 = scaleHistory[a]; Vector3[] array6 = scaleHistory[b]; ExpSmoothingFactor(Plugin.PositionSmoothing.Value); ExpSmoothingFactor(Plugin.RotationSmoothing.Value); int num2 = Mathf.Min(clone.Length, sourceBones.Length); for (int i = 0; i < num2; i++) { Transform val = clone[i]; if (!((Object)(object)val == (Object)null)) { Vector3 val2 = Vector3.LerpUnclamped(array[i], array2[i], lerp); Quaternion val3 = Quaternion.SlerpUnclamped(array3[i], array4[i], lerp); if (i == 0) { val.position = val2; val.rotation = val3; } else { val.localPosition = val2; val.localRotation = val3; } if (Plugin.CopyScale.Value) { val.localScale = Vector3.LerpUnclamped(array5[i], array6[i], lerp); } } } return true; } private void FindFrames(float target, out int a, out int b, out float lerp) { int num = (b = (a = (historyHead - historyCount + 1 + historyCapacity) % historyCapacity)); lerp = 0f; int num2 = num; for (int i = 1; i < historyCount; i++) { int num3 = (num + i) % historyCapacity; if (timeHistory[num3] >= target) { a = num2; b = num3; float num4 = timeHistory[a]; float num5 = timeHistory[b]; lerp = ((num5 > num4) ? Mathf.Clamp01((target - num4) / (num5 - num4)) : 0f); return; } num2 = num3; } a = historyHead; b = historyHead; } private void ClearPoseHistory() { historyCount = 0; historyHead = -1; nextSample = 0f; posHistory = null; rotHistory = null; scaleHistory = null; timeHistory = null; historyCapacity = 0; } private void SetCloneDistanceReady(int cloneIndex, bool hasHistory) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) if (cloneIndex < 0 || cloneIndex >= cloneDistanceReady.Length) { return; } if (!hasHistory) { cloneDistanceReady[cloneIndex] = false; return; } float num = Mathf.Max(0f, Plugin.MinimumVisibleDistance.Value); if (num <= 0f || (Object)(object)visualRoot == (Object)null || cloneBones[cloneIndex] == null || cloneBones[cloneIndex].Length == 0 || (Object)(object)cloneBones[cloneIndex][0] == (Object)null) { cloneDistanceReady[cloneIndex] = true; return; } Vector3 val = cloneBones[cloneIndex][0].position - visualRoot.position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; float num2 = ((Plugin.MinimumVisibleDistanceHysteresis != null) ? Mathf.Max(0f, Plugin.MinimumVisibleDistanceHysteresis.Value) : 0.03f); float num3 = (cloneDistanceReady[cloneIndex] ? num : (num + num2)); cloneDistanceReady[cloneIndex] = sqrMagnitude >= num3 * num3; } private void SetCloneHistoryReady(int cloneIndex, bool ready) { if (cloneIndex < 0 || cloneIndex >= cloneHistoryReady.Length || cloneHistoryReady[cloneIndex] == ready) { return; } cloneHistoryReady[cloneIndex] = ready; for (int i = 0; i < rendererMaps.Count; i++) { RendererMap rendererMap = rendererMaps[i]; if (cloneIndex >= rendererMap.clones.Count) { continue; } SkinnedMeshRenderer val = rendererMap.clones[cloneIndex]; if ((Object)(object)val == (Object)null) { continue; } bool flag = ready && cloneIndex < cloneDistanceReady.Length && cloneDistanceReady[cloneIndex]; if ((Object)(object)rendererMap.source != (Object)null) { if (Plugin.MirrorRendererToggles.Value) { flag &= ((Renderer)rendererMap.source).enabled; } if (Plugin.MirrorRendererActiveState.Value) { flag &= ((Component)rendererMap.source).gameObject.activeInHierarchy; } } ((Renderer)val).enabled = flag; } } private int ComputeVisualStructureSignature() { if ((Object)(object)visualRoot == (Object)null) { return 0; } int num = 17; int num2 = 0; SkinnedMeshRenderer[] componentsInChildren = ((Component)visualRoot).GetComponentsInChildren(true); foreach (SkinnedMeshRenderer val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !Plugin.IsOurRuntimeObject(((Component)val).transform) && !Plugin.IsLegacyAfterimageObject(((Component)val).transform)) { num2++; num = num * 31 + ((Object)val).GetInstanceID(); num = num * 31 + (((Object)(object)val.sharedMesh != (Object)null) ? ((Object)val.sharedMesh).GetInstanceID() : 0); } } return num * 31 + num2; } private void MirrorRendererStates() { for (int i = 0; i < rendererMaps.Count; i++) { RendererMap rendererMap = rendererMaps[i]; if ((Object)(object)rendererMap.source == (Object)null) { continue; } bool flag = true; if (Plugin.MirrorRendererToggles.Value) { flag &= ((Renderer)rendererMap.source).enabled; } if (Plugin.MirrorRendererActiveState.Value) { flag &= ((Component)rendererMap.source).gameObject.activeInHierarchy; } for (int j = 0; j < rendererMap.clones.Count; j++) { SkinnedMeshRenderer val = rendererMap.clones[j]; if (!((Object)(object)val == (Object)null)) { bool flag2 = j < 0 || j >= cloneHistoryReady.Length || cloneHistoryReady[j]; bool flag3 = j < 0 || j >= cloneDistanceReady.Length || cloneDistanceReady[j]; ((Renderer)val).enabled = flag && flag2 && flag3; if (Plugin.MirrorMeshChanges.Value && (Object)(object)val.sharedMesh != (Object)(object)rendererMap.source.sharedMesh) { val.sharedMesh = rendererMap.source.sharedMesh; } } } } } private void CopyBlendShapes() { for (int i = 0; i < rendererMaps.Count; i++) { RendererMap rendererMap = rendererMaps[i]; if ((Object)(object)rendererMap.source == (Object)null || (Object)(object)rendererMap.source.sharedMesh == (Object)null) { continue; } int blendShapeCount = rendererMap.source.sharedMesh.blendShapeCount; for (int j = 0; j < rendererMap.clones.Count; j++) { SkinnedMeshRenderer val = rendererMap.clones[j]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.sharedMesh == (Object)null)) { int num = Mathf.Min(blendShapeCount, val.sharedMesh.blendShapeCount); for (int k = 0; k < num; k++) { val.SetBlendShapeWeight(k, rendererMap.source.GetBlendShapeWeight(k)); } } } } } private void ApplyNetworkOrLocalColors() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) Plugin.GetLocalEffectiveColors(out var c, out var c2, out var c3); try { ClientController instance = ClientController.Instance; if ((Object)(object)instance != (Object)null && instance.Connected && OwnerPlayerId != 0 && OwnerPlayerId != instance.LocalID && (Object)(object)Plugin.Instance != (Object)null && Plugin.Instance.TryGetSyncedColors(OwnerPlayerId, out var c4, out var c5, out var c6)) { c = c4; c2 = c5; c3 = c6; } } catch { } if (!SameColor(c, lastColor1) || !SameColor(c2, lastColor2) || !SameColor(c3, lastColor3)) { ApplyColorToClone(0, c); ApplyColorToClone(1, c2); ApplyColorToClone(2, c3); lastColor1 = c; lastColor2 = c2; lastColor3 = c3; } } private void ApplyColorToClone(int cloneIndex, Color color) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < rendererMaps.Count; i++) { RendererMap rendererMap = rendererMaps[i]; if (cloneIndex >= rendererMap.clones.Count) { continue; } SkinnedMeshRenderer val = rendererMap.clones[cloneIndex]; if ((Object)(object)val == (Object)null) { continue; } Material[] sharedMaterials = ((Renderer)val).sharedMaterials; for (int j = 0; j < sharedMaterials.Length; j++) { if ((Object)(object)sharedMaterials[j] != (Object)null) { ApplyColor(sharedMaterials[j], color); } } } } private static void ApplyColor(Material m, Color c) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (m.HasProperty("_Color")) { m.SetColor("_Color", c); } if (m.HasProperty("_BaseColor")) { m.SetColor("_BaseColor", c); } if (m.HasProperty("_TintColor")) { m.SetColor("_TintColor", c); } if (m.HasProperty("_EmissionColor")) { Color val = c; val.a = 1f; m.SetColor("_EmissionColor", val); } if (m.HasProperty("_Tint")) { m.SetColor("_Tint", c); } } private void DestroyRuntime() { RestoreEffectStates(); rendererMaps.Clear(); particleStates.Clear(); trailStates.Clear(); for (int i = 0; i < ownedMaterials.Count; i++) { if ((Object)(object)ownedMaterials[i] != (Object)null) { Object.Destroy((Object)(object)ownedMaterials[i]); } } ownedMaterials.Clear(); if ((Object)(object)runtimeRoot != (Object)null) { runtimeRoot.SetActive(false); Object.Destroy((Object)(object)runtimeRoot); } runtimeRoot = null; sourceBones = (Transform[])(object)new Transform[0]; sourcePaths = new string[0]; cloneBones = new Transform[3][]; cloneHistoryReady[0] = false; cloneHistoryReady[1] = false; cloneHistoryReady[2] = false; cloneDistanceReady[0] = false; cloneDistanceReady[1] = false; cloneDistanceReady[2] = false; ClearPoseHistory(); } private void RestoreEffectStates() { for (int i = 0; i < particleStates.Count; i++) { Renderer renderer = particleStates[i].renderer; if ((Object)(object)renderer != (Object)null) { renderer.enabled = particleStates[i].originalEnabled; } } for (int j = 0; j < trailStates.Count; j++) { Renderer renderer2 = trailStates[j].renderer; if ((Object)(object)renderer2 != (Object)null) { renderer2.enabled = trailStates[j].originalEnabled; } } } private void OnDestroy() { DestroyRuntime(); } private static float ExpSmoothingFactor(float speed) { if (speed <= 0f) { return 1f; } return 1f - Mathf.Exp((0f - speed) * Time.unscaledDeltaTime); } private static string RelativePath(Transform root, Transform t) { if ((Object)(object)root == (Object)(object)t) { return ""; } List list = new List(); Transform val = t; while ((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)root) { list.Add(((Object)val).name); val = val.parent; } if ((Object)(object)val != (Object)(object)root) { return ""; } list.Reverse(); return string.Join("/", list.ToArray()); } private static string ParentPath(string path) { int num = path.LastIndexOf('/'); if (num >= 0) { return path.Substring(0, num); } return ""; } private static bool SameColor(Color a, Color b) { if (Mathf.Approximately(a.r, b.r) && Mathf.Approximately(a.g, b.g) && Mathf.Approximately(a.b, b.b)) { return Mathf.Approximately(a.a, b.a); } return false; } }