Decompiled source of pixelsmokefx v1.1.0

BepInEx/plugins/PixelSmokeFx.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: AssemblyProduct("Pixel Smoke Fx")]
[assembly: AssemblyDescription("Low-poly 3D smoke for Ravenfield bullet hits - zero assets, zero particles. Player-fire exemption, distance/frustum culling, pooled.")]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyTitle("Pixel Smoke Fx")]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("1.1.0.0")]
namespace PixelSmokeFx;

[BepInPlugin("com.local.pixelsmokefx", "Pixel Smoke Fx", "1.1.0")]
public class FxPlugin : BaseUnityPlugin
{
	public static class SpawnDecal_Patch
	{
		private static void Prefix(Projectile __instance, RaycastHit hitInfo)
		{
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_0287: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: 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_00f5: 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)
			try
			{
				if (!EnabledCfg)
				{
					return;
				}
				if ((Object)(object)TemplatePlugin == (Object)null)
				{
					if (++_hitNoTemplate % 500 == 0 && Log != null)
					{
						Log.LogWarning((object)("PixelSmokeFx: " + _hitNoTemplate + " hits arrived while template missing"));
					}
				}
				else
				{
					if (__instance is ExplodingProjectile)
					{
						return;
					}
					string text = (((Object)(object)__instance.sourceWeapon != (Object)null) ? ((Object)__instance.sourceWeapon).name : null);
					if (TargetWeapon.Length > 0 && (string.IsNullOrEmpty(text) || text.IndexOf(TargetWeapon, StringComparison.OrdinalIgnoreCase) < 0))
					{
						return;
					}
					Vector3 point = ((RaycastHit)(ref hitInfo)).point;
					Vector3 normal = ((RaycastHit)(ref hitInfo)).normal;
					bool flag = AlwaysRenderPlayer && IsPlayerFire(__instance);
					Camera val = MainCam();
					float num = (((Object)(object)val != (Object)null) ? Vector3.Distance(((Component)val).transform.position, point) : 0f);
					if ((!flag && num >= HalfDist) || (!flag && !IsOnScreen(point)))
					{
						return;
					}
					float time = Time.time;
					string key = ((text != null) ? text : "");
					if (!_lastGunTime.TryGetValue(key, out var value) || !(time - value < BurstWindow))
					{
						if (time - _lastGlobalTime < GlobalCooldown || (_lastGunTime.TryGetValue(key, out value) && time - value < GunCooldown))
						{
							return;
						}
						_lastGlobalTime = time;
					}
					_lastGunTime[key] = time;
					float caliber = 800f;
					float num2 = -1f;
					try
					{
						if (__instance.configuration != null)
						{
							Type type = ((object)__instance.configuration).GetType();
							num2 = ReadDamage(__instance.configuration);
							if (num2 < 0f)
							{
								num2 = ReadDamage(__instance);
							}
							if (_speedProp == null || _speedPropType != type)
							{
								_speedPropType = type;
								_speedProp = type.GetProperty("speed");
							}
							if (_speedProp != null)
							{
								object value2 = _speedProp.GetValue(__instance.configuration, null);
								if (value2 != null && value2 is float && (float)value2 > 50f)
								{
									caliber = (float)value2;
								}
							}
						}
					}
					catch (Exception)
					{
					}
					Transform transform = ((Component)__instance).transform;
					Vector3 flight = (((Object)(object)transform != (Object)null) ? transform.forward : Vector3.up);
					SpawnEffect(key, point, normal, flight, num, caliber, num2);
				}
			}
			catch (Exception ex2)
			{
				if (Log != null)
				{
					Log.LogWarning((object)("PixelSmokeFx: spawn: " + ex2));
				}
			}
		}
	}

	private const int TEMPLATE_ATTEMPT_LIMIT = 10;

	internal static FxPlugin Instance;

	internal static ManualLogSource Log;

	internal static string TargetWeapon = "";

	internal static int PoolSize = 120;

	internal static float GlobalCooldown = 0.005f;

	internal static float GunCooldown = 0.02f;

	internal static float BurstWindow = 0.08f;

	internal static float FullDist = 60f;

	internal static float HalfDist = 200f;

	internal static float Lifetime = 1.5f;

	internal static float ExpandTime = 0.15f;

	internal static float DecayRate = 2.2f;

	internal static float BaseScale = 0.7f;

	internal static float DamageRef = 40f;

	internal static float MinSizeFactor = 0.5f;

	internal static float MaxSizeFactor = 1.5f;

	internal static float ShotgunFactor = 0.25f;

	internal static bool EnabledCfg = true;

	internal static bool AlwaysRenderPlayer = true;

	internal static bool AutoTestCfg;

	private static bool _autoTestStarted;

	internal static GameObject TemplatePlugin;

	private static float _nextTemplateAttempt = -100f;

	private static int _templateAttempts;

	private static bool _templateFailedPermanent;

	private static Plane[] _planes;

	private static int _planeFrame = -1;

	private static PropertyInfo _speedProp;

	private static Type _speedPropType;

	private static MemberInfo _dmgMember;

	private static Type _dmgMemberType;

	private static float _lastGlobalTime = -1f;

	private static readonly Dictionary<string, float> _lastGunTime = new Dictionary<string, float>();

	internal static int SpawnCount;

	private static int _spawnLogThrottle;

	internal static int _hitNoTemplate;

	private static Camera _cachedMain;

	private static int _cachedMainFrame = -1;

	private static FieldInfo _firedByPlayerField;

	private static bool _firedByPlayerResolved;

	private static MethodInfo _userIsPlayerMethod;

	private static bool _userIsPlayerResolved;

	private static bool _playerFireLogged;

	private readonly List<GameObject> _pool = new List<GameObject>();

	private readonly Dictionary<GameObject, SmokeBlob> _slotBlobs = new Dictionary<GameObject, SmokeBlob>();

	private bool _toppingUp;

	internal static float ReadDamage(object target)
	{
		if (target == null)
		{
			return -1f;
		}
		Type type = target.GetType();
		if (_dmgMember == null || _dmgMemberType != type)
		{
			_dmgMemberType = type;
			_dmgMember = FindDamageMember(type);
		}
		if (_dmgMember == null)
		{
			return -1f;
		}
		try
		{
			object obj = ((_dmgMember is PropertyInfo) ? ((PropertyInfo)_dmgMember).GetValue(target, null) : ((FieldInfo)_dmgMember).GetValue(target));
			if (obj == null)
			{
				return -1f;
			}
			float num = Convert.ToSingle(obj);
			return (num > 0f) ? num : (-1f);
		}
		catch (Exception)
		{
			return -1f;
		}
	}

	private static MemberInfo FindDamageMember(Type t)
	{
		string[] array = new string[5] { "damage", "damageMax", "maxDamage", "damagePerProjectile", "baseDamage" };
		try
		{
			string[] array2 = array;
			foreach (string name in array2)
			{
				PropertyInfo property = t.GetProperty(name);
				if (property != null && (property.PropertyType == typeof(float) || property.PropertyType == typeof(int)))
				{
					return property;
				}
			}
			string[] array3 = array;
			foreach (string name2 in array3)
			{
				FieldInfo field = t.GetField(name2);
				if (field != null && (field.FieldType == typeof(float) || field.FieldType == typeof(int)))
				{
					return field;
				}
			}
		}
		catch (Exception)
		{
		}
		return null;
	}

	internal static bool IsPlayerFire(Projectile proj)
	{
		try
		{
			if (!_firedByPlayerResolved)
			{
				_firedByPlayerResolved = true;
				_firedByPlayerField = AccessTools.Field(typeof(Projectile), "firedByPlayer");
				if (Log != null && !_playerFireLogged)
				{
					_playerFireLogged = true;
					Log.LogInfo((object)("PixelSmokeFx: player-fire resolver uses " + ((_firedByPlayerField != null) ? "Projectile.firedByPlayer" : "Weapon.UserIsPlayer() fallback")));
				}
			}
			if (_firedByPlayerField != null)
			{
				object value = _firedByPlayerField.GetValue(proj);
				if (value != null && value is bool result)
				{
					return result;
				}
			}
			if ((Object)(object)proj.sourceWeapon != (Object)null)
			{
				if (!_userIsPlayerResolved)
				{
					_userIsPlayerResolved = true;
					_userIsPlayerMethod = AccessTools.Method(typeof(Weapon), "UserIsPlayer", (Type[])null, (Type[])null);
				}
				if (_userIsPlayerMethod != null)
				{
					object obj = _userIsPlayerMethod.Invoke(proj.sourceWeapon, null);
					if (obj != null && obj is bool result2)
					{
						return result2;
					}
				}
			}
		}
		catch (Exception)
		{
		}
		return false;
	}

	private static float SafeFloat(string key, float value, float min, float max, float fallback)
	{
		if (float.IsNaN(value) || float.IsInfinity(value))
		{
			Log.LogWarning((object)("PixelSmokeFx: cfg " + key + " is not a number (" + value + "), using " + fallback));
			return fallback;
		}
		if (value < min || value > max)
		{
			Log.LogWarning((object)("PixelSmokeFx: cfg " + key + "=" + value + " out of range [" + min + "," + max + "], clamped"));
			return Mathf.Clamp(value, min, max);
		}
		return value;
	}

	private static int SafeInt(string key, int value, int min, int max, int fallback)
	{
		if (value < min || value > max)
		{
			Log.LogWarning((object)("PixelSmokeFx: cfg " + key + "=" + value + " out of range [" + min + "," + max + "], clamped"));
			return Math.Max(min, Math.Min(max, value));
		}
		return value;
	}

	private void Awake()
	{
		//IL_05ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_05d4: Expected O, but got Unknown
		//IL_063f: Unknown result type (might be due to invalid IL or missing references)
		//IL_064d: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		try
		{
			EnabledCfg = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch.").Value;
			TargetWeapon = ((BaseUnityPlugin)this).Config.Bind<string>("General", "TargetWeapon", "", "Weapon-name substring that sourceWeapon.name must contain; empty = all non-explosive guns.").Value;
			AlwaysRenderPlayer = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AlwaysRenderPlayer", true, "Effects from shots FIRED BY THE PLAYER render regardless of distance and viewport; other shots keep distance + frustum culling.").Value;
			PoolSize = SafeInt("PoolSize", ((BaseUnityPlugin)this).Config.Bind<int>("Performance", "PoolSize", 120, "Pool slots (= concurrent alive blob cap).").Value, 1, 400, 120);
			GlobalCooldown = SafeFloat("GlobalCooldown", ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "GlobalCooldown", 0.005f, "Min seconds between any two bursts (same-burst hits bypass this).").Value, 0f, 10f, 0.005f);
			GunCooldown = SafeFloat("GunCooldown", ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "GunCooldown", 0.02f, "Min seconds between bursts of the same weapon (auto-fire pacifier).").Value, 0f, 10f, 0.02f);
			BurstWindow = SafeFloat("BurstWindow", ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "BurstWindow", 0.08f, "Hits from one weapon within this many seconds count as ONE shot (shotgun pellets land on different frames when angled) and all render.").Value, 0f, 10f, 0.08f);
			FullDist = SafeFloat("FullDist", ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "FullDist", 60f, "Blobs closer than this are full size.").Value, 1f, 10000f, 60f);
			HalfDist = Math.Max(FullDist, SafeFloat("HalfDist", ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "HalfDist", 200f, "Blobs closer than this are half size; further = skipped.").Value, 1f, 20000f, 200f));
			Lifetime = SafeFloat("Lifetime", ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "Lifetime", 1.5f, "Seconds a blob lives (alpha fades 1 -> 0 linearly across this).").Value, 0.1f, 60f, 1.5f);
			ExpandTime = SafeFloat("ExpandTime", ((BaseUnityPlugin)this).Config.Bind<float>("Fx", "ExpandTime", 0.15f, "Rapid-expansion window: scale 0.04 -> 1.0 over this many seconds.").Value, 0.01f, 30f, 0.15f);
			DecayRate = SafeFloat("DecayRate", ((BaseUnityPlugin)this).Config.Bind<float>("Fx", "DecayRate", 2.2f, "Exponential shrink rate after ExpandTime (higher = snappier).").Value, 0.05f, 100f, 2.2f);
			BaseScale = SafeFloat("BaseScale", ((BaseUnityPlugin)this).Config.Bind<float>("Fx", "BaseScale", 0.7f, "Master blob size multiplier (damage/speed + distance LOD still scale it).").Value, 0.01f, 100f, 0.7f);
			DamageRef = SafeFloat("DamageRef", ((BaseUnityPlugin)this).Config.Bind<float>("Fx", "DamageRef", 40f, "Projectile damage that maps to size factor 1.0 (rifle-size smoke).").Value, 1f, 10000f, 40f);
			MinSizeFactor = SafeFloat("MinSizeFactor", ((BaseUnityPlugin)this).Config.Bind<float>("Fx", "MinSizeFactor", 0.5f, "Smallest damage size factor (shotgun pellets - many hits per shot - can shrink to 0.25).").Value, 0.05f, 1f, 0.5f);
			MaxSizeFactor = SafeFloat("MaxSizeFactor", ((BaseUnityPlugin)this).Config.Bind<float>("Fx", "MaxSizeFactor", 1.5f, "Largest damage size factor (sniper hits billow big).").Value, 0.1f, 10f, 1.5f);
			ShotgunFactor = SafeFloat("ShotgunFactor", ((BaseUnityPlugin)this).Config.Bind<float>("Fx", "ShotgunFactor", 0.25f, "Size floor for low-damage pellets (shotguns fire many; each pellet stays tiny).").Value, 0.05f, 1f, 0.25f);
			AutoTestCfg = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "AutoTest", false, "True = run built-in AutoTest once at boot (spawns blobs at several distances/angles in front of the camera, captures screenshots).").Value;
			Log.LogInfo((object)("PixelSmokeFx: cfg Target='" + TargetWeapon + "' Pool=" + PoolSize + " cd=" + GlobalCooldown + "/" + GunCooldown + " burst=" + BurstWindow + " dist=" + FullDist + "/" + HalfDist + " expand=" + ExpandTime + " decay=" + DecayRate + " base=" + BaseScale + " dmgRef=" + DamageRef + " size=" + MinSizeFactor + "/" + MaxSizeFactor + " pellet=" + ShotgunFactor));
		}
		catch (Exception ex)
		{
			Log.LogError((object)("PixelSmokeFx cfg: " + ex));
		}
		try
		{
			Harmony val = new Harmony("com.local.pixelsmokefx");
			MethodInfo methodInfo = AccessTools.Method(typeof(Projectile), "SpawnDecal", (Type[])null, (Type[])null);
			MethodInfo method = typeof(SpawnDecal_Patch).GetMethod("Prefix", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (methodInfo == null)
			{
				Log.LogError((object)"PixelSmokeFx: target Projectile::SpawnDecal not found");
				return;
			}
			if (method == null)
			{
				Log.LogError((object)"PixelSmokeFx: prefix method not found");
				return;
			}
			val.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			bool flag = false;
			foreach (MethodBase patchedMethod in val.GetPatchedMethods())
			{
				if (patchedMethod != null && patchedMethod.DeclaringType == typeof(Projectile) && patchedMethod.Name == "SpawnDecal")
				{
					flag = true;
					break;
				}
			}
			if (flag)
			{
				Log.LogInfo((object)"PixelSmokeFx: harmony hook CONFIRMED on Projectile::SpawnDecal");
			}
			else
			{
				Log.LogError((object)"PixelSmokeFx: harmony hook NOT registered (SpawnDecal missing from patched list)");
			}
		}
		catch (Exception ex2)
		{
			Log.LogError((object)("PixelSmokeFx: harmony patch failed: " + ex2));
		}
	}

	private void Update()
	{
		if ((Object)(object)TemplatePlugin == (Object)null && !_templateFailedPermanent && Time.time >= _nextTemplateAttempt)
		{
			_templateAttempts++;
			_nextTemplateAttempt = Time.time + ((_templateAttempts <= 5) ? 3f : 30f);
			if (_templateAttempts > 10)
			{
				_templateFailedPermanent = true;
				try
				{
					if (Log != null)
					{
						Log.LogWarning((object)("PixelSmokeFx: mesh template failed " + 10 + " times - disabled. Delete BepInEx/config/com.local.pixelsmokefx.cfg and check the log."));
					}
					return;
				}
				catch (Exception)
				{
					return;
				}
			}
			LoadTemplate();
		}
		if (AutoTestCfg && !_autoTestStarted)
		{
			_autoTestStarted = true;
			((MonoBehaviour)this).StartCoroutine(AutoTestRoutine());
		}
	}

	private IEnumerator AutoTestRoutine()
	{
		Camera cam = null;
		bool textOnly = true;
		float deadline = Time.time + 60f;
		float lastDiag = 0f;
		while (Time.time < deadline)
		{
			cam = MainCam();
			if ((Object)(object)cam == (Object)null)
			{
				cam = FallbackTestCam();
				if ((Object)(object)cam != (Object)null)
				{
					textOnly = ((Object)cam).name != null && ((Object)cam).name.IndexOf("Text", StringComparison.OrdinalIgnoreCase) >= 0;
					if (!textOnly)
					{
						break;
					}
					cam = null;
				}
			}
			else
			{
				textOnly = ((Object)cam).name != null && ((Object)cam).name.IndexOf("Text", StringComparison.OrdinalIgnoreCase) >= 0;
				if (!textOnly)
				{
					break;
				}
			}
			if (Time.time - lastDiag > 10f)
			{
				lastDiag = Time.time;
				try
				{
					Camera[] array = Object.FindObjectsOfType<Camera>();
					string[] array2 = new string[array.Length];
					for (int i = 0; i < array.Length; i++)
					{
						array2[i] = ((Object)array[i]).name + "[" + (((Component)array[i]).tag ?? "?") + "," + (((Behaviour)array[i]).enabled ? "on" : "off") + "]";
					}
					Log.LogInfo((object)("PixelSmokeFx: AutoTest waiting for camera, found: " + string.Join(", ", array2)));
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("PixelSmokeFx AutoTest cam diag: " + ex));
				}
			}
			yield return null;
		}
		if ((Object)(object)cam == (Object)null)
		{
			Log.LogWarning((object)"PixelSmokeFx: AutoTest aborted - no camera within 60s");
			_autoTestStarted = true;
			yield break;
		}
		if (textOnly)
		{
			Log.LogWarning((object)"PixelSmokeFx: AutoTest proceeding with text/loading camera - shots may be blank");
		}
		Log.LogInfo((object)("PixelSmokeFx: AutoTest start, camera=" + ((Object)cam).name));
		string dir = Path.Combine(Paths.BepInExRootPath, "PixelSmokeFxTest", DateTime.Now.ToString("yyyyMMdd_HHmmss"));
		try
		{
			Directory.CreateDirectory(dir);
		}
		catch (Exception ex2)
		{
			Log.LogError((object)("PixelSmokeFx: AutoTest dir: " + ex2));
			dir = null;
		}
		float[] dists = new float[3] { 8f, 24f, 60f };
		try
		{
			bool built;
			try
			{
				built = SmokeMeshFactory.BuildMainTemplate();
			}
			catch (Exception ex3)
			{
				Log.LogError((object)("PixelSmokeFx: AutoTest template build: " + ex3));
				built = false;
			}
			if (!built)
			{
				Log.LogWarning((object)"PixelSmokeFx: AutoTest template build failed, skipped");
				yield break;
			}
			TemplatePlugin = SmokeMeshFactory.TemplateRoot;
			RebuildPoolForTemplate();
			Log.LogInfo((object)"PixelSmokeFx: AutoTest evaluating mesh template");
			Vector3 fwd = ((Component)cam).transform.forward;
			Vector3 right = ((Component)cam).transform.right;
			for (int d = 0; d < dists.Length; d++)
			{
				Vector3 basePos = ((Component)cam).transform.position + fwd * dists[d];
				Vector3[] burstPts = (Vector3[])(object)new Vector3[3];
				Vector3[] burstNs = (Vector3[])(object)new Vector3[3];
				float[] offsets = new float[3] { -2.2f, 0f, 2.2f };
				float[] yOff = new float[3] { -0.4f, 0f, 0.4f };
				for (int j = 0; j < 3; j++)
				{
					Vector3 val = basePos + right * offsets[j] + Vector3.up * yOff[j];
					Vector3 val2 = ((Component)cam).transform.position - val;
					Vector3 normalized = ((Vector3)(ref val2)).normalized;
					Vector3 val3;
					switch (j)
					{
					default:
					{
						Vector3 val4 = Vector3.up + normalized;
						val3 = ((Vector3)(ref val4)).normalized;
						break;
					}
					case 1:
						val3 = normalized;
						break;
					case 0:
						val3 = Vector3.up;
						break;
					}
					Vector3 val5 = val3;
					burstPts[j] = val;
					burstNs[j] = val5;
					SpawnEffect("autotest", val, val5, fwd, dists[d], 800f, 40f);
				}
				yield return (object)new WaitForSeconds(0.4f);
				if (dir == null)
				{
					continue;
				}
				string file = Path.Combine(dir, "d" + dists[d] + "m.png");
				float content = -1f;
				for (int attempt = 0; attempt < 8; attempt++)
				{
					for (int k = 0; k < 3; k++)
					{
						SpawnEffect("autotest", burstPts[k], burstNs[k], fwd, dists[d], 800f, 40f);
					}
					yield return (object)new WaitForSeconds(0.4f);
					TakeScreenshot(file);
					yield return (object)new WaitForEndOfFrame();
					Thread.Sleep(120);
					content = ScreenshotContent(file);
					if (content >= 0.25f)
					{
						break;
					}
					Log.LogInfo((object)("PixelSmokeFx: AutoTest re-shoot " + (attempt + 1) + " for " + file + " (content=" + content + ")"));
					try
					{
						if (File.Exists(file))
						{
							File.Delete(file);
						}
					}
					catch (Exception)
					{
					}
				}
				Log.LogInfo((object)("PixelSmokeFx: AutoTest shot -> " + file + " (content=" + content + ")"));
			}
			yield return (object)new WaitForSeconds(0.8f);
		}
		finally
		{
			for (int array = _pool.Count - 1; array >= 0; array--)
			{
				GameObject array2 = _pool[array];
				if ((Object)(object)array2 != (Object)null)
				{
					DropSlot(array2);
					Object.Destroy((Object)(object)array2);
				}
			}
			_pool.Clear();
			Instance._slotBlobs.Clear();
			TemplatePlugin = null;
			SmokeMeshFactory.ReleaseTemplate();
			_nextTemplateAttempt = 0f;
			_templateAttempts = 0;
			_templateFailedPermanent = false;
			if (dir != null)
			{
				Log.LogInfo((object)("PixelSmokeFx: AutoTest done - screenshots in " + dir));
			}
			else
			{
				Log.LogInfo((object)"PixelSmokeFx: AutoTest done (no screenshots taken)");
			}
		}
	}

	private void RebuildPoolForTemplate()
	{
		for (int num = _pool.Count - 1; num >= 0; num--)
		{
			GameObject val = _pool[num];
			if ((Object)(object)val != (Object)null)
			{
				DropSlot(val);
				Object.Destroy((Object)(object)val);
			}
		}
		_pool.Clear();
		_slotBlobs.Clear();
		if (!((Object)(object)TemplatePlugin == (Object)null))
		{
			int num2 = Math.Min(PoolSize, 12);
			for (int i = 0; i < num2; i++)
			{
				GameObject val2 = Object.Instantiate<GameObject>(TemplatePlugin);
				Object.DontDestroyOnLoad((Object)(object)val2);
				val2.SetActive(false);
				_pool.Add(val2);
			}
			Log.LogInfo((object)("PixelSmokeFx: AutoTest pool rebuilt (" + num2 + ")"));
		}
	}

	private static float ScreenshotContent(string path)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Expected O, but got Unknown
		//IL_0054: 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)
		try
		{
			if (!File.Exists(path))
			{
				return -1f;
			}
			Texture2D val = new Texture2D(2, 2);
			if (!ImageConversion.LoadImage(val, File.ReadAllBytes(path)))
			{
				Object.Destroy((Object)(object)val);
				return -1f;
			}
			Color32[] pixels = val.GetPixels32();
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < pixels.Length; i += 5)
			{
				Color32 val2 = pixels[i];
				if (val2.r < 70 && val2.g < 70 && val2.b < 70)
				{
					num++;
				}
				if (val2.r > 110 && val2.g > 110 && val2.b > 110)
				{
					num2++;
				}
			}
			Object.Destroy((Object)(object)val);
			int num3 = (pixels.Length + 4) / 5;
			float num4 = (float)num / (float)num3;
			float num5 = (float)num2 / (float)num3;
			return Mathf.Min(1f, num4 * 2.5f) + ((num5 > 0.15f) ? 0.5f : 0f);
		}
		catch (Exception ex)
		{
			if (Log != null)
			{
				Log.LogWarning((object)("PixelSmokeFx: content check " + path + ": " + ex));
			}
			return -1f;
		}
	}

	private static void TakeScreenshot(string path)
	{
		try
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			Type type = null;
			for (int i = 0; i < assemblies.Length; i++)
			{
				if (!(type == null))
				{
					break;
				}
				if (assemblies[i].FullName.StartsWith("UnityEngine.ScreenCaptureModule", StringComparison.Ordinal))
				{
					type = assemblies[i].GetType("UnityEngine.ScreenCapture");
				}
			}
			if (type == null)
			{
				Log.LogWarning((object)"PixelSmokeFx: ScreenCapture type not found, no shot");
				return;
			}
			MethodInfo method = type.GetMethod("CaptureScreenshot", new Type[2]
			{
				typeof(string),
				typeof(int)
			});
			if (method == null)
			{
				method = type.GetMethod("CaptureScreenshot", new Type[1] { typeof(string) });
			}
			if (method == null)
			{
				Log.LogWarning((object)"PixelSmokeFx: CaptureScreenshot not found, no shot");
				return;
			}
			object[] parameters = ((method.GetParameters().Length == 2) ? new object[2] { path, 1 } : new object[1] { path });
			method.Invoke(null, parameters);
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("PixelSmokeFx: screenshot " + path + ": " + ex));
		}
	}

	private void LoadTemplate()
	{
		try
		{
			if (SmokeMeshFactory.BuildMainTemplate())
			{
				TemplatePlugin = SmokeMeshFactory.TemplateRoot;
				Log.LogInfo((object)"PixelSmokeFx: mesh template ready");
				((MonoBehaviour)this).StartCoroutine(WarmPool());
			}
			else
			{
				Log.LogError((object)"PixelSmokeFx: mesh template build failed, retrying in 3s");
			}
		}
		catch (Exception ex)
		{
			Log.LogError((object)("PixelSmokeFx: template load failed: " + ex));
		}
	}

	private IEnumerator WarmPool()
	{
		while (_pool.Count < PoolSize && (Object)(object)TemplatePlugin != (Object)null)
		{
			GameObject go = Object.Instantiate<GameObject>(TemplatePlugin);
			Object.DontDestroyOnLoad((Object)(object)go);
			go.SetActive(false);
			_pool.Add(go);
			if (_pool.Count % 10 == 0)
			{
				yield return null;
			}
		}
		Log.LogInfo((object)("PixelSmokeFx: pool warmed to " + _pool.Count));
	}

	internal static SmokeBlob BlobOf(GameObject go)
	{
		if (Instance._slotBlobs.TryGetValue(go, out var value))
		{
			return value;
		}
		value = go.GetComponent<SmokeBlob>();
		if ((Object)(object)value == (Object)null)
		{
			value = go.AddComponent<SmokeBlob>();
		}
		Instance._slotBlobs[go] = value;
		return value;
	}

	private static void DropSlot(GameObject go)
	{
		if ((Object)(object)Instance != (Object)null && go != null)
		{
			Instance._slotBlobs.Remove(go);
		}
	}

	internal static GameObject GetFromPool()
	{
		List<GameObject> pool = Instance._pool;
		for (int num = pool.Count - 1; num >= 0; num--)
		{
			if ((Object)(object)pool[num] == (Object)null)
			{
				DropSlot(pool[num]);
				pool.RemoveAt(num);
			}
			else if (!pool[num].activeSelf)
			{
				return pool[num];
			}
		}
		return null;
	}

	private IEnumerator TopUpPool()
	{
		while (_pool.Count < PoolSize && (Object)(object)TemplatePlugin != (Object)null)
		{
			GameObject go = Object.Instantiate<GameObject>(TemplatePlugin);
			Object.DontDestroyOnLoad((Object)(object)go);
			go.SetActive(false);
			_pool.Add(go);
			yield return null;
		}
	}

	internal static void RequestTopUp()
	{
		if (!Instance._toppingUp)
		{
			Instance._toppingUp = true;
			((MonoBehaviour)Instance).StartCoroutine(Instance.TopUpPoolAndUnset());
		}
	}

	private IEnumerator TopUpPoolAndUnset()
	{
		yield return TopUpPool();
		_toppingUp = false;
	}

	internal static void SpawnEffect(string key, Vector3 point, Vector3 normal, Vector3 flight, float dist, float caliber, float damage)
	{
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: 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)
		try
		{
			if ((Object)(object)TemplatePlugin == (Object)null)
			{
				return;
			}
			float num = ((dist < FullDist) ? 1f : 0.5f);
			float num4;
			if (damage > 0f)
			{
				float num2 = ((damage * 2f < DamageRef) ? ShotgunFactor : MinSizeFactor);
				float num3 = Mathf.Clamp(damage / DamageRef, num2, MaxSizeFactor);
				num4 = num3 * 0.7f * num * BaseScale;
			}
			else
			{
				num4 = Mathf.Clamp((float)Math.Sqrt(caliber / 800f) * 0.7f, 0.45f, 1.6f) * num * BaseScale;
			}
			num4 = Mathf.Clamp(num4, 0.001f, 3f);
			GameObject fromPool = GetFromPool();
			if ((Object)(object)fromPool == (Object)null)
			{
				RequestTopUp();
				return;
			}
			fromPool.transform.position = point + normal * 0.3f;
			fromPool.transform.rotation = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f);
			SmokeBlob smokeBlob = BlobOf(fromPool);
			smokeBlob.Launch(num4, Lifetime);
			fromPool.SetActive(true);
			SpawnCount++;
			if (++_spawnLogThrottle % 200 == 0 && Log != null)
			{
				Log.LogInfo((object)("PixelSmokeFx: spawned " + SpawnCount + " total, key=" + key + " dist=" + dist.ToString("F0") + " cal=" + caliber.ToString("F0") + " scale=" + num4.ToString("F2")));
			}
		}
		catch (Exception ex)
		{
			if (Log != null)
			{
				Log.LogWarning((object)("PixelSmokeFx: spawn: " + ex));
			}
		}
	}

	internal static Camera MainCam()
	{
		if ((Object)(object)_cachedMain == (Object)null || _cachedMainFrame != Time.frameCount)
		{
			_cachedMain = Camera.main;
			_cachedMainFrame = Time.frameCount;
		}
		return _cachedMain;
	}

	private static Camera FallbackTestCam()
	{
		Camera val = null;
		try
		{
			Camera[] array = Object.FindObjectsOfType<Camera>();
			foreach (Camera val2 in array)
			{
				if ((Object)(object)val2 == (Object)null || !((Behaviour)val2).enabled)
				{
					continue;
				}
				string name = ((Object)val2).name;
				if (name == null || (name.IndexOf("Text", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Icon", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("Post-process", StringComparison.OrdinalIgnoreCase) < 0))
				{
					if ((Object)(object)val == (Object)null)
					{
						val = val2;
					}
					if (name != null && name.IndexOf("Main", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return val2;
					}
				}
			}
		}
		catch (Exception)
		{
		}
		return val;
	}

	internal static bool IsOnScreen(Vector3 point)
	{
		//IL_003e: 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_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		Camera val = MainCam();
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		if (_planes == null || _planeFrame != Time.frameCount)
		{
			_planes = GeometryUtility.CalculateFrustumPlanes(val);
			_planeFrame = Time.frameCount;
		}
		return GeometryUtility.TestPlanesAABB(_planes, new Bounds(point, Vector3.one * 0.4f));
	}
}
public static class SmokeMeshFactory
{
	public static GameObject TemplateRoot;

	private static Material _sharedMat;

	private static readonly Dictionary<int, Mesh> _cache = new Dictionary<int, Mesh>();

	private static readonly int[] PUFF_SEEDS = new int[3] { 20260808, 20260809, 20260810 };

	internal static readonly float[] PUFF_BASE_SCALE = new float[3] { 1f, 0.62f, 0.45f };

	internal static readonly float[] PUFF_DELAY = new float[3] { 0f, 0.1f, 0.16f };

	public static bool BuildMainTemplate()
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Expected O, but got Unknown
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: 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)
		try
		{
			Material val = MakeBlobMaterial();
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			_sharedMat = val;
			GameObject val2 = new GameObject("PixelSmokeFxBlobExplosion");
			val2.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)val2);
			MakePuff(val2, "SmokePuff0", new Vector3(0f, 0f, 0f), GetOrBuild(PUFF_SEEDS[0], 3));
			MakePuff(val2, "SmokePuff1", new Vector3(0.26f, -0.1f, 0.14f), GetOrBuild(PUFF_SEEDS[1], 3));
			MakePuff(val2, "SmokePuff2", new Vector3(-0.24f, 0.07f, 0.12f), GetOrBuild(PUFF_SEEDS[2], 3));
			val2.AddComponent<SmokeBlob>();
			TemplateRoot = val2;
			return true;
		}
		catch (Exception ex)
		{
			if (FxPlugin.Log != null)
			{
				FxPlugin.Log.LogWarning((object)("PixelSmokeFx: mesh template build: " + ex));
			}
			return false;
		}
	}

	private static GameObject MakePuff(GameObject parent, string name, Vector3 localPos, Mesh mesh)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		//IL_001f: 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)
		GameObject val = new GameObject(name);
		val.transform.SetParent(parent.transform, false);
		val.transform.localPosition = localPos;
		val.transform.localRotation = Quaternion.identity;
		MeshFilter val2 = val.AddComponent<MeshFilter>();
		val2.sharedMesh = mesh;
		MeshRenderer val3 = val.AddComponent<MeshRenderer>();
		((Renderer)val3).sharedMaterial = _sharedMat;
		return val;
	}

	public static void ReleaseTemplate()
	{
		if ((Object)(object)TemplateRoot != (Object)null)
		{
			Object.Destroy((Object)(object)TemplateRoot);
		}
		TemplateRoot = null;
		if ((Object)(object)_sharedMat != (Object)null)
		{
			Object.Destroy((Object)(object)_sharedMat);
		}
		_sharedMat = null;
	}

	private static Mesh GetOrBuild(int seed, int subdiv)
	{
		if (_cache.TryGetValue(seed, out var value) && (Object)(object)value != (Object)null)
		{
			return value;
		}
		value = BuildIcosphere(seed, subdiv);
		_cache[seed] = value;
		return value;
	}

	private static Mesh BuildIcosphere(int seed, int subdiv)
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: 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_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: 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_009a: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: 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_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: 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_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0151: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: Unknown result type (might be due to invalid IL or missing references)
		//IL_015a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_017a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0191: Unknown result type (might be due to invalid IL or missing references)
		//IL_0196: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_020c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0211: Unknown result type (might be due to invalid IL or missing references)
		//IL_022d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0232: Unknown result type (might be due to invalid IL or missing references)
		//IL_024e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0253: Unknown result type (might be due to invalid IL or missing references)
		//IL_026f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0274: Unknown result type (might be due to invalid IL or missing references)
		//IL_0290: Unknown result type (might be due to invalid IL or missing references)
		//IL_0295: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0331: Unknown result type (might be due to invalid IL or missing references)
		//IL_0336: Unknown result type (might be due to invalid IL or missing references)
		//IL_0340: Unknown result type (might be due to invalid IL or missing references)
		//IL_0345: Unknown result type (might be due to invalid IL or missing references)
		//IL_034a: Unknown result type (might be due to invalid IL or missing references)
		//IL_034e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0353: Unknown result type (might be due to invalid IL or missing references)
		//IL_0358: Unknown result type (might be due to invalid IL or missing references)
		//IL_0366: Unknown result type (might be due to invalid IL or missing references)
		//IL_0371: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_0413: Unknown result type (might be due to invalid IL or missing references)
		//IL_0418: Unknown result type (might be due to invalid IL or missing references)
		//IL_0422: Unknown result type (might be due to invalid IL or missing references)
		//IL_0427: Unknown result type (might be due to invalid IL or missing references)
		//IL_0431: Unknown result type (might be due to invalid IL or missing references)
		//IL_0436: Unknown result type (might be due to invalid IL or missing references)
		//IL_0438: Unknown result type (might be due to invalid IL or missing references)
		//IL_043a: Unknown result type (might be due to invalid IL or missing references)
		//IL_043c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0441: Unknown result type (might be due to invalid IL or missing references)
		//IL_0443: Unknown result type (might be due to invalid IL or missing references)
		//IL_0445: Unknown result type (might be due to invalid IL or missing references)
		//IL_044a: Unknown result type (might be due to invalid IL or missing references)
		//IL_044f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0453: Unknown result type (might be due to invalid IL or missing references)
		//IL_0458: Unknown result type (might be due to invalid IL or missing references)
		//IL_0463: Unknown result type (might be due to invalid IL or missing references)
		//IL_0465: Unknown result type (might be due to invalid IL or missing references)
		//IL_0473: Unknown result type (might be due to invalid IL or missing references)
		//IL_0475: Unknown result type (might be due to invalid IL or missing references)
		//IL_0485: Unknown result type (might be due to invalid IL or missing references)
		//IL_0487: Unknown result type (might be due to invalid IL or missing references)
		//IL_0497: Unknown result type (might be due to invalid IL or missing references)
		//IL_0499: Unknown result type (might be due to invalid IL or missing references)
		//IL_04a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_04bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_04bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0504: Unknown result type (might be due to invalid IL or missing references)
		//IL_0509: Unknown result type (might be due to invalid IL or missing references)
		//IL_0600: Unknown result type (might be due to invalid IL or missing references)
		//IL_0607: Expected O, but got Unknown
		//IL_05c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_05c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_05d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_05d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_05e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_05ea: Unknown result type (might be due to invalid IL or missing references)
		Random random = new Random(seed);
		List<Vector3> list = new List<Vector3>();
		List<int> list2 = new List<int>();
		float num = (1f + Mathf.Sqrt(5f)) * 0.5f;
		Vector3 val = new Vector3(-1f, num, 0f);
		list.Add(((Vector3)(ref val)).normalized);
		Vector3 val2 = new Vector3(1f, num, 0f);
		list.Add(((Vector3)(ref val2)).normalized);
		Vector3 val3 = new Vector3(-1f, 0f - num, 0f);
		list.Add(((Vector3)(ref val3)).normalized);
		Vector3 val4 = new Vector3(1f, 0f - num, 0f);
		list.Add(((Vector3)(ref val4)).normalized);
		Vector3 val5 = new Vector3(0f, -1f, num);
		list.Add(((Vector3)(ref val5)).normalized);
		Vector3 val6 = new Vector3(0f, 1f, num);
		list.Add(((Vector3)(ref val6)).normalized);
		Vector3 val7 = new Vector3(0f, -1f, 0f - num);
		list.Add(((Vector3)(ref val7)).normalized);
		Vector3 val8 = new Vector3(0f, 1f, 0f - num);
		list.Add(((Vector3)(ref val8)).normalized);
		Vector3 val9 = new Vector3(num, 0f, -1f);
		list.Add(((Vector3)(ref val9)).normalized);
		Vector3 val10 = new Vector3(num, 0f, 1f);
		list.Add(((Vector3)(ref val10)).normalized);
		Vector3 val11 = new Vector3(0f - num, 0f, -1f);
		list.Add(((Vector3)(ref val11)).normalized);
		Vector3 val12 = new Vector3(0f - num, 0f, 1f);
		list.Add(((Vector3)(ref val12)).normalized);
		int[] array = new int[60]
		{
			0, 11, 5, 0, 5, 1, 0, 1, 7, 0,
			7, 10, 0, 10, 11, 1, 5, 9, 5, 11,
			4, 11, 10, 2, 10, 7, 6, 7, 1, 8,
			3, 9, 4, 3, 4, 2, 3, 2, 6, 3,
			6, 8, 3, 8, 9, 4, 9, 5, 2, 4,
			11, 6, 2, 10, 8, 6, 7, 9, 8, 1
		};
		for (int i = 0; i < array.Length; i++)
		{
			list2.Add(array[i]);
		}
		for (int j = 0; j < subdiv; j++)
		{
			SubdivideOnce(list, list2);
		}
		Vector3[] array2 = (Vector3[])(object)new Vector3[8]
		{
			new Vector3(1f, 0.35f, 0.1f),
			new Vector3(-0.72f, 0.5f, 0.42f),
			new Vector3(0.2f, -0.9f, 0.3f),
			new Vector3(-0.4f, -0.5f, -0.85f),
			new Vector3(0.5f, 0.62f, -0.6f),
			new Vector3(-0.82f, 0.2f, -0.45f),
			new Vector3(0.3f, 0.8f, 0.5f),
			new Vector3(0.75f, -0.45f, -0.5f)
		};
		float[] array3 = new float[8] { 0.17f, 0.15f, 0.14f, 0.16f, 0.13f, 0.14f, 0.12f, 0.11f };
		Quaternion val13 = Quaternion.Euler((float)(seed % 360), (float)(seed / 7 % 360), (float)(seed / 13 % 360));
		for (int k = 0; k < list.Count; k++)
		{
			Vector3 val14 = list[k];
			Vector3 val15 = val13 * ((Vector3)(ref val14)).normalized;
			float num2 = 1f;
			for (int l = 0; l < array2.Length; l++)
			{
				float num3 = Vector3.Dot(val15, ((Vector3)(ref array2[l])).normalized);
				float num4 = Mathf.Clamp01((num3 - 0.3f) / 0.7f);
				num4 = num4 * num4 * (3f - 2f * num4);
				num2 += array3[l] * num4;
			}
			list[k] = val15 * num2;
		}
		int[] array4 = list2.ToArray();
		Vector3[] array5 = (Vector3[])(object)new Vector3[array4.Length];
		Vector3[] array6 = (Vector3[])(object)new Vector3[array4.Length];
		for (int m = 0; m < array4.Length; m += 3)
		{
			Vector3 val16 = list[array4[m]];
			Vector3 val17 = list[array4[m + 1]];
			Vector3 val18 = list[array4[m + 2]];
			Vector3 val19 = Vector3.Cross(val17 - val16, val18 - val16);
			Vector3 normalized = ((Vector3)(ref val19)).normalized;
			array5[m] = val16;
			array6[m] = normalized;
			array5[m + 1] = val17;
			array6[m + 1] = normalized;
			array5[m + 2] = val18;
			array6[m + 2] = normalized;
		}
		int[] array7 = new int[array4.Length];
		for (int n = 0; n < array7.Length; n++)
		{
			array7[n] = n;
		}
		Color[] array8 = (Color[])(object)new Color[array5.Length];
		Color val20 = BaseTintFor(seed);
		Color val21 = default(Color);
		for (int num5 = 0; num5 < array5.Length; num5 += 3)
		{
			float num6 = array6[num5].y * 0.5f + 0.5f;
			float num7 = ((num6 < 0.3f) ? 0.14f : ((num6 < 0.62f) ? 0.26f : 0.44f));
			float num8 = (float)(random.NextDouble() * 0.1 - 0.05);
			float num9 = Mathf.Clamp01(num7 + num8);
			((Color)(ref val21))..ctor(val20.r * num9 * 0.5f, val20.g * num9 * 0.5f, val20.b * num9 * 0.5f, 1f);
			array8[num5] = val21;
			array8[num5 + 1] = val21;
			array8[num5 + 2] = val21;
		}
		Mesh val22 = new Mesh();
		((Object)val22).name = "SmokeBlob_" + seed;
		val22.vertices = array5;
		val22.normals = array6;
		val22.colors = array8;
		val22.triangles = array7;
		val22.RecalculateBounds();
		return val22;
	}

	private static Color BaseTintFor(int seed)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		return (Color)(seed switch
		{
			20260809 => new Color(0.42f, 0.4f, 0.37f), 
			20260810 => new Color(0.38f, 0.38f, 0.4f), 
			_ => new Color(0.34f, 0.34f, 0.36f), 
		});
	}

	private static void SubdivideOnce(List<Vector3> verts, List<int> tris)
	{
		Dictionary<int, int> midMap = new Dictionary<int, int>();
		int count = tris.Count;
		List<int> list = new List<int>(count * 4);
		for (int i = 0; i < count; i += 3)
		{
			int num = tris[i];
			int num2 = tris[i + 1];
			int num3 = tris[i + 2];
			int item = MidPoint(verts, midMap, num, num2);
			int item2 = MidPoint(verts, midMap, num2, num3);
			int item3 = MidPoint(verts, midMap, num3, num);
			list.Add(num);
			list.Add(item);
			list.Add(item3);
			list.Add(num2);
			list.Add(item2);
			list.Add(item);
			list.Add(num3);
			list.Add(item3);
			list.Add(item2);
			list.Add(item);
			list.Add(item2);
			list.Add(item3);
		}
		tris.Clear();
		tris.AddRange(list);
	}

	private static int MidPoint(List<Vector3> verts, Dictionary<int, int> midMap, int i1, int i2)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: 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_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: 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)
		int key = ((i1 < i2) ? ((i1 << 16) | i2) : ((i2 << 16) | i1));
		if (midMap.TryGetValue(key, out var value))
		{
			return value;
		}
		Vector3 item = (verts[i1] + verts[i2]) * 0.5f;
		((Vector3)(ref item)).Normalize();
		int count = verts.Count;
		verts.Add(item);
		midMap[key] = count;
		return count;
	}

	private static Material MakeBlobMaterial()
	{
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Expected O, but got Unknown
		//IL_0081: 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)
		string[] array = new string[3] { "Particles/Alpha Blended", "Legacy Shaders/Particles/Alpha Blended", "Sprites/Default" };
		Shader val = null;
		for (int i = 0; i < array.Length; i++)
		{
			if (!((Object)(object)val == (Object)null))
			{
				break;
			}
			val = Shader.Find(array[i]);
		}
		if ((Object)(object)val == (Object)null)
		{
			return null;
		}
		Material val2 = new Material(val);
		if (val2.HasProperty("_TintColor"))
		{
			val2.SetColor("_TintColor", new Color(1f, 1f, 1f, 0.5f));
		}
		else if (val2.HasProperty("_Color"))
		{
			val2.SetColor("_Color", new Color(1f, 1f, 1f, 1f));
		}
		return val2;
	}
}
public class SmokeBlob : MonoBehaviour
{
	private static readonly int TINT_ID = Shader.PropertyToID("_TintColor");

