Decompiled source of CleanCombat v1.6.0

CleanCombat.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CleanCombat
{
	internal sealed class ConfigurationManagerAttributes
	{
		public string Category;

		public string DispName;

		public int? Order;
	}
	[HarmonyPatch(typeof(Character), "ApplyDamage")]
	internal static class DamageNumberPatch
	{
		internal static int Patched;

		public static float Shown(float before, HitData hit)
		{
			if (hit == null || !Plugin.IsOn(Plugin.ActualDamageNumbers))
			{
				return before;
			}
			return hit.GetTotalDamage();
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Expected O, but got Unknown
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			Patched = 0;
			FieldInfo fieldInfo = AccessTools.Field(typeof(HitData), "m_point");
			MethodInfo show = AccessTools.Method(typeof(DamageText), "ShowText", new Type[4]
			{
				typeof(DamageModifier),
				typeof(Vector3),
				typeof(float),
				typeof(bool)
			}, (Type[])null);
			MethodInfo methodInfo = AccessTools.Method(typeof(DamageNumberPatch), "Shown", (Type[])null, (Type[])null);
			if (fieldInfo == null || show == null || methodInfo == null)
			{
				return list;
			}
			for (int i = 0; i + 1 < list.Count; i++)
			{
				if (CodeInstructionExtensions.LoadsField(list[i], fieldInfo, false) && CodeInstructionExtensions.IsLdloc(list[i + 1], (LocalBuilder)null))
				{
					int num = list.FindIndex(i + 2, (CodeInstruction ins) => CodeInstructionExtensions.Calls(ins, show));
					if (num >= 0 && num - i <= 12)
					{
						list.InsertRange(i + 2, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[2]
						{
							new CodeInstruction(OpCodes.Ldarg_1, (object)null),
							new CodeInstruction(OpCodes.Call, (object)methodInfo)
						});
						Patched++;
						i += 3;
					}
				}
			}
			return list;
		}
	}
	[HarmonyPatch(typeof(Game), "Start")]
	internal static class GameStartPatch
	{
		private static void Postfix()
		{
			try
			{
				Registry.Build();
				if ((Object)(object)ObjectDB.instance != (Object)null)
				{
					StatusIcons.Build(ObjectDB.instance);
				}
			}
			catch (Exception ex)
			{
				Guard.Report(ex);
			}
		}
	}
	internal static class Guard
	{
		private static bool _reported;

		internal static void Report(Exception ex)
		{
			if (!_reported)
			{
				_reported = true;
				Plugin.Log.LogError((object)("Error while changing effects; the game carries on as normal: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(EffectList), "Create")]
	internal static class EffectListCreatePatch
	{
		private static void Postfix(EffectList __instance, int variant, GameObject[] __result)
		{
			if ((Plugin.HideMask | Plugin.ReduceMask) == 0 || __result == null || __result.Length == 0 || __instance.m_effectPrefabs == null)
			{
				return;
			}
			try
			{
				int num = 0;
				EffectData[] effectPrefabs = __instance.m_effectPrefabs;
				ZNetView val3 = default(ZNetView);
				foreach (EffectData val in effectPrefabs)
				{
					if (val != null && val.m_enabled && (variant < 0 || val.m_variant < 0 || variant == val.m_variant))
					{
						if (num >= __result.Length)
						{
							break;
						}
						GameObject val2 = __result[num++];
						int num2 = Sources.Resolve(Registry.MaskOf(val.m_prefab), Sources.Current);
						if (num2 != 0 && (Object)(object)val2 != (Object)null && !val2.TryGetComponent<ZNetView>(ref val3))
						{
							Visuals.Apply(val2, num2);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Guard.Report(ex);
			}
		}
	}
	[HarmonyPatch(typeof(ZNetView), "Awake")]
	internal static class ZNetViewAwakePatch
	{
		private static void Postfix(ZNetView __instance, ZDO ___m_zdo)
		{
			if (___m_zdo == null)
			{
				return;
			}
			int num = Registry.MaskOf(___m_zdo.GetPrefab());
			if (num == 0)
			{
				return;
			}
			try
			{
				if ((num & 0x1F) != 0)
				{
					StatusIcons.Track(__instance, num & 0x1F);
				}
				Visuals.Apply(((Component)__instance).gameObject, Sources.Resolve(num, Sources.Current));
			}
			catch (Exception ex)
			{
				Guard.Report(ex);
			}
		}
	}
	[HarmonyPatch(typeof(EnemyHud), "UpdateHuds")]
	internal static class EnemyHudPatch
	{
		private static void Postfix(IDictionary ___m_huds)
		{
			try
			{
				StatusIcons.Update(___m_huds);
			}
			catch (Exception ex)
			{
				Guard.Report(ex);
			}
		}
	}
	[HarmonyPatch(typeof(VisEquipment), "SetWeaponTrails")]
	internal static class WeaponTrailsPatch
	{
		private static void Prefix(ref bool enabled)
		{
			if (Plugin.ModeOf(Plugin.WeaponTrails) == Plugin.Mode.Hide)
			{
				enabled = false;
			}
		}
	}
	[HarmonyPatch(typeof(MeleeWeaponTrail), "CustomFixedUpdate")]
	internal static class WeaponTrailLengthPatch
	{
		private static readonly ConditionalWeakTable<MeleeWeaponTrail, StrongBox<float>> Original = new ConditionalWeakTable<MeleeWeaponTrail, StrongBox<float>>();

		private static void Prefix(MeleeWeaponTrail __instance, ref float ____lifeTime)
		{
			if (!Original.TryGetValue(__instance, out var value))
			{
				value = new StrongBox<float>(____lifeTime);
				Original.Add(__instance, value);
			}
			____lifeTime = ((Plugin.ModeOf(Plugin.WeaponTrails) == Plugin.Mode.Reduce) ? (value.Value * Plugin.ReduceAmount) : value.Value);
		}
	}
	[HarmonyPatch(typeof(Hud), "DamageFlash")]
	internal static class DamageFlashPatch
	{
		private static bool Prefix()
		{
			return Plugin.ModeOf(Plugin.DamageFlash) != Plugin.Mode.Hide;
		}

		private static void Postfix(Hud __instance)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.ModeOf(Plugin.DamageFlash) == Plugin.Mode.Reduce && !((Object)(object)__instance.m_damageScreen == (Object)null))
			{
				Color color = ((Graphic)__instance.m_damageScreen).color;
				color.a = Plugin.ReduceAmount;
				((Graphic)__instance.m_damageScreen).color = color;
			}
		}
	}
	[HarmonyPatch(typeof(GameCamera), "AddShake")]
	internal static class CameraShakePatch
	{
		private static bool Prefix(ref float strength)
		{
			Plugin.Mode num = Plugin.ModeOf(Plugin.CameraShake);
			if (num == Plugin.Mode.Reduce)
			{
				strength *= Plugin.ReduceAmount;
			}
			return num != Plugin.Mode.Hide;
		}
	}
	[Flags]
	internal enum Fx
	{
		None = 0,
		Fire = 1,
		Poison = 2,
		Frost = 4,
		Lightning = 8,
		Spirit = 0x10,
		Hits = 0x20,
		GroundDust = 0x40,
		AttackEffects = 0x80,
		ProjectileImpacts = 0x100,
		PlayerAreas = 0x200,
		ProjectileTrails = 0x400,
		EnemyHits = 0x800,
		EnemyGroundDust = 0x1000,
		EnemyAttackEffects = 0x2000,
		EnemyProjectileImpacts = 0x4000,
		EnemyAreas = 0x8000,
		EnemyProjectileTrails = 0x10000,
		BossHits = 0x20000,
		BossGroundDust = 0x40000,
		BossAttackEffects = 0x80000,
		BossProjectileImpacts = 0x100000,
		BossAreas = 0x200000,
		BossProjectileTrails = 0x400000,
		BlockParryDodge = 0x800000,
		Death = 0x1000000,
		Adrenaline = 0x2000000
	}
	[BepInPlugin("vh.cleancombat", "CleanCombat", "1.6.0")]
	public class Plugin : BaseUnityPlugin
	{
		public enum Mode
		{
			Show,
			Reduce,
			Hide
		}

		public const string Guid = "vh.cleancombat";

		public const string Name = "CleanCombat";

		public const string Version = "1.6.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<int> ReduceAmountPercent;

		internal static ConfigEntry<int> ReduceSizePercent;

		internal static ConfigEntry<int> EnemyReduceAmountPercent;

		internal static ConfigEntry<int> EnemyReduceSizePercent;

		internal static ConfigEntry<bool> LogChanged;

		internal static ConfigEntry<bool> StatusIcons;

		internal static ConfigEntry<bool> StaggerIcon;

		internal static ConfigEntry<bool> StatusClock;

		internal static ConfigEntry<bool> StatusIconBorder;

		internal static ConfigEntry<int> StatusIconSize;

		internal static ConfigEntry<Mode> WeaponTrails;

		internal static ConfigEntry<Mode> DamageFlash;

		internal static ConfigEntry<Mode> CameraShake;

		internal static ConfigEntry<bool> ActualDamageNumbers;

		private const string General = "0. General";

		private const string Status = "1. Status effects on characters";

		private const string Weapons = "2. Hits and weapons";

		private const string Projectiles = "3. Projectiles, bombs and areas";

		private const string Other = "4. Other";

		private const string Enemies = "5. Enemies and bosses";

		private const string Modes = " Show: as in the normal game. Reduce: fewer, smaller particles and no glow. Hide: nothing drawn. Applies to effects that appear after the change.";

		private readonly List<(ConfigEntry<Mode> Entry, Fx Fx)> _fxEntries = new List<(ConfigEntry<Mode>, Fx)>();

		private Harmony _harmony;

		private FileSystemWatcher _watcher;

		private DateTime _lastReload;

		internal static int HideMask { get; private set; }

		internal static int ReduceMask { get; private set; }

		internal static float ReduceAmount => (float)ReduceAmountPercent.Value / 100f;

		internal static float ReduceSize => (float)ReduceSizePercent.Value / 100f;

		internal static float EnemyReduceAmount => (float)EnemyReduceAmountPercent.Value / 100f;

		internal static float EnemyReduceSize => (float)EnemyReduceSizePercent.Value / 100f;

		private static ConfigurationManagerAttributes Display(string category, string name, int order)
		{
			return new ConfigurationManagerAttributes
			{
				Category = category,
				DispName = name,
				Order = order
			};
		}

		private void Awake()
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Expected O, but got Unknown
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Expected O, but got Unknown
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Expected O, but got Unknown
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Expected O, but got Unknown
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Expected O, but got Unknown
			//IL_0261: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Expected O, but got Unknown
			//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ba: Expected O, but got Unknown
			//IL_0494: Unknown result type (might be due to invalid IL or missing references)
			//IL_049e: Expected O, but got Unknown
			//IL_04d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04dd: Expected O, but got Unknown
			//IL_0512: Unknown result type (might be due to invalid IL or missing references)
			//IL_051c: Expected O, but got Unknown
			//IL_055a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0564: Expected O, but got Unknown
			//IL_05a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ad: Expected O, but got Unknown
			//IL_066c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0676: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("0. General", "Enabled", true, new ConfigDescription("Master switch. Off shows every effect and damage number as in the normal game and removes the status icons, whatever the settings below say.", (AcceptableValueBase)null, new object[1] { Display("0. General", "Enabled", 4) }));
			ReduceAmountPercent = ((BaseUnityPlugin)this).Config.Bind<int>("0. General", "ReduceAmount", 20, new ConfigDescription("What Reduce keeps, in percent: of an effect's particles, of camera shake strength, of the red flash's opacity and of weapon trail length. Bursts of one or more particles keep at least one.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 90), new object[1] { Display("0. General", "Reduce: amount kept (%)", 3) }));
			ReduceSizePercent = ((BaseUnityPlugin)this).Config.Bind<int>("0. General", "ReduceSize", 40, new ConfigDescription("Size of particles and width of trails in effects set to Reduce, in percent of normal.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(20, 100), new object[1] { Display("0. General", "Reduce: particle size (%)", 2) }));
			LogChanged = ((BaseUnityPlugin)this).Config.Bind<bool>("0. General", "LogChangedEffects", false, new ConfigDescription("Write the name and group of each effect the first time it's reduced or hidden to BepInEx\\LogOutput.log. For finding out which setting covers an effect.", (AcceptableValueBase)null, new object[1] { Display("0. General", "Log changed effects", 1) }));
			StatusIcons = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Status effects on characters", "StatusIcons", true, new ConfigDescription("Show an icon next to a creature's or other player's health bar for each of these status effects it has, so you can tell it's burning or poisoned whatever the visuals are set to. Works for creatures another player is fighting too.", (AcceptableValueBase)null, new object[1] { Display("1. Status effects on characters", "Status icons on health bars", 10) }));
			StatusClock = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Status effects on characters", "StatusClock", true, new ConfigDescription("Darken each icon clockwise from 12 o'clock as its time runs out, like a clock; fully dark means it's about to end. Stagger always has one. Status effects have one on creatures your game runs, which is every creature you fight alone; the game doesn't share the time left of creatures another player runs, so theirs show the icon only.", (AcceptableValueBase)null, new object[1] { Display("1. Status effects on characters", "Duration clock", 9) }));
			StaggerIcon = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Status effects on characters", "StaggerIcon", true, new ConfigDescription("Also show a white burst in a gold frame while a creature or player is staggered. A staggered creature takes double damage, so this is the moment to hit hard. Works for creatures another player is fighting too. Needs Status icons on health bars.", (AcceptableValueBase)null, new object[1] { Display("1. Status effects on characters", "Stagger icon", 8) }));
			StatusIconBorder = ((BaseUnityPlugin)this).Config.Bind<bool>("1. Status effects on characters", "StatusIconBorder", true, new ConfigDescription("Draw each status icon on a dark backing inside a frame in the status's colour (fire orange, poison green, frost blue, lightning violet, spirit white), so it stands out against any background.", (AcceptableValueBase)null, new object[1] { Display("1. Status effects on characters", "Status icon border", 7) }));
			StatusIconSize = ((BaseUnityPlugin)this).Config.Bind<int>("1. Status effects on characters", "StatusIconSize", 150, new ConfigDescription("Size of the status icons, in percent.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(50, 300), new object[1] { Display("1. Status effects on characters", "Status icon size (%)", 6) }));
			BindFx("1. Status effects on characters", "Fire", Fx.Fire, Mode.Reduce, 5, "Fire (burning)", "The flames and burn-tick puffs on burning creatures and players (the Burning status effect).");
			BindFx("1. Status effects on characters", "Poison", Fx.Poison, Mode.Reduce, 4, "Poison", "The visuals of the Poison status effect on creatures and players.");
			BindFx("1. Status effects on characters", "Frost", Fx.Frost, Mode.Reduce, 3, "Frost", "The visuals of the Frost status effect (frost damage) on creatures and players.");
			BindFx("1. Status effects on characters", "Lightning", Fx.Lightning, Mode.Reduce, 2, "Lightning (shock)", "The visuals of the Lightning status effect (lightning damage) on creatures and players.");
			BindFx("1. Status effects on characters", "Spirit", Fx.Spirit, Mode.Reduce, 1, "Spirit", "The visuals of the Spirit status effect (spirit damage) on creatures and players.");
			BindFx("2. Hits and weapons", "HitEffects", Fx.Hits, Mode.Reduce, 5, "Hit effects (blood, sparks)", "The blood, spark and splinter bursts where your hits land, including crit and sneak-attack bursts. \"Your\" here and below means you, other players and tames; creatures and bosses have their own settings under 5.");
			BindFx("2. Hits and weapons", "GroundDust", Fx.GroundDust, Mode.Hide, 4, "Weapon dust on the ground", "The dust and debris when your weapon hits the ground.");
			BindFx("2. Hits and weapons", "AttackEffects", Fx.AttackEffects, Mode.Hide, 3, "Your attack effects (slams, casts)", "The start, swing, trigger and burst effects of your weapons and staffs: sledge and atgeir slam rings, staff cast flashes and similar.");
			BindFx("2. Hits and weapons", "BlockParryDodge", Fx.BlockParryDodge, Mode.Reduce, 2, "Block, parry and dodge", "The flash when a shield or weapon blocks or parries, and the dodge-roll and perfect-dodge effects.");
			WeaponTrails = ((BaseUnityPlugin)this).Config.Bind<Mode>("2. Hits and weapons", "WeaponTrails", Mode.Reduce, new ConfigDescription("The streak that follows melee weapons during a swing, for players and creatures. Reduce makes it shorter (the amount kept); Hide turns it off.", (AcceptableValueBase)null, new object[1] { Display("2. Hits and weapons", "Weapon swing trails", 1) }));
			BindFx("3. Projectiles, bombs and areas", "ProjectileImpacts", Fx.ProjectileImpacts, Mode.Reduce, 3, "Projectile impacts", "The burst where your arrows, bolts, spears and other projectiles hit something. Bombs and projectiles that leave a cloud or area belong to the area setting instead.");
			BindFx("3. Projectiles, bombs and areas", "PlayerAreas", Fx.PlayerAreas, Mode.Reduce, 2, "Bombs and player area effects", "The explosion and the lingering cloud or area of your bombs (ooze, bile, lava and others) and of staff and weapon area attacks. The area still deals its damage and status effects.");
			BindFx("3. Projectiles, bombs and areas", "ProjectileTrails", Fx.ProjectileTrails, Mode.Show, 1, "Player projectile trails", "The particles, trails and glow on flying player projectiles (fire arrows, staff bolts, thrown bombs). Their solid model always stays; projectiles made only of particles, such as some staff fireballs, become invisible in flight with Hide.");
			BindFx("4. Other", "DeathEffects", Fx.Death, Mode.Hide, 4, "Death and corpse puffs", "The effects when a creature dies and the puff when its corpse disappears. Corpses and loot are unchanged.");
			BindFx("4. Other", "AdrenalineEffects", Fx.Adrenaline, Mode.Hide, 3, "Adrenaline and trinket effects", "The burst when the adrenaline bar fills and the visuals of the trinket effect it starts. The bar and the status list still show it.");
			DamageFlash = ((BaseUnityPlugin)this).Config.Bind<Mode>("4. Other", "DamageFlash", Mode.Show, new ConfigDescription("The red screen flash when you take damage. Reduce starts it at the amount kept as opacity.", (AcceptableValueBase)null, new object[1] { Display("4. Other", "Red damage flash", 2) }));
			CameraShake = ((BaseUnityPlugin)this).Config.Bind<Mode>("4. Other", "CameraShake", Mode.Show, new ConfigDescription("Camera shake from hits you take, explosions and events. Reduce scales it to the amount kept. The game's own Settings > Accessibility > Camera Shake turns it off too.", (AcceptableValueBase)null, new object[1] { Display("4. Other", "Camera shake", 1) }));
			ActualDamageNumbers = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Other", "ActualDamageNumbers", true, new ConfigDescription("The number that pops up over a hit creature or player shows the health it actually lost. The normal game shows the damage from before two reductions it then applies: co-op scaling (each extra player near a creature makes it take less damage) and the world's Combat setting, so in co-op or on a harder world the number is bigger than what the health bar loses. The number comes from the game that runs the character, which sends it to everyone nearby: this corrects it for characters your game runs (every creature you fight alone), and players nearby see the corrected number for those too. Off: as in the normal game.", (AcceptableValueBase)null, new object[1] { Display("4. Other", "Damage numbers: health actually lost", 0) }));
			EnemyReduceAmountPercent = ((BaseUnityPlugin)this).Config.Bind<int>("5. Enemies and bosses", "ReduceAmount", 20, new ConfigDescription("What Reduce keeps of an enemy or boss effect's particles, in percent. These settings use this and the size below instead of the ones in General.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 90), new object[1] { Display("5. Enemies and bosses", "Enemies: amount kept (%)", 14) }));
			EnemyReduceSizePercent = ((BaseUnityPlugin)this).Config.Bind<int>("5. Enemies and bosses", "ReduceSize", 40, new ConfigDescription("Size of particles and width of trails in enemy and boss effects set to Reduce, in percent.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(20, 100), new object[1] { Display("5. Enemies and bosses", "Enemies: particle size (%)", 13) }));
			BindFamily("Enemy", "Enemy", "creatures (not bosses)", Fx.EnemyHits, Fx.EnemyGroundDust, Fx.EnemyAttackEffects, Fx.EnemyProjectileImpacts, Fx.EnemyAreas, Fx.EnemyProjectileTrails, 12);
			BindFamily("Boss", "Boss", "bosses (Eikthyr to Fader, and any modded boss)", Fx.BossHits, Fx.BossGroundDust, Fx.BossAttackEffects, Fx.BossProjectileImpacts, Fx.BossAreas, Fx.BossProjectileTrails, 6);
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				UpdateMasks();
			};
			((BaseUnityPlugin)this).Config.ConfigReloaded += delegate
			{
				UpdateMasks();
			};
			UpdateMasks();
			((BaseUnityPlugin)this).Config.Save();
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = true;
			_harmony = new Harmony("vh.cleancombat");
			_harmony.PatchAll();
			if (DamageNumberPatch.Patched != 1)
			{
				Log.LogWarning((object)($"Damage numbers: found {DamageNumberPatch.Patched} places to change in " + "Character.ApplyDamage (expected 1); numbers stay as in the normal game in this version."));
			}
			SetupWatcher();
		}

		private void BindFamily(string prefix, string label, string who, Fx hits, Fx dust, Fx attacks, Fx impacts, Fx areas, Fx trails, int order)
		{
			BindFx("5. Enemies and bosses", prefix + "HitEffects", hits, Mode.Show, order, label + " hits", "The blood, spark and impact bursts where " + who + " land hits on you, other players and tames.");
			BindFx("5. Enemies and bosses", prefix + "GroundDust", dust, Mode.Show, order - 1, label + " ground dust", "The dust and debris when attacks of " + who + " hit the ground: slams, stomps and charges.");
			BindFx("5. Enemies and bosses", prefix + "AttackEffects", attacks, Mode.Show, order - 2, label + " attack and ability effects", "The start, swing, cast, breath and burst effects of attacks and abilities of " + who + ". Many of these warn you of what's coming; try Reduce before Hide.");
			BindFx("5. Enemies and bosses", prefix + "ProjectileImpacts", impacts, Mode.Show, order - 3, label + " projectile impacts", "The burst where projectiles of " + who + " hit something. Projectiles that leave a cloud or area belong to the area setting.");
			BindFx("5. Enemies and bosses", prefix + "Areas", areas, Mode.Show, order - 4, label + " area effects", "The clouds, pools, blasts and ground effects of area attacks of " + who + ", and the warning effects before some of them. These still hurt you: hidden, you can't see where they are.");
			BindFx("5. Enemies and bosses", prefix + "ProjectileGlow", trails, Mode.Show, order - 5, label + " projectile glow", "The particles, trails and glow on flying projectiles of " + who + "; solid models stay. Projectiles made only of particles become invisible in flight with Hide, so you can't dodge them.");
		}

		private void BindFx(string section, string key, Fx fx, Mode defaultValue, int order, string name, string description)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			ConfigEntry<Mode> item = ((BaseUnityPlugin)this).Config.Bind<Mode>(section, key, defaultValue, new ConfigDescription(description + " Show: as in the normal game. Reduce: fewer, smaller particles and no glow. Hide: nothing drawn. Applies to effects that appear after the change.", (AcceptableValueBase)null, new object[1] { Display(section, name, order) }));
			_fxEntries.Add((item, fx));
		}

		private void UpdateMasks()
		{
			int num = 0;
			int num2 = 0;
			if (Enabled.Value)
			{
				foreach (var (val, fx) in _fxEntries)
				{
					if (val.Value == Mode.Hide)
					{
						num |= (int)fx;
					}
					else if (val.Value == Mode.Reduce)
					{
						num2 |= (int)fx;
					}
				}
			}
			HideMask = num;
			ReduceMask = num2;
		}

		internal static Mode ModeOf(ConfigEntry<Mode> entry)
		{
			if (!Enabled.Value)
			{
				return Mode.Show;
			}
			return entry.Value;
		}

		internal static bool IsOn(ConfigEntry<bool> entry)
		{
			if (Enabled.Value)
			{
				return entry.Value;
			}
			return false;
		}

		private void OnDestroy()
		{
			_watcher?.Dispose();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void SetupWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath));
			_watcher.Changed += ReloadConfig;
			_watcher.Created += ReloadConfig;
			_watcher.Renamed += ReloadConfig;
			_watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_watcher.EnableRaisingEvents = true;
		}

		private void ReloadConfig(object sender, FileSystemEventArgs e)
		{
			if ((DateTime.Now - _lastReload).TotalSeconds < 1.0 || !File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath))
			{
				return;
			}
			_lastReload = DateTime.Now;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
				Log.LogInfo((object)"Configuration reloaded.");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error reloading configuration: " + ex.Message));
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = true;
			}
		}
	}
	internal static class Registry
	{
		private static readonly Dictionary<GameObject, int> ByPrefab = new Dictionary<GameObject, int>();

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

		private static readonly HashSet<(GameObject, Source)> Walked = new HashSet<(GameObject, Source)>();

		private static readonly Type[] Bodies = new Type[11]
		{
			typeof(Character),
			typeof(Ragdoll),
			typeof(ItemDrop),
			typeof(Piece),
			typeof(Destructible),
			typeof(MineRock),
			typeof(MineRock5),
			typeof(TreeBase),
			typeof(TreeLog),
			typeof(Pickable),
			typeof(Container)
		};

		private static ZNetScene _scene;

		internal static int MaskOf(GameObject prefab)
		{
			if (!((Object)(object)prefab != (Object)null) || !ByPrefab.TryGetValue(prefab, out var value))
			{
				return 0;
			}
			return value;
		}

		internal static int MaskOf(int prefabHash)
		{
			if (!ByHash.TryGetValue(prefabHash, out var value))
			{
				return 0;
			}
			return value;
		}

		internal static void Build()
		{
			ByPrefab.Clear();
			ByHash.Clear();
			Walked.Clear();
			_scene = ZNetScene.instance;
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)_scene == (Object)null || (Object)(object)instance == (Object)null)
			{
				Plugin.Log.LogWarning((object)"ZNetScene or ObjectDB missing; no effects will be changed.");
				return;
			}
			foreach (StatusEffect statusEffect in instance.m_StatusEffects)
			{
				AddStatus(statusEffect, StatusGroup(statusEffect));
			}
			HashSet<string> hashSet = new HashSet<string>(from recipe in instance.m_recipes
				where (Object)(object)recipe != (Object)null && (Object)(object)recipe.m_item != (Object)null
				select ((Object)((Component)recipe.m_item).gameObject).name);
			Dictionary<string, Source> dictionary = CreatureItems();
			ItemDrop val = default(ItemDrop);
			foreach (GameObject item in (from prefab in instance.m_items.Concat(_scene.m_prefabs)
				where (Object)(object)prefab != (Object)null
				select prefab).Distinct())
			{
				if (item.TryGetComponent<ItemDrop>(ref val) && val.m_itemData?.m_shared != null)
				{
					Source value;
					Source source = (hashSet.Contains(((Object)item).name) ? Source.Player : (dictionary.TryGetValue(((Object)item).name, out value) ? value : Source.Enemy));
					AddItem(val.m_itemData.m_shared, source);
				}
			}
			Character character = default(Character);
			Ragdoll val2 = default(Ragdoll);
			foreach (GameObject prefab in _scene.m_prefabs)
			{
				if (!((Object)(object)prefab == (Object)null))
				{
					if (prefab.TryGetComponent<Character>(ref character))
					{
						AddCharacter(character);
					}
					if (prefab.TryGetComponent<Ragdoll>(ref val2))
					{
						AddEffects(val2.m_removeEffect, Fx.Death);
					}
				}
			}
			foreach (GameObject prefab2 in _scene.m_prefabs)
			{
				if ((Object)(object)prefab2 != (Object)null && IsSpawner(prefab2) && !WalkedAtAll(prefab2))
				{
					Walk(prefab2, Source.Enemy, 0);
				}
			}
			IEnumerable<string> values = from Fx fx in Enum.GetValues(typeof(Fx))
				where fx != Fx.None
				select $"{fx} {ByPrefab.Count((KeyValuePair<GameObject, int> pair) => ((uint)pair.Value & (uint)fx) != 0)}";
			Plugin.Log.LogInfo((object)string.Format("Sorted {0} effect prefabs: {1}.", ByPrefab.Count, string.Join(", ", values)));
		}

		private static Dictionary<string, Source> CreatureItems()
		{
			Dictionary<string, Source> dictionary = new Dictionary<string, Source>();
			Humanoid val = default(Humanoid);
			foreach (GameObject prefab in _scene.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null || !prefab.TryGetComponent<Humanoid>(ref val) || val is Player)
				{
					continue;
				}
				Source source = (((Character)val).IsBoss() ? Source.Boss : Source.Enemy);
				foreach (GameObject[] item in new GameObject[4][] { val.m_defaultItems, val.m_randomWeapon, val.m_randomArmor, val.m_randomShield }.Concat(val.m_randomSets?.Select((ItemSet set) => set?.m_items) ?? Enumerable.Empty<GameObject[]>()).Append(val.m_randomItems?.Select((RandomItem random) => random?.m_prefab).ToArray()))
				{
					if (item == null)
					{
						continue;
					}
					GameObject[] array = item;
					foreach (GameObject val2 in array)
					{
						if ((Object)(object)val2 != (Object)null && (source == Source.Boss || !dictionary.ContainsKey(((Object)val2).name)))
						{
							dictionary[((Object)val2).name] = source;
						}
					}
				}
			}
			return dictionary;
		}

		private static Fx StatusGroup(StatusEffect se)
		{
			if ((Object)(object)se == (Object)null)
			{
				return Fx.None;
			}
			int num = se.NameHash();
			if (num == SEMan.s_statusEffectBurning)
			{
				return Fx.Fire;
			}
			if (num == SEMan.s_statusEffectPoison)
			{
				return Fx.Poison;
			}
			if (num == SEMan.s_statusEffectFrost)
			{
				return Fx.Frost;
			}
			if (num == SEMan.s_statusEffectLightning)
			{
				return Fx.Lightning;
			}
			if (num == SEMan.s_statusEffectSpirit)
			{
				return Fx.Spirit;
			}
			return Fx.None;
		}

		private static void AddStatus(StatusEffect se, Fx fx)
		{
			if ((Object)(object)se == (Object)null || fx == Fx.None)
			{
				return;
			}
			AddEffects(se.m_startEffects, fx);
			AddEffects(se.m_stopEffects, fx);
			SE_Burning val = (SE_Burning)(object)((se is SE_Burning) ? se : null);
			if (val == null)
			{
				SE_Poison val2 = (SE_Poison)(object)((se is SE_Poison) ? se : null);
				if (val2 == null)
				{
					SE_Stats val3 = (SE_Stats)(object)((se is SE_Stats) ? se : null);
					if (val3 != null)
					{
						AddEffects(val3.m_tickEffect, fx);
					}
				}
				else
				{
					AddEffects(val2.m_tickEffect, fx);
				}
			}
			else
			{
				AddEffects(val.m_tickEffect, fx);
			}
		}

		private static void AddItem(SharedData shared, Source source)
		{
			AddAttackEffects(shared.m_hitEffect, Sources.For(Fx.Hits, source), source);
			AddAttackEffects(shared.m_hitTerrainEffect, Sources.For(Fx.GroundDust, source), source);
			AddEffects(shared.m_blockEffect, Fx.BlockParryDodge);
			AddEffects(shared.m_blockChargeEffects, Fx.BlockParryDodge);
			Fx fx = Sources.For(Fx.AttackEffects, source);
			AddAttackEffects(shared.m_startEffect, fx, source);
			AddAttackEffects(shared.m_holdStartEffect, fx, source);
			AddAttackEffects(shared.m_triggerEffect, fx, source);
			AddAttackEffects(shared.m_trailStartEffect, fx, source);
			AddAttack(shared.m_attack, source);
			AddAttack(shared.m_secondaryAttack, source);
			Walk(shared.m_spawnOnHit, source, 0);
			Walk(shared.m_spawnOnHitTerrain, source, 0);
			AddStatus(shared.m_fullAdrenalineSE, Fx.Adrenaline);
			AddStatus(shared.m_perfectBlockStatusEffect, Fx.BlockParryDodge);
		}

		private static void AddAttack(Attack attack, Source source)
		{
			if (attack != null)
			{
				AddAttackEffects(attack.m_hitEffect, Sources.For(Fx.Hits, source), source);
				AddAttackEffects(attack.m_hitTerrainEffect, Sources.For(Fx.GroundDust, source), source);
				Fx fx = Sources.For(Fx.AttackEffects, source);
				AddAttackEffects(attack.m_startEffect, fx, source);
				AddAttackEffects(attack.m_triggerEffect, fx, source);
				AddAttackEffects(attack.m_trailStartEffect, fx, source);
				AddAttackEffects(attack.m_burstEffect, fx, source);
				Walk(attack.m_attackProjectile, source, 0);
				Walk(attack.m_spawnOnTrigger, source, 0);
				Walk(attack.m_spawnOnHit, source, 0);
			}
		}

		private static void AddCharacter(Character character)
		{
			Fx fx = ((character is Player) ? (Fx.EnemyHits | Fx.BossHits) : Fx.Hits);
			AddEffects(character.m_hitEffects, fx);
			AddEffects(character.m_critHitEffects, fx);
			AddEffects(character.m_backstabHitEffects, fx);
			Source source = ((character is Player) ? Source.Player : (character.IsBoss() ? Source.Boss : Source.Enemy));
			AddAttackEffects(character.m_deathEffects, Fx.Death, source);
			Humanoid val = (Humanoid)(object)((character is Humanoid) ? character : null);
			if (val != null)
			{
				AddEffects(val.m_perfectBlockEffect, Fx.BlockParryDodge);
			}
			Player val2 = (Player)(object)((character is Player) ? character : null);
			if (val2 != null)
			{
				AddEffects(val2.m_dodgeEffects, Fx.BlockParryDodge);
				AddEffects(val2.m_perfectDodgeEffects, Fx.BlockParryDodge);
				AddEffects(val2.m_adrenalinePopEffects, Fx.Adrenaline);
			}
		}

		private static void Walk(GameObject prefab, Source source, int depth)
		{
			if ((Object)(object)prefab == (Object)null || depth > 5 || !Walked.Add((prefab, source)))
			{
				return;
			}
			Fx fx = Sources.For(Fx.PlayerAreas, source);
			Projectile val = default(Projectile);
			Fx num;
			if (prefab.TryGetComponent<Projectile>(ref val))
			{
				if (!((Object)(object)val.m_spawnOnHit != (Object)null))
				{
					List<GameObject> randomSpawnOnHit = val.m_randomSpawnOnHit;
					if (randomSpawnOnHit == null || randomSpawnOnHit.Count <= 0)
					{
						num = Sources.For(Fx.ProjectileImpacts, source);
						goto IL_0074;
					}
				}
				num = fx;
				goto IL_0074;
			}
			goto IL_0108;
			IL_0074:
			Fx fx2 = num;
			AddAttackEffects(val.m_hitEffects, fx2, source);
			AddAttackEffects(val.m_hitWaterEffects, fx2, source);
			AddAttackEffects(val.m_spawnOnHitEffects, fx2, source);
			if (!IsBody(prefab))
			{
				Add(prefab, Sources.For(Fx.ProjectileTrails, source));
			}
			Walk(val.m_spawnOnHit, source, depth + 1);
			if (val.m_randomSpawnOnHit != null)
			{
				foreach (GameObject item in val.m_randomSpawnOnHit)
				{
					Walk(item, source, depth + 1);
				}
			}
			goto IL_0108;
			IL_0108:
			Aoe val2 = default(Aoe);
			if (prefab.TryGetComponent<Aoe>(ref val2))
			{
				if (!IsBody(prefab))
				{
					Add(prefab, fx);
				}
				AddAttackEffects(val2.m_hitEffects, fx, source);
				AddAttackEffects(val2.m_initiateEffect, fx, source);
				AddAttackEffects(val2.m_chainEffects, fx, source);
				Walk(val2.m_spawnOnHitTerrain, source, depth + 1);
			}
			SpawnAbility val3 = default(SpawnAbility);
			if (!prefab.TryGetComponent<SpawnAbility>(ref val3))
			{
				return;
			}
			AddAttackEffects(val3.m_spawnEffects, fx, source);
			AddAttackEffects(val3.m_preSpawnEffects, fx, source);
			Walk(val3.m_aoePrefab, source, depth + 1);
			if (val3.m_spawnPrefab != null)
			{
				GameObject[] spawnPrefab = val3.m_spawnPrefab;
				for (int i = 0; i < spawnPrefab.Length; i++)
				{
					Walk(spawnPrefab[i], source, depth + 1);
				}
			}
		}

		private static bool WalkedAtAll(GameObject prefab)
		{
			if (!Walked.Contains((prefab, Source.Player)) && !Walked.Contains((prefab, Source.Enemy)))
			{
				return Walked.Contains((prefab, Source.Boss));
			}
			return true;
		}

		private static void AddAttackEffects(EffectList list, Fx fx, Source source)
		{
			if (list?.m_effectPrefabs == null)
			{
				return;
			}
			EffectData[] effectPrefabs = list.m_effectPrefabs;
			for (int i = 0; i < effectPrefabs.Length; i++)
			{
				GameObject val = effectPrefabs[i]?.m_prefab;
				if (!((Object)(object)val == (Object)null))
				{
					if (IsSpawner(val))
					{
						Walk(val, source, 0);
					}
					else if (!IsBody(val))
					{
						Add(val, fx);
					}
				}
			}
		}

		private static void AddEffects(EffectList list, Fx fx)
		{
			if (list?.m_effectPrefabs == null)
			{
				return;
			}
			EffectData[] effectPrefabs = list.m_effectPrefabs;
			for (int i = 0; i < effectPrefabs.Length; i++)
			{
				GameObject val = effectPrefabs[i]?.m_prefab;
				if ((Object)(object)val != (Object)null && !IsBody(val))
				{
					Add(val, fx);
				}
			}
		}

		private static void Add(GameObject prefab, Fx fx)
		{
			ByPrefab.TryGetValue(prefab, out var value);
			ByPrefab[prefab] = value | (int)fx;
			int prefabHash = _scene.GetPrefabHash(prefab);
			ByHash.TryGetValue(prefabHash, out var value2);
			ByHash[prefabHash] = value2 | (int)fx;
		}

		private static bool IsSpawner(GameObject prefab)
		{
			if (!((Object)(object)prefab.GetComponent<Projectile>() != (Object)null) && !((Object)(object)prefab.GetComponent<Aoe>() != (Object)null))
			{
				return (Object)(object)prefab.GetComponent<SpawnAbility>() != (Object)null;
			}
			return true;
		}

		private static bool IsBody(GameObject prefab)
		{
			return Bodies.Any((Type type) => (Object)(object)prefab.GetComponent(type) != (Object)null);
		}
	}
	internal enum Source
	{
		Unknown,
		Player,
		Enemy,
		Boss
	}
	internal static class Sources
	{
		internal static Source Current;

		private static readonly int[] PlayerKinds = new int[6] { 32, 64, 128, 256, 512, 1024 };

		private static readonly int[] EnemyKinds = new int[6] { 2048, 4096, 8192, 16384, 32768, 65536 };

		private static readonly int[] BossKinds = new int[6] { 131072, 262144, 524288, 1048576, 2097152, 4194304 };

		internal static readonly int EnemyFamily = EnemyKinds.Aggregate(0, (int mask, int fx) => mask | fx);

		internal static readonly int BossFamily = BossKinds.Aggregate(0, (int mask, int fx) => mask | fx);

		internal static readonly int Trails = 4260864;

		private static readonly int Split = PlayerKinds.Aggregate(0, (int mask, int fx) => mask | fx) | EnemyFamily | BossFamily;

		internal static Source Of(Character character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return Source.Unknown;
			}
			if (character.IsPlayer() || character.IsTamed())
			{
				return Source.Player;
			}
			if (!character.IsBoss())
			{
				return Source.Enemy;
			}
			return Source.Boss;
		}

		internal static Fx For(Fx playerKind, Source source)
		{
			int num = Array.IndexOf(PlayerKinds, (int)playerKind);
			if (num < 0)
			{
				return playerKind;
			}
			return source switch
			{
				Source.Enemy => (Fx)EnemyKinds[num], 
				Source.Boss => (Fx)BossKinds[num], 
				_ => (Fx)PlayerKinds[num], 
			};
		}

		internal static int Resolve(int groups, Source source)
		{
			if ((groups & Split) == 0)
			{
				return groups;
			}
			int num = groups & ~Split;
			for (int i = 0; i < PlayerKinds.Length; i++)
			{
				int num2 = groups & PlayerKinds[i];
				int num3 = groups & EnemyKinds[i];
				int num4 = groups & BossKinds[i];
				if ((num2 | num3 | num4) != 0)
				{
					num = source switch
					{
						Source.Player => num | PlayerKinds[i], 
						Source.Enemy => num | EnemyKinds[i], 
						Source.Boss => num | BossKinds[i], 
						_ => num | ((num2 != 0) ? num2 : ((num3 != 0) ? num3 : num4)), 
					};
				}
			}
			return num;
		}
	}
	[HarmonyPatch]
	internal static class AttackSourcePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return ((IEnumerable<string>)new string[7] { "DoMeleeAttack", "DoAreaAttack", "DoNonAttack", "Update", "OnTrailStart", "ProjectileAttackTriggered", "FireProjectileBurst" }).Select((Func<string, MethodBase>)((string name) => AccessTools.Method(typeof(Attack), name, (Type[])null, (Type[])null)));
		}

		private static void Prefix(Humanoid ___m_character, out Source __state)
		{
			__state = Sources.Current;
			Sources.Current = Sources.Of((Character)(object)___m_character);
		}

		private static void Finalizer(Source __state)
		{
			Sources.Current = __state;
		}
	}
	[HarmonyPatch(typeof(Projectile), "OnHit")]
	internal static class ProjectileSourcePatch
	{
		private static void Prefix(Character ___m_owner, out Source __state)
		{
			__state = Sources.Current;
			Sources.Current = Sources.Of(___m_owner);
		}

		private static void Finalizer(Source __state)
		{
			Sources.Current = __state;
		}
	}
	[HarmonyPatch]
	internal static class AoeSourcePatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			return ((IEnumerable<string>)new string[3] { "Initiate", "OnHit", "CustomFixedUpdate" }).Select((Func<string, MethodBase>)((string name) => AccessTools.Method(typeof(Aoe), name, (Type[])null, (Type[])null)));
		}

		private static void Prefix(Character ___m_owner, out Source __state)
		{
			__state = Sources.Current;
			Sources.Current = Sources.Of(___m_owner);
		}

		private static void Finalizer(Source __state)
		{
			Sources.Current = __state;
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	internal static class DamageSourcePatch
	{
		private static void Prefix(HitData hit, out Source __state)
		{
			__state = Sources.Current;
			Sources.Current = Sources.Of((hit != null) ? hit.GetAttacker() : null);
		}

		private static void Finalizer(Source __state)
		{
			Sources.Current = __state;
		}
	}
	internal static class StatusIcons
	{
		private sealed class Visual
		{
			public ZNetView View;

			public int Groups;

			public Character Character;
		}

		private sealed class Slot
		{
			public Image Frame;

			public Image Back;

			public Image Icon;

			public Image Clock;
		}

		private sealed class Row
		{
			public RectTransform Root;

			public RectTransform Bar;

			public Slot[] Slots;

			public int Groups = -1;

			public float Size;

			public bool Border;
		}

		internal const int Groups = 31;

		private static readonly Fx[] Order = new Fx[5]
		{
			Fx.Fire,
			Fx.Poison,
			Fx.Frost,
			Fx.Lightning,
			Fx.Spirit
		};

		private static readonly int[] Hashes = new int[Order.Length];

		private const int StaggerSlot = 5;

		private const int Staggered = 32;

		private const int SlotCount = 6;

		private static readonly Sprite[] Sprites = (Sprite[])(object)new Sprite[6];

		private static readonly Color[] FrameColors = (Color[])(object)new Color[6]
		{
			new Color(1f, 0.55f, 0.1f),
			new Color(0.45f, 0.85f, 0.2f),
			new Color(0.55f, 0.85f, 1f),
			new Color(0.7f, 0.55f, 1f),
			new Color(0.92f, 0.95f, 1f),
			new Color(1f, 0.82f, 0.2f)
		};

		private static readonly string[] SlotNames = new string[6] { "Fire", "Poison", "Frost", "Lightning", "Spirit", "Stagger" };

		private static readonly Color Backing = new Color(0f, 0f, 0f, 0.6f);

		private static readonly Color ClockShade = new Color(0f, 0f, 0f, 0.65f);

		private static readonly int StaggerTag = ZSyncAnimation.GetHash("stagger");

		private static readonly FieldRef<Character, Animator> CharacterAnimator = AccessTools.FieldRefAccess<Character, Animator>("m_animator");

		private static readonly FieldRef<object, GameObject> HudGui = AccessTools.FieldRefAccess<GameObject>(AccessTools.Inner(typeof(EnemyHud), "HudData"), "m_gui");

		private static readonly List<Visual> Visuals = new List<Visual>();

		private static readonly Dictionary<Character, int> Active = new Dictionary<Character, int>();

		private static readonly Dictionary<GameObject, Row> Rows = new Dictionary<GameObject, Row>();

		private static readonly List<GameObject> Gone = new List<GameObject>();

		private static Sprite _staggerSprite;

		private static Sprite _whiteSprite;

		internal static void Build(ObjectDB db)
		{
			Visuals.Clear();
			Rows.Clear();
			Hashes[0] = SEMan.s_statusEffectBurning;
			Hashes[1] = SEMan.s_statusEffectPoison;
			Hashes[2] = SEMan.s_statusEffectFrost;
			Hashes[3] = SEMan.s_statusEffectLightning;
			Hashes[4] = SEMan.s_statusEffectSpirit;
			for (int i = 0; i < Order.Length; i++)
			{
				Sprites[i] = db.GetStatusEffect(Hashes[i])?.m_icon;
			}
			Sprites[5] = StaggerSprite();
		}

		private static Sprite StaggerSprite()
		{
			//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_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: 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_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Expected O, but got Unknown
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_staggerSprite != (Object)null)
			{
				return _staggerSprite;
			}
			Color32[] array = (Color32[])(object)new Color32[4096];
			for (int i = 0; i < 64; i++)
			{
				for (int j = 0; j < 64; j++)
				{
					int num = 0;
					for (int k = 0; k < 4; k++)
					{
						for (int l = 0; l < 4; l++)
						{
							float x = ((float)j + ((float)l + 0.5f) / 4f) / 64f * 2f - 1f;
							float y = ((float)i + ((float)k + 0.5f) / 4f) / 64f * 2f - 1f;
							if (InBurst(x, y))
							{
								num++;
							}
						}
					}
					array[i * 64 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(255 * num / 16));
				}
			}
			Texture2D val = new Texture2D(64, 64, (TextureFormat)4, false)
			{
				name = "CleanCombatStagger",
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1
			};
			val.SetPixels32(array);
			val.Apply(false, true);
			_staggerSprite = Sprite.Create(val, new Rect(0f, 0f, 64f, 64f), new Vector2(0.5f, 0.5f), 100f);
			((Object)_staggerSprite).name = "CleanCombatStagger";
			return _staggerSprite;
		}

		private static bool InBurst(float x, float y)
		{
			float num = Mathf.Atan2(y, x);
			float num2 = Mathf.Cos(2f * num);
			float num3 = 0.4f + 0.25f * num2 * num2;
			float num4 = 0.3f + num3 * Mathf.Pow(Mathf.Abs(Mathf.Cos(4f * num)), 8f);
			return x * x + y * y <= num4 * num4;
		}

		internal static void Track(ZNetView view, int groups)
		{
			if (Plugin.IsOn(Plugin.StatusIcons))
			{
				Visuals.Add(new Visual
				{
					View = view,
					Groups = groups
				});
			}
		}

		internal static void Update(IDictionary huds)
		{
			if (!Plugin.IsOn(Plugin.StatusIcons))
			{
				Clear();
				return;
			}
			Active.Clear();
			for (int num = Visuals.Count - 1; num >= 0; num--)
			{
				Visual visual = Visuals[num];
				if ((Object)(object)visual.View == (Object)null || !visual.View.IsValid())
				{
					Visuals.RemoveAt(num);
				}
				else
				{
					if ((Object)(object)visual.Character == (Object)null)
					{
						visual.Character = FindCharacter(visual.View.GetZDO());
					}
					if (!((Object)(object)visual.Character == (Object)null))
					{
						Active.TryGetValue(visual.Character, out var value);
						Active[visual.Character] = value | visual.Groups;
					}
				}
			}
			IDictionaryEnumerator enumerator = huds.GetEnumerator();
			while (enumerator.MoveNext())
			{
				object key = enumerator.Key;
				Character val = (Character)((key is Character) ? key : null);
				GameObject val2 = ((enumerator.Value != null) ? HudGui.Invoke(enumerator.Value) : null);
				if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
				{
					continue;
				}
				Active.TryGetValue(val, out var value2);
				if (val.IsOwner())
				{
					value2 |= FromSEMan(val.GetSEMan());
				}
				if (Plugin.StaggerIcon.Value && val.IsStaggering())
				{
					value2 |= 0x20;
				}
				if (Rows.TryGetValue(val2, out var value3) || value2 != 0)
				{
					if (value3 == null)
					{
						value3 = CreateRow(val2);
					}
					Layout(value3, value2);
					UpdateClocks(value3, val, value2);
				}
			}
			foreach (GameObject key2 in Rows.Keys)
			{
				if ((Object)(object)key2 == (Object)null)
				{
					Gone.Add(key2);
				}
			}
			foreach (GameObject item in Gone)
			{
				Rows.Remove(item);
			}
			Gone.Clear();
		}

		private static Character FindCharacter(ZDO zdo)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (zdo == null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return null;
			}
			long num = zdo.GetLong(ZDOVars.s_ownerZDOUserId, 0L);
			long num2 = zdo.GetLong(ZDOVars.s_ownerZDOId, 0L);
			if (num == 0L && num2 == 0L)
			{
				return null;
			}
			GameObject val = ZNetScene.instance.FindInstance(new ZDOID(num, (uint)num2));
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return val.GetComponent<Character>();
		}

		private static int FromSEMan(SEMan seman)
		{
			int num = 0;
			if (seman == null)
			{
				return num;
			}
			for (int i = 0; i < Order.Length; i++)
			{
				if (seman.HaveStatusEffect(Hashes[i]))
				{
					num |= (int)Order[i];
				}
			}
			return num;
		}

		private static Row CreateRow(GameObject gui)
		{
			//IL_001f: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			Transform obj = gui.transform.Find("Health");
			RectTransform val = (RectTransform)(((object)((obj is RectTransform) ? obj : null)) ?? ((object)(RectTransform)gui.transform));
			Transform obj2 = gui.transform.Find("Health/health_slow");
			RectTransform bar = (RectTransform)(((object)((obj2 is RectTransform) ? obj2 : null)) ?? ((object)val));
			RectTransform val2 = (RectTransform)new GameObject("CleanCombatStatusIcons", new Type[1] { typeof(RectTransform) }).transform;
			((Transform)val2).SetParent((Transform)(object)val, false);
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(1f, 0.5f);
			val2.anchorMax = val3;
			val2.anchorMin = val3;
			val2.pivot = new Vector2(0f, 0.5f);
			val2.anchoredPosition = new Vector2(4f, 0f);
			val2.sizeDelta = Vector2.zero;
			Image template = IconTemplate();
			Slot[] array = new Slot[6];
			for (int i = 0; i < 6; i++)
			{
				Image val4 = NewImage(template, (Transform)(object)val2, SlotNames[i], FrameColors[i]);
				RectTransform rectTransform = ((Graphic)val4).rectTransform;
				((Vector2)(ref val3))..ctor(0f, 0.5f);
				rectTransform.anchorMax = val3;
				rectTransform.anchorMin = val3;
				rectTransform.pivot = new Vector2(0f, 0.5f);
				Image val5 = NewImage(template, (Transform)(object)rectTransform, "Backing", Backing);
				Image val6 = NewImage(template, (Transform)(object)((Graphic)val5).rectTransform, "Icon", Color.white);
				val6.sprite = Sprites[i];
				val6.preserveAspect = true;
				Image val7 = NewImage(template, (Transform)(object)((Graphic)val5).rectTransform, "Clock", ClockShade);
				val7.sprite = WhiteSprite();
				val7.type = (Type)3;
				val7.fillMethod = (FillMethod)4;
				val7.fillOrigin = 2;
				val7.fillClockwise = true;
				val7.fillAmount = 0f;
				((Behaviour)val7).enabled = false;
				((Component)val4).gameObject.SetActive(false);
				array[i] = new Slot
				{
					Frame = val4,
					Back = val5,
					Icon = val6,
					Clock = val7
				};
			}
			Row row = new Row
			{
				Root = val2,
				Bar = bar,
				Slots = array
			};
			Rows[gui] = row;
			return row;
		}

		private static Image NewImage(Image template, Transform parent, string name, Color color)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			Image val = (((Object)(object)template != (Object)null) ? Object.Instantiate<Image>(template, parent, false) : new GameObject(name, new Type[2]
			{
				typeof(RectTransform),
				typeof(Image)
			}).GetComponent<Image>());
			if ((Object)(object)template == (Object)null)
			{
				((Transform)((Graphic)val).rectTransform).SetParent(parent, false);
			}
			foreach (Transform item in ((Component)val).transform)
			{
				((Component)item).gameObject.SetActive(false);
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			((Object)val).name = name;
			((Transform)((Graphic)val).rectTransform).localScale = Vector3.one;
			((Transform)((Graphic)val).rectTransform).localRotation = Quaternion.identity;
			val.sprite = null;
			val.type = (Type)0;
			val.preserveAspect = false;
			((Graphic)val).raycastTarget = false;
			((Graphic)val).color = color;
			return val;
		}

		private static void Stretch(RectTransform rect, float inset)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_003d: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = Vector2.zero;
			rect.anchorMax = Vector2.one;
			rect.pivot = new Vector2(0.5f, 0.5f);
			rect.offsetMin = new Vector2(inset, inset);
			rect.offsetMax = new Vector2(0f - inset, 0f - inset);
		}

		private static Image IconTemplate()
		{
			RectTransform val = (((Object)(object)Hud.instance != (Object)null) ? Hud.instance.m_statusEffectTemplate : null);
			Transform val2 = (((Object)(object)val != (Object)null) ? ((Transform)val).Find("Icon") : null);
			if (!((Object)(object)val2 != (Object)null))
			{
				return null;
			}
			return ((Component)val2).GetComponent<Image>();
		}

		private static void Layout(Row row, int groups)
		{
			//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_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			Rect rect = row.Bar.rect;
			float num = Mathf.Clamp(((Rect)(ref rect)).height, 10f, 28f) * (float)Plugin.StatusIconSize.Value / 100f;
			bool value = Plugin.StatusIconBorder.Value;
			if (row.Groups == groups && Mathf.Approximately(row.Size, num) && row.Border == value)
			{
				return;
			}
			row.Groups = groups;
			row.Size = num;
			row.Border = value;
			float num2 = (value ? Mathf.Max(1.5f, num * 0.12f) : 0f);
			float num3 = (value ? 1f : 0f);
			float num4 = num + 2f * (num2 + num3);
			float num5 = 0f;
			for (int i = 0; i < 6; i++)
			{
				Slot slot = row.Slots[i];
				bool flag = (groups & (1 << i)) != 0 && (Object)(object)slot.Icon.sprite != (Object)null;
				if (((Component)slot.Frame).gameObject.activeSelf != flag)
				{
					((Component)slot.Frame).gameObject.SetActive(flag);
				}
				if (flag)
				{
					((Behaviour)slot.Frame).enabled = value;
					((Behaviour)slot.Back).enabled = value;
					Stretch(((Graphic)slot.Back).rectTransform, num2);
					Stretch(((Graphic)slot.Icon).rectTransform, num3);
					Stretch(((Graphic)slot.Clock).rectTransform, num3);
					RectTransform rectTransform = ((Graphic)slot.Frame).rectTransform;
					rectTransform.sizeDelta = new Vector2(num4, num4);
					rectTransform.anchoredPosition = new Vector2(num5, 0f);
					num5 += num4 + 2f;
				}
			}
		}

		private static void UpdateClocks(Row row, Character character, int groups)
		{
			bool value = Plugin.StatusClock.Value;
			SEMan seman = ((value && character.IsOwner()) ? character.GetSEMan() : null);
			for (int i = 0; i < 6; i++)
			{
				float num = -1f;
				if (value && (groups & (1 << i)) != 0)
				{
					num = ((i == 5) ? StaggerElapsed(character) : StatusElapsed(seman, Hashes[i]));
				}
				Image clock = row.Slots[i].Clock;
				bool flag = num >= 0f;
				if (((Behaviour)clock).enabled != flag)
				{
					((Behaviour)clock).enabled = flag;
				}
				if (flag)
				{
					clock.fillAmount = Mathf.Round(num * 120f) / 120f;
				}
			}
		}

		private static float StatusElapsed(SEMan seman, int hash)
		{
			StatusEffect val = ((seman != null) ? seman.GetStatusEffect(hash) : null);
			if ((Object)(object)val == (Object)null || val.m_ttl <= 0f)
			{
				return -1f;
			}
			return Mathf.Clamp01(1f - val.GetRemaningTime() / val.m_ttl);
		}

		private static float StaggerElapsed(Character character)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			Animator val = CharacterAnimator.Invoke(character);
			if ((Object)(object)val == (Object)null)
			{
				return -1f;
			}
			AnimatorStateInfo val2 = val.GetCurrentAnimatorStateInfo(0);
			if (((AnimatorStateInfo)(ref val2)).tagHash != StaggerTag)
			{
				if (!val.IsInTransition(0))
				{
					return -1f;
				}
				val2 = val.GetNextAnimatorStateInfo(0);
				if (((AnimatorStateInfo)(ref val2)).tagHash != StaggerTag)
				{
					return -1f;
				}
			}
			return Mathf.Clamp01(((AnimatorStateInfo)(ref val2)).normalizedTime);
		}

		private static Sprite WhiteSprite()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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_0091: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_whiteSprite != (Object)null)
			{
				return _whiteSprite;
			}
			Texture2D val = new Texture2D(4, 4, (TextureFormat)4, false)
			{
				name = "CleanCombatWhite"
			};
			Color32[] array = (Color32[])(object)new Color32[16];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue);
			}
			val.SetPixels32(array);
			val.Apply(false, true);
			_whiteSprite = Sprite.Create(val, new Rect(0f, 0f, 4f, 4f), new Vector2(0.5f, 0.5f), 100f);
			((Object)_whiteSprite).name = "CleanCombatWhite";
			return _whiteSprite;
		}

		private static void Clear()
		{
			Visuals.Clear();
			foreach (Row value in Rows.Values)
			{
				if ((Object)(object)value.Root != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)value.Root).gameObject);
				}
			}
			Rows.Clear();
		}
	}
	internal static class Visuals
	{
		private static readonly List<Renderer> Renderers = new List<Renderer>();

		private static readonly List<ParticleSystem> Particles = new List<ParticleSystem>();

		private static readonly List<Light> Lights = new List<Light>();

		private static readonly List<TrailRenderer> Trails = new List<TrailRenderer>();

		private static readonly List<LineRenderer> Lines = new List<LineRenderer>();

		private static readonly HashSet<string> Logged = new HashSet<string>();

		internal static void Apply(GameObject instance, int groups)
		{
			int num = groups & Plugin.HideMask;
			int num2 = groups & Plugin.ReduceMask;
			if (num != 0)
			{
				Hide(instance, num);
			}
			else if (num2 != 0)
			{
				int num3 = Sources.EnemyFamily | Sources.BossFamily;
				if ((num2 & ~num3) == 0)
				{
					Reduce(instance, num2, Plugin.EnemyReduceAmount, Plugin.EnemyReduceSize);
				}
				else
				{
					Reduce(instance, num2, Plugin.ReduceAmount, Plugin.ReduceSize);
				}
			}
		}

		private static void Hide(GameObject instance, int groups)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			bool flag = (groups & ~Sources.Trails) == 0;
			instance.GetComponentsInChildren<Renderer>(true, Renderers);
			foreach (Renderer renderer in Renderers)
			{
				if (!flag || (!(renderer is MeshRenderer) && !(renderer is SkinnedMeshRenderer)))
				{
					renderer.enabled = false;
				}
			}
			Renderers.Clear();
			instance.GetComponentsInChildren<ParticleSystem>(true, Particles);
			foreach (ParticleSystem particle in Particles)
			{
				EmissionModule emission = particle.emission;
				((EmissionModule)(ref emission)).enabled = false;
				LightsModule lights = particle.lights;
				((LightsModule)(ref lights)).enabled = false;
				particle.Clear(false);
			}
			Particles.Clear();
			HideLights(instance);
			LogOnce(instance, "Hiding", groups);
		}

		private static void Reduce(GameObject instance, int groups, float amount, float size)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: 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_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: 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)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			instance.GetComponentsInChildren<ParticleSystem>(true, Particles);
			foreach (ParticleSystem particle in Particles)
			{
				MainModule main = particle.main;
				((MainModule)(ref main)).maxParticles = Mathf.CeilToInt((float)((MainModule)(ref main)).maxParticles * amount);
				DisableIfBroken(particle);
				if (((MainModule)(ref main)).startSize3D)
				{
					((MainModule)(ref main)).startSizeX = Scale(((MainModule)(ref main)).startSizeX, size);
					((MainModule)(ref main)).startSizeY = Scale(((MainModule)(ref main)).startSizeY, size);
					((MainModule)(ref main)).startSizeZ = Scale(((MainModule)(ref main)).startSizeZ, size);
				}
				else
				{
					((MainModule)(ref main)).startSize = Scale(((MainModule)(ref main)).startSize, size);
				}
				EmissionModule emission = particle.emission;
				((EmissionModule)(ref emission)).rateOverTime = Scale(((EmissionModule)(ref emission)).rateOverTime, amount);
				((EmissionModule)(ref emission)).rateOverDistance = Scale(((EmissionModule)(ref emission)).rateOverDistance, amount);
				for (int i = 0; i < ((EmissionModule)(ref emission)).burstCount; i++)
				{
					Burst burst = ((EmissionModule)(ref emission)).GetBurst(i);
					((Burst)(ref burst)).count = Scale(((Burst)(ref burst)).count, amount, keepOne: true);
					((EmissionModule)(ref emission)).SetBurst(i, burst);
				}
				LightsModule lights = particle.lights;
				((LightsModule)(ref lights)).enabled = false;
			}
			Particles.Clear();
			instance.GetComponentsInChildren<TrailRenderer>(true, Trails);
			foreach (TrailRenderer trail in Trails)
			{
				trail.widthMultiplier *= size;
			}
			Trails.Clear();
			instance.GetComponentsInChildren<LineRenderer>(true, Lines);
			foreach (LineRenderer line in Lines)
			{
				line.widthMultiplier *= size;
			}
			Lines.Clear();
			HideLights(instance);
			LogOnce(instance, "Reducing", groups);
		}

		private static void DisableIfBroken(ParticleSystem particles)
		{
			ParticleSystemRenderer val = default(ParticleSystemRenderer);
			if (((Component)particles).TryGetComponent<ParticleSystemRenderer>(ref val) && ((Renderer)val).enabled)
			{
				Material sharedMaterial = ((Renderer)val).sharedMaterial;
				Shader val2 = (((Object)(object)sharedMaterial != (Object)null) ? sharedMaterial.shader : null);
				if ((Object)(object)val2 == (Object)null || !val2.isSupported || ((Object)val2).name == "Hidden/InternalErrorShader")
				{
					((Renderer)val).enabled = false;
				}
			}
		}

		private static void HideLights(GameObject instance)
		{
			instance.GetComponentsInChildren<Light>(true, Lights);
			foreach (Light light in Lights)
			{
				((Behaviour)light).enabled = false;
				light.cullingMask = 0;
			}
			Lights.Clear();
		}

		private static MinMaxCurve Scale(MinMaxCurve curve, float factor, bool keepOne = false)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			ParticleSystemCurveMode mode = ((MinMaxCurve)(ref curve)).mode;
			if ((int)mode != 0)
			{
				if ((int)mode == 3)
				{
					((MinMaxCurve)(ref curve)).constantMin = Scaled(((MinMaxCurve)(ref curve)).constantMin, factor, keepOne);
					((MinMaxCurve)(ref curve)).constantMax = Scaled(((MinMaxCurve)(ref curve)).constantMax, factor, keepOne);
				}
				else
				{
					((MinMaxCurve)(ref curve)).curveMultiplier = ((MinMaxCurve)(ref curve)).curveMultiplier * factor;
				}
			}
			else
			{
				((MinMaxCurve)(ref curve)).constant = Scaled(((MinMaxCurve)(ref curve)).constant, factor, keepOne);
			}
			return curve;
		}

		private static float Scaled(float value, float factor, bool keepOne)
		{
			if (!keepOne || !(value >= 1f))
			{
				return value * factor;
			}
			return Mathf.Max(1f, Mathf.Round(value * factor));
		}

		private static void LogOnce(GameObject instance, string action, int groups)
		{
			if (Plugin.LogChanged.Value)
			{
				string prefabName = Utils.GetPrefabName(instance);
				if (Logged.Add(prefabName))
				{
					Plugin.Log.LogInfo((object)$"{action} {prefabName} ({(Fx)groups}).");
				}
			}
		}
	}
}