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<bool> Enabled;
internal static ConfigEntry<bool> ShowOtherPlayersAfterimages;
internal static ConfigEntry<float> Clone1Delay;
internal static ConfigEntry<float> Clone2Delay;
internal static ConfigEntry<float> Clone3Delay;
internal static ConfigEntry<float> MinimumVisibleDistance;
internal static ConfigEntry<float> MinimumVisibleDistanceHysteresis;
internal static ConfigEntry<float> SampleInterval;
internal static ConfigEntry<float> CloneUpdateInterval;
internal static ConfigEntry<float> PositionSmoothing;
internal static ConfigEntry<float> RotationSmoothing;
internal static ConfigEntry<bool> CopyScale;
internal static ConfigEntry<bool> CopyBlendShapes;
internal static ConfigEntry<Color> Clone1Color;
internal static ConfigEntry<Color> Clone2Color;
internal static ConfigEntry<Color> Clone3Color;
internal static ConfigEntry<bool> RainbowMode;
internal static ConfigEntry<float> RainbowSpeed;
internal static ConfigEntry<float> RainbowSaturation;
internal static ConfigEntry<bool> MirrorRendererToggles;
internal static ConfigEntry<bool> MirrorRendererActiveState;
internal static ConfigEntry<bool> MirrorMeshChanges;
internal static ConfigEntry<bool> ParticleSystemsVisible;
internal static ConfigEntry<bool> TrailRenderersVisible;
internal static ConfigEntry<float> ScanInterval;
internal static ConfigEntry<bool> DisableCloneShadows;
internal static ConfigEntry<bool> CloneUpdateWhenOffscreen;
private readonly Dictionary<int, Driver> drivers = new Dictionary<int, Driver>();
private readonly Dictionary<ushort, SyncedColors> remoteColors = new Dictionary<ushort, SyncedColors>();
private readonly Dictionary<ushort, RemotePresence> remotePresence = new Dictionary<ushort, RemotePresence>();
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<bool>("General", "Enabled", true, "Master toggle for runtime afterimages.");
ShowOtherPlayersAfterimages = ((BaseUnityPlugin)this).Config.Bind<bool>("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<float>("Clones", "Clone1Delay", 0.05f, "");
Clone2Delay = ((BaseUnityPlugin)this).Config.Bind<float>("Clones", "Clone2Delay", 0.1f, "");
Clone3Delay = ((BaseUnityPlugin)this).Config.Bind<float>("Clones", "Clone3Delay", 0.15f, "");
MinimumVisibleDistance = ((BaseUnityPlugin)this).Config.Bind<float>("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<float>("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<float>("Pose History", "SampleInterval", 0f, "0 = capture pose every rendered frame (recommended). Higher values cap sampling.");
CloneUpdateInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "CloneUpdateInterval", 0f, "0 = update all three afterimages every rendered frame (recommended).");
PositionSmoothing = ((BaseUnityPlugin)this).Config.Bind<float>("Smoothing", "PositionSmoothing", 16f, "");
RotationSmoothing = ((BaseUnityPlugin)this).Config.Bind<float>("Smoothing", "RotationSmoothing", 16f, "");
CopyScale = ((BaseUnityPlugin)this).Config.Bind<bool>("Smoothing", "CopyScale", true, "");
CopyBlendShapes = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance", "CopyBlendShapes", false, "Copies blendshape weights from the live CBB. Leave OFF for best FPS.");
DisableCloneShadows = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance", "DisableCloneShadows", true, "");
CloneUpdateWhenOffscreen = ((BaseUnityPlugin)this).Config.Bind<bool>("Performance", "CloneUpdateWhenOffscreen", false, "");
MirrorRendererToggles = ((BaseUnityPlugin)this).Config.Bind<bool>("Skin Switch / Toggles", "MirrorRendererToggles", true, "Mirrors source SkinnedMeshRenderer.enabled onto all 3 runtime clones.");
MirrorRendererActiveState = ((BaseUnityPlugin)this).Config.Bind<bool>("Skin Switch / Toggles", "MirrorRendererActiveState", true, "Hides a clone mesh when the matching source mesh object is inactive.");
MirrorMeshChanges = ((BaseUnityPlugin)this).Config.Bind<bool>("Skin Switch / Toggles", "MirrorMeshChanges", true, "Updates clone meshes when CharacterDefinition / CBB swaps a SkinnedMeshRenderer mesh.");
ParticleSystemsVisible = ((BaseUnityPlugin)this).Config.Bind<bool>("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<bool>("Character Effects", "TrailRenderersVisible", true, "Finds TrailRenderer components on the current character and lets you show/hide them.");
Clone1Color = ((BaseUnityPlugin)this).Config.Bind<Color>("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<Color>("Colors", "Clone2Color", new Color(0.59180313f, 0.8012104f, 93f / 106f, 0.2509804f), "Clone 2 RGBA.");
Clone3Color = ((BaseUnityPlugin)this).Config.Bind<Color>("Colors", "Clone3Color", new Color(0.6123621f, 99f / 106f, 0.8760166f, 0.2509804f), "Clone 3 RGBA.");
RainbowMode = ((BaseUnityPlugin)this).Config.Bind<bool>("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<float>("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<float>(0f, 2f), new object[0]));
RainbowSaturation = ((BaseUnityPlugin)this).Config.Bind<float>("Rainbow Mode", "Saturation", 0.45f, new ConfigDescription("Pastel strength. Lower values are paler/whiter; higher values are more vivid.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), new object[0]));
ScanInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced", "ScanInterval", 0.5f, "");
SceneManager.sceneLoaded += SceneLoaded;
try
{
ClientController.RegisterCustomPacketHandler("AfterImages.ColorSync.v1", (Action<ushort, byte[]>)OnAcnColorPacket);
ClientController.RegisterCustomPacketHandler("AfterImages.Presence.v1", (Action<ushort, byte[]>)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<int, Driver> 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<int> list = null;
foreach (KeyValuePair<int, Driver> driver in drivers)
{
Driver value = driver.Value;
if ((Object)(object)value == (Object)null || value.PlayerGone)
{
if (list == null)
{
list = new List<int>();
}
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<int>();
}
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<Player>();
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<Driver>();
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<SkinnedMeshRenderer>(true);
if (componentsInChildren == null || componentsInChildren.Length == 0)
{
return null;
}
Dictionary<Transform, int> dictionary = new Dictionary<Transform, int>();
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<Transform, int> 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<ushort, MPPlayer> 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<int, Driver> 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<SkinnedMeshRenderer> clones = new List<SkinnedMeshRenderer>();
}
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<RendererMap> rendererMaps = new List<RendererMap>();
private readonly List<Material> ownedMaterials = new List<Material>();
private readonly List<EffectRendererState> particleStates = new List<EffectRendererState>();
private readonly List<EffectRendererState> trailStates = new List<EffectRendererState>();
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<Transform>(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<SkinnedMeshRenderer>(true);
Dictionary<Transform, int> dictionary = new Dictionary<Transform, int>();
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<Transform, int> 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<Transform>(true);
List<Transform> list = new List<Transform>();
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<Transform, Transform> dictionary = new Dictionary<Transform, Transform>();
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<SkinnedMeshRenderer>(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<SkinnedMeshRenderer>();
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<Transform, Transform> 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<ParticleSystem>(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<SkinnedMeshRenderer>() == (Object)null)
{
gameObject.SetActive(false);
}
}
}
ParticleSystemRenderer[] componentsInChildren2 = runtimeRoot.GetComponentsInChildren<ParticleSystemRenderer>(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<TrailRenderer>(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<SkinnedMeshRenderer>(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<string> list = new List<string>();
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;
}
}