	private static readonly int COLOR_ID = Shader.PropertyToID("_Color");

	private Transform[] _puffs;

	private Renderer[] _renderers;

	private MaterialPropertyBlock[] _blocks;

	private int _intPropId = TINT_ID;

	private bool _live;

	private float _t0;

	private float _life;

	private float _scale;

	public void Launch(float scale, float life)
	{
		//IL_0058: 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)
		_scale = Mathf.Max(0.001f, scale);
		_life = Mathf.Max(0.2f, life);
		_t0 = Time.time;
		_live = true;
		if (_puffs == null)
		{
			return;
		}
		for (int i = 0; i < _puffs.Length; i++)
		{
			if ((Object)(object)_puffs[i] != (Object)null)
			{
				_puffs[i].localScale = Vector3.one * 0.001f;
			}
		}
	}

	private void Awake()
	{
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Expected O, but got Unknown
		int childCount = ((Component)this).transform.childCount;
		_puffs = (Transform[])(object)new Transform[childCount];
		_renderers = (Renderer[])(object)new Renderer[childCount];
		_blocks = (MaterialPropertyBlock[])(object)new MaterialPropertyBlock[childCount];
		for (int i = 0; i < childCount; i++)
		{
			Transform child = ((Component)this).transform.GetChild(i);
			_puffs[i] = child;
			_renderers[i] = (((Object)(object)child != (Object)null) ? ((Component)child).GetComponent<Renderer>() : null);
			_blocks[i] = new MaterialPropertyBlock();
		}
		Renderer val = ((_renderers.Length > 0) ? _renderers[0] : null);
		if ((Object)(object)val != (Object)null && (Object)(object)val.sharedMaterial != (Object)null)
		{
			if (val.sharedMaterial.HasProperty("_Color") && !val.sharedMaterial.HasProperty("_TintColor"))
			{
				_intPropId = COLOR_ID;
			}
			else
			{
				_intPropId = TINT_ID;
			}
		}
	}

	private void Update()
	{
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
		if (!_live || _puffs == null || _puffs.Length == 0)
		{
			return;
		}
		float num = Time.time - _t0;
		if (num >= _life)
		{
			_live = false;
			((Component)this).gameObject.SetActive(false);
			return;
		}
		float expandTime = FxPlugin.ExpandTime;
		float num3;
		if (num < expandTime)
		{
			float num2 = Mathf.Clamp01(num / expandTime);
			num3 = 0.04f + 0.96f * num2;
		}
		else
		{
			num3 = Mathf.Exp((0f - FxPlugin.DecayRate) * (num - expandTime));
		}
		float num4 = Mathf.Clamp01(1f - num / _life);
		for (int i = 0; i < _puffs.Length; i++)
		{
			Transform val = _puffs[i];
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			float num5 = num - ((i < SmokeMeshFactory.PUFF_DELAY.Length) ? SmokeMeshFactory.PUFF_DELAY[i] : 0f);
			float num6;
			float num7;
			if (num5 <= 0f)
			{
				num6 = 0.001f;
				num7 = 0f;
			}
			else
			{
				float num8 = num3;
				float num9 = expandTime;
				if (num5 < num9)
				{
					float num10 = Mathf.Clamp01(num5 / num9);
					num8 = 0.04f + 0.96f * num10;
				}
				else
				{
					num8 = Mathf.Exp((0f - FxPlugin.DecayRate) * (num5 - num9));
				}
				num6 = num8 * ((i < SmokeMeshFactory.PUFF_BASE_SCALE.Length) ? SmokeMeshFactory.PUFF_BASE_SCALE[i] : 1f) * _scale;
				num7 = num4;
			}
			val.localScale = new Vector3(num6, num6, num6);
			Renderer val2 = _renderers[i];
			if (!((Object)(object)val2 == (Object)null) && val2.isVisible)
			{
				MaterialPropertyBlock val3 = _blocks[i];
				val3.SetColor(_intPropId, new Color(1f, 1f, 1f, num7 * 0.5f));
				val2.SetPropertyBlock(val3);
			}
		}
	}
}