Decompiled source of TML Infection v1.2.0

Mods/TMLInfection.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.Json;
using Il2CppFishNet.Object;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppScheduleOne.Core.Weather;
using Il2CppScheduleOne.DevUtilities;
using Il2CppScheduleOne.Effects;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.Messaging;
using Il2CppScheduleOne.NPCs;
using Il2CppScheduleOne.Networking;
using Il2CppScheduleOne.PlayerScripts;
using Il2CppScheduleOne.Product;
using Il2CppScheduleOne.UI.MainMenu;
using Il2CppScheduleOne.Weather;
using Il2CppSystem.Collections.Generic;
using MelonLoader;
using MelonLoader.Preferences;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using S1API.Console;
using S1API.Entities;
using S1API.GameTime;
using S1API.Lifecycle;
using S1API.Money;
using S1API.UI;
using TMLInfection;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: MelonInfo(typeof(Core), "TML Infection", "1.2.0", "ThatMaddLadd", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("TMLInfection")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0+d2c92b3760785cb969d7407d481067dd605cc07e")]
[assembly: AssemblyProduct("TMLInfection")]
[assembly: AssemblyTitle("TMLInfection")]
[assembly: AssemblyVersion("1.2.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 TMLInfection
{
	internal static class BloodSky
	{
		private static readonly Color MoonGlow = new Color(1f, 0.22f, 0.16f);

		private static readonly int SkyUpper = Shader.PropertyToID("_SkyUpperColor");

		private static readonly int SkyMiddle = Shader.PropertyToID("_SkyMiddleColor");

		private static readonly int SkyLower = Shader.PropertyToID("_SkyLowerColor");

		private static readonly int MoonColor = Shader.PropertyToID("_MoonColor");

		private static readonly int MoonSize = Shader.PropertyToID("_MoonSize");

		private static readonly int FogColor = Shader.PropertyToID("_FogColor");

		private static readonly int FogDensity = Shader.PropertyToID("_FogDensity");

		private static float _blend;

		private static float _moonBlend;

		private static bool _tinted;

		public static void LateUpdate()
		{
			bool flag = Settings.BloodSky && Horde.Active;
			float num = ((!flag) ? 0f : (Horde.BloodMoon ? Settings.BloodMoonSkyStrength : Settings.BloodSkyStrength));
			float num2 = ((Settings.BloodSkyFadeSeconds > 0f) ? (Time.deltaTime / Settings.BloodSkyFadeSeconds) : 1f);
			_blend = Mathf.MoveTowards(_blend, num, num2);
			_moonBlend = Mathf.MoveTowards(_moonBlend, (flag && Horde.BloodMoon) ? 1f : 0f, num2);
			if ((!(_blend <= 0f) || _tinted) && NetworkSingleton<EnvironmentManager>.InstanceExists)
			{
				EnvironmentManager instance = NetworkSingleton<EnvironmentManager>.Instance;
				SkyState currentSkyState = instance._currentSkyState;
				if (currentSkyState != null)
				{
					Paint(currentSkyState, instance._dayNightController);
					_tinted = _blend > 0f;
				}
			}
		}

		public static void Reset()
		{
			_blend = 0f;
			_moonBlend = 0f;
			_tinted = false;
		}

		private static void Paint(SkyState sky, DayNightController? dayNight)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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)
			//IL_004d: 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_0054: 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_0064: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: 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_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			Color val = Toward(sky.SkyUpperColor, 0.3f, 1f);
			Color val2 = Toward(sky.SkyMiddleColor, 0.65f, 1f);
			Color val3 = Toward(sky.SkyLowerColor, 1f + 0.4f * _moonBlend, 1f);
			Color val4 = Color.Lerp(sky.MoonColor, Keep(MoonGlow, sky.MoonColor.a), _blend);
			float num = sky.MoonSize * Mathf.Lerp(1f, Settings.BloodMoonSize, _moonBlend);
			Color val5 = Toward(sky.FogColor, 0.35f, 1f);
			float num2 = sky.FogDensity * Mathf.Lerp(1f, Settings.BloodMoonFog, _moonBlend);
			Shader.SetGlobalColor(SkyUpper, val);
			Shader.SetGlobalColor(SkyMiddle, val2);
			Shader.SetGlobalColor(SkyLower, val3);
			Shader.SetGlobalColor(MoonColor, val4);
			Shader.SetGlobalFloat(MoonSize, num);
			Shader.SetGlobalColor(FogColor, val5);
			Shader.SetGlobalFloat(FogDensity, num2);
			object obj;
			if (dayNight == null)
			{
				obj = null;
			}
			else
			{
				MeshRenderer skyRenderer = dayNight._skyRenderer;
				obj = ((skyRenderer != null) ? ((Renderer)skyRenderer).sharedMaterial : null);
			}
			Material val6 = (Material)obj;
			if ((Object)(object)val6 != (Object)null)
			{
				SetColor(val6, SkyUpper, val);
				SetColor(val6, SkyMiddle, val2);
				SetColor(val6, SkyLower, val3);
				SetColor(val6, MoonColor, val4);
				if (val6.HasProperty(MoonSize))
				{
					val6.SetFloat(MoonSize, num);
				}
			}
			Light val7 = ((dayNight != null) ? dayNight._moonLight : null);
			if ((Object)(object)val7 != (Object)null)
			{
				val7.color = Color.Lerp(sky.MoonLightColor, Keep(MoonGlow, sky.MoonLightColor.a), _blend);
			}
			RenderSettings.fogColor = val5;
			RenderSettings.fogDensity = num2;
			RenderSettings.ambientSkyColor = Toward(sky.AmbientSkyColor, 0.5f, 0.6f);
			RenderSettings.ambientEquatorColor = Toward(sky.AmbientEquatorColor, 0.4f, 0.6f);
			RenderSettings.ambientGroundColor = Toward(sky.AmbientGroundColor, 0.25f, 0.6f);
		}

		private static void SetColor(Material material, int id, Color color)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (material.HasProperty(id))
			{
				material.SetColor(id, color);
			}
		}

		private static Color Toward(Color current, float shade, float weight)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: 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_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			Color bloodSkyColor = Settings.BloodSkyColor;
			Color val = default(Color);
			((Color)(ref val))..ctor(bloodSkyColor.r * shade, bloodSkyColor.g * shade, bloodSkyColor.b * shade, current.a);
			return Color.Lerp(current, val, _blend * weight);
		}

		private static Color Keep(Color color, float alpha)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: 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_0013: Unknown result type (might be due to invalid IL or missing references)
			return new Color(color.r, color.g, color.b, alpha);
		}
	}
	internal static class Json
	{
		public static T? Read<T>(string json) where T : class
		{
			try
			{
				return JsonSerializer.Deserialize<T>(json);
			}
			catch (Exception ex) when (((ex is JsonException || ex is NotSupportedException) ? 1 : 0) != 0)
			{
				return null;
			}
		}

		public static string Write<T>(T value)
		{
			return JsonSerializer.Serialize(value, new JsonSerializerOptions
			{
				WriteIndented = true
			});
		}
	}
	public sealed class Core : MelonMod
	{
		internal static Instance Log { get; private set; }

		public override void OnInitializeMelon()
		{
			Log = ((MelonBase)this).LoggerInstance;
			Settings.Load();
			Lang.Load(Settings.Language);
			Stats.Load();
			GameLifecycle.OnPreLoad += Horde.Detach;
			GameLifecycle.OnPreSceneChange += Horde.Detach;
			GameLifecycle.OnPreSceneChange += BloodSky.Reset;
			GameLifecycle.OnLoadComplete += Horde.Attach;
			TimeManager.OnHourPass = (Action)Delegate.Combine(TimeManager.OnHourPass, new Action(Horde.OnHour));
			TimeManager.OnHourPass = (Action)Delegate.Combine(TimeManager.OnHourPass, new Action(Infection.OnHour));
			TimeManager.OnSleepEnd = (Action<int>)Delegate.Combine(TimeManager.OnSleepEnd, (Action<int>)delegate
			{
				Infection.Cure();
			});
		}

		public override void OnUpdate()
		{
			Horde.Update();
		}

		public override void OnSceneWasInitialized(int buildIndex, string sceneName)
		{
			MenuTheme.OnScene(sceneName);
		}

		public override void OnLateUpdate()
		{
			BloodSky.LateUpdate();
		}

		internal static void Debug(string message)
		{
			if (Settings.DebugLogs)
			{
				Log.Msg(message);
			}
		}
	}
	internal static class Horde
	{
		public static readonly Vector3 Crypt = new Vector3(-46f, 1.06f, 74f);

		private const string SpeedControl = "tmlinfection_shamble";

		private const float ThinkInterval = 0.5f;

		private const float BiteInterval = 0.2f;

		private const float ChaseRefresh = 0.75f;

		private const float WanderRadius = 10f;

		private const float PoolTimeout = 30f;

		private const int RiseAttempts = 16;

		private const float BannerGap = 4.5f;

		private const string GlowName = "TMLInfectionGlow";

		private static readonly Random Rng = new Random();

		private static readonly Dictionary<Zombie, float> Risen = new Dictionary<Zombie, float>();

		private static readonly Dictionary<Zombie, float> Graves = new Dictionary<Zombie, float>();

		private static readonly Dictionary<Zombie, float> NextBite = new Dictionary<Zombie, float>();

		private static readonly HashSet<Zombie> Hooked = new HashSet<Zombie>();

		private static readonly List<(float at, Vector3 position)> Bitten = new List<(float, Vector3)>();

		private static readonly Queue<Vector3> Turning = new Queue<Vector3>();

		private static bool _attached;

		private static Player? _hookedPlayer;

		private static float _nextThink;

		private static float _nextBite;

		private static int _turned;

		private static bool _disguised;

		public static bool Active { get; private set; }

		public static bool BloodMoon { get; private set; }

		public static int RisenCount => Risen.Count;

		public static int DeadCount => Graves.Count;

		private static bool IsHost
		{
			get
			{
				Lobby instance = Singleton<Lobby>.Instance;
				return instance == null || !instance.IsInLobby || instance.IsHost;
			}
		}

		public static void Attach()
		{
			_attached = true;
			MelonCoroutines.Start(WaitForPool());
		}

		public static void Detach()
		{
			_attached = false;
			Active = false;
			Risen.Clear();
			Graves.Clear();
			NextBite.Clear();
			Hooked.Clear();
			ClearTurning();
			Stats.Abandon();
			Infection.Cure();
			Hud.Hide();
		}

		public static void OnHour()
		{
			if (!_attached)
			{
				return;
			}
			bool flag = Settings.Enabled && IsNightTime();
			if (flag && !Active)
			{
				bool flag2 = IsBloodMoonTonight();
				if (flag2 || RollsTonight())
				{
					Rise(flag2);
				}
			}
			else if (!flag && Active)
			{
				Dawn();
			}
		}

		public static void Rise(bool bloodMoon)
		{
			Active = true;
			BloodMoon = bloodMoon;
			Hud.Announce(bloodMoon ? Strings.BloodMoon : Strings.Rise, danger: true);
			Stats.NightStart();
			if (bloodMoon && Settings.PatientZero)
			{
				Hud.AnnounceAfter(4.5f, Strings.PatientZero, danger: true);
			}
			Core.Debug("The dead rise" + (bloodMoon ? " under a Blood Moon" : "") + ".");
		}

		public static void Dawn()
		{
			Stats.Dawn(BloodMoon, 4.5f);
			Active = false;
			BloodMoon = false;
			ClearTurning();
			if (IsHost)
			{
				foreach (Zombie item in Everyone())
				{
					if (((NPC)item).IsDead)
					{
						Safely((Action)((NPC)item).Revive);
					}
					Bury(item);
				}
			}
			Graves.Clear();
			NextBite.Clear();
			Hud.Announce(Strings.Dawn, danger: false);
			Core.Debug("Dawn. The dead are buried.");
		}

		public static void Update()
		{
			if (_attached && Active)
			{
				float time = Time.time;
				if (IsHost && time >= _nextThink)
				{
					_nextThink = time + 0.5f;
					Think(time);
				}
				if (time >= _nextBite)
				{
					_nextBite = time + 0.2f;
					Bite(time);
				}
			}
		}

		private static IEnumerator WaitForPool()
		{
			float deadline = Time.realtimeSinceStartup + 30f;
			while (_attached && Time.realtimeSinceStartup < deadline && Pool().Count() < 24)
			{
				yield return (object)new WaitForSeconds(1f);
			}
			if (!_attached)
			{
				yield break;
			}
			foreach (Zombie item in Everyone())
			{
				Hook(item);
				if (IsHost)
				{
					Bury(item);
				}
			}
			Player local = Player.Local;
			if (local != null && local != _hookedPlayer)
			{
				_hookedPlayer = local;
				local.OnDeath += Infection.Cure;
				local.OnDeath += Stats.Died;
			}
			Core.Debug($"{Pool().Count()} zombies waiting in the crypt.");
			OnHour();
		}

		private static void Hook(Zombie zombie)
		{
			if (Hooked.Add(zombie))
			{
				((NPC)zombie).OnDeath += delegate
				{
					Reward(zombie);
				};
				HideContact(zombie);
				if (zombie.Kind == ZombieKind.PatientZero)
				{
					Glow(zombie);
				}
			}
		}

		private static void Think(float now)
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			List<Player> list = Player.All.Where((Player p) => p != null && !p.IsDead).ToList();
			if (list.Count == 0)
			{
				return;
			}
			for (int num = Bitten.Count - 1; num >= 0; num--)
			{
				if (!(now < Bitten[num].at))
				{
					Turning.Enqueue(Bitten[num].position);
					Bitten.RemoveAt(num);
					_turned++;
				}
			}
			List<Player> hunted = list.Where((Player p) => !Disguised(p)).ToList();
			List<(NPC, Vector3)> prey = (List<(NPC, Vector3)>)(Settings.AttackNpcs ? ((IList)Prey()) : ((IList)new List<(NPC, Vector3)>()));
			int num2 = Math.Min(24, (BloodMoon ? Settings.BloodMoonZombies : Settings.Zombies) + _turned);
			int num3 = 0;
			foreach (Zombie item in Pool())
			{
				Hook(item);
				if (((NPC)item).IsDead)
				{
					Risen.Remove(item);
					if (!Graves.TryGetValue(item, out var value))
					{
						Graves[item] = now + (float)Settings.RespawnSeconds;
					}
					else if (now >= value)
					{
						Graves.Remove(item);
						Safely((Action)((NPC)item).Revive);
						Bury(item);
					}
				}
				else if (num3++ >= num2)
				{
					if (Risen.ContainsKey(item))
					{
						Bury(item);
					}
				}
				else
				{
					Tend(item, list, hunted, prey, now);
				}
			}
			Zombie zombie = Boss();
			if (zombie != null)
			{
				Hook(zombie);
				if (BloodMoon && Settings.PatientZero && !((NPC)zombie).IsDead)
				{
					Tend(zombie, list, hunted, prey, now);
				}
				else if (Risen.ContainsKey(zombie))
				{
					Bury(zombie);
				}
			}
		}

		private static void Tend(Zombie zombie, List<Player> players, List<Player> hunted, List<(NPC npc, Vector3 position)> prey, float now)
		{
			//IL_004e: 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_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			if (!Risen.TryGetValue(zombie, out var value))
			{
				Raise(zombie, players, now);
				return;
			}
			Player val = players.OrderBy((Player p) => Vector3.Distance(((NPC)zombie).Position, p.Position)).First();
			if (Vector3.Distance(((NPC)zombie).Position, val.Position) > Settings.DespawnDistance)
			{
				Bury(zombie);
			}
			else
			{
				if (now < value || !((NPC)zombie).IsConscious)
				{
					return;
				}
				float sight = zombie.Stats.Sight * (BloodMoon ? 2f : 1f);
				(Player, float) tuple = (from p in hunted
					select (player: p, distance: Vector3.Distance(((NPC)zombie).Position, p.Position)) into t
					where t.distance <= sight
					orderby t.distance
					select t).FirstOrDefault();
				(NPC, Vector3, float) tuple2 = (from p in prey
					select (npc: p.npc, position: p.position, distance: Vector3.Distance(((NPC)zombie).Position, p.position)) into t
					where t.distance <= sight
					orderby t.distance
					select t).FirstOrDefault();
				if (tuple.Item1 != null && ((Object)(object)tuple2.Item1 == (Object)null || tuple.Item2 <= tuple2.Item3))
				{
					Go(zombie, tuple.Item1.Position);
					Risen[zombie] = now + 0.75f;
				}
				else if ((Object)(object)tuple2.Item1 != (Object)null)
				{
					if (tuple2.Item3 <= Settings.AttackRange)
					{
						BiteNpc(zombie, tuple2.Item1, now);
					}
					Go(zombie, tuple2.Item2);
					Risen[zombie] = now + 0.75f;
				}
				else if (!Moving(zombie))
				{
					Vector3 position = ((NPC)zombie).Position;
					Vector3 val2 = val.Position - ((NPC)zombie).Position;
					Vector3 centre = position + ((Vector3)(ref val2)).normalized * 10f * 0.5f;
					Go(zombie, (Vector3)(((??)NearbyPoint(centre, 2f, 10f)) ?? ((NPC)zombie).Position));
					Risen[zombie] = now + 4f + (float)Rng.NextDouble() * 4f;
				}
			}
		}

		private static void Bite(float now)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			Player local = Player.Local;
			if (local == null || local.IsDead)
			{
				return;
			}
			bool num = Disguised(local);
			if (num && !_disguised)
			{
				Hud.Announce(Strings.Disguised, danger: false);
			}
			_disguised = num;
			if (num)
			{
				return;
			}
			foreach (Zombie item in Everyone())
			{
				if (((NPC)item).IsDead || !((NPC)item).IsConscious)
				{
					continue;
				}
				NPC val = Live(item);
				if (val != null && val.isVisible && !(Vector3.Distance(((NPC)item).Position, local.Position) > Settings.AttackRange) && (!NextBite.TryGetValue(item, out var value) || !(now < value)))
				{
					NextBite[item] = now + Settings.AttackCooldown;
					int num2 = BiteDamage(item, Settings.Damage);
					if (num2 > 0)
					{
						local.Damage(num2);
					}
					Infection.Bitten();
				}
			}
		}

		private static void BiteNpc(Zombie zombie, NPC victim, float now)
		{
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			if (NextBite.TryGetValue(zombie, out var value) && now < value)
			{
				return;
			}
			NextBite[zombie] = now + Settings.AttackCooldown;
			int damage = BiteDamage(zombie, Settings.NpcDamage);
			if (damage > 0)
			{
				Safely(delegate
				{
					victim.Health.TakeDamage((float)damage, true);
				});
			}
			Safely((Action)victim.SetPanicked_Server);
			if (Settings.BittenRise && (Object)(object)victim.Health != (Object)null && victim.Health.IsDead)
			{
				Bitten.Add((now + Settings.BittenRiseSeconds, ((Component)victim).transform.position));
			}
		}

		private static void ClearTurning()
		{
			Bitten.Clear();
			Turning.Clear();
			_turned = 0;
		}

		private static bool Disguised(Player player)
		{
			if (!Settings.ZombifyingDisguise)
			{
				return false;
			}
			try
			{
				Player component = ((Component)player.Transform).GetComponent<Player>();
				object obj;
				if (component == null)
				{
					obj = null;
				}
				else
				{
					ProductItemInstance consumedProduct = component.ConsumedProduct;
					obj = ((consumedProduct != null) ? ((ItemInstance)consumedProduct).Definition : null);
				}
				PropertyItemDefinition obj2 = ((obj != null) ? ((Il2CppObjectBase)obj).TryCast<PropertyItemDefinition>() : null);
				List<Effect> val = ((obj2 != null) ? obj2.Properties : null);
				if (val == null)
				{
					return false;
				}
				for (int i = 0; i < val.Count; i++)
				{
					if ((Object)(object)val[i] != (Object)null && val[i].Name.IndexOf("Zombif", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
				}
				return false;
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static void Glow(Zombie zombie)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)((NPC)zombie).gameObject == (Object)null) && !((Object)(object)((NPC)zombie).gameObject.transform.Find("TMLInfectionGlow") != (Object)null))
			{
				GameObject val = new GameObject("TMLInfectionGlow");
				val.transform.SetParent(((NPC)zombie).gameObject.transform, false);
				val.transform.localPosition = new Vector3(0f, 1.7f, 0f);
				Light obj = val.AddComponent<Light>();
				obj.type = (LightType)2;
				obj.color = new Color(1f, 0.08f, 0.04f);
				obj.range = 5f;
				obj.intensity = 3f;
			}
		}

		private static int BiteDamage(Zombie zombie, int baseDamage)
		{
			return Mathf.RoundToInt((float)baseDamage * zombie.Stats.Damage * (BloodMoon ? Settings.BloodMoonDamage : 1f));
		}

		private static List<(NPC npc, Vector3 position)> Prey()
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			List<(NPC, Vector3)> list = new List<(NPC, Vector3)>();
			List<NPC> nPCRegistry = NPCManager.NPCRegistry;
			if (nPCRegistry == null)
			{
				return list;
			}
			for (int i = 0; i < nPCRegistry.Count; i++)
			{
				try
				{
					NPC val = nPCRegistry[i];
					if (!((Object)(object)val == (Object)null) && !val.ID.StartsWith("tmlinfection_zombie_") && val.isVisible && !val.isInBuilding && !val.IsInVehicle && val.IsConscious && !((Object)(object)val.Health == (Object)null) && !val.Health.IsDead)
					{
						list.Add((val, ((Component)val).transform.position));
					}
				}
				catch (Exception)
				{
				}
			}
			return list;
		}

		private static void Reward(Zombie zombie)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (zombie.Kind == ZombieKind.PatientZero)
			{
				Hud.Announce(Strings.PatientZeroDown, danger: false);
			}
			Player local = Player.Local;
			if (local != null && !local.IsDead && !(Vector3.Distance(((NPC)zombie).Position, local.Position) > Settings.RewardRange))
			{
				Stats.Kill();
				float num = ((zombie.Kind == ZombieKind.PatientZero) ? Settings.PatientZeroReward : Settings.KillReward);
				if (num > 0f)
				{
					Money.ChangeCashBalance(num, true, true);
				}
				Core.Debug($"{((NPC)zombie).FullName} put down. +{num} cash.");
			}
		}

		private static void Raise(Zombie zombie, List<Player> players, float now)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			NPC npc = Live(zombie);
			if (npc == null)
			{
				return;
			}
			Vector3? spot = null;
			NavMeshHit val = default(NavMeshHit);
			if (zombie.Kind != ZombieKind.PatientZero && Turning.Count > 0 && NavMesh.SamplePosition(Turning.Dequeue(), ref val, 3f, -1))
			{
				spot = ((NavMeshHit)(ref val)).position;
			}
			Vector3 position = players[Rng.Next(players.Count)].Position;
			for (int i = 0; i < 16; i++)
			{
				if (spot.HasValue)
				{
					break;
				}
				Vector3? val2 = NearbyPoint(position, Settings.SpawnMinDistance, Settings.SpawnMaxDistance);
				if (val2.HasValue)
				{
					Vector3 p = val2.GetValueOrDefault();
					if (players.All((Player pl) => Vector3.Distance(pl.Position, p) >= Settings.SpawnMinDistance * 0.8f) && Risen.Keys.All((Zombie other) => other == zombie || Vector3.Distance(((NPC)other).Position, p) >= Settings.Spacing))
					{
						spot = p;
					}
				}
			}
			if (!spot.HasValue)
			{
				Core.Debug($"No ground to raise {((NPC)zombie).FullName} near {position}.");
				return;
			}
			HideContact(zombie);
			Safely(delegate
			{
				npc.Movement.SetAgentEnabled(true);
			});
			Safely(delegate
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				((NPC)zombie).Movement.Warp(spot.Value);
			});
			npc.SetVisible(true, true);
			if (((NPC)zombie).Movement.DoesSpeedControlExist("tmlinfection_shamble"))
			{
				((NPC)zombie).Movement.RemoveSpeedControl("tmlinfection_shamble");
			}
			((NPC)zombie).Movement.AddSpeedControl("tmlinfection_shamble", 60, (BloodMoon ? Settings.BloodMoonSpeed : Settings.Speed) * zombie.Stats.Speed);
			Safely(zombie.Toughen);
			Risen[zombie] = now;
			Core.Debug($"{((NPC)zombie).FullName} rose at {spot.Value}.");
		}

		private static void Bury(Zombie zombie)
		{
			Risen.Remove(zombie);
			NPC val = Live(zombie);
			if (val != null)
			{
				Safely(delegate
				{
					((NPC)zombie).Movement.Stop();
				});
				Safely(delegate
				{
					//IL_000b: Unknown result type (might be due to invalid IL or missing references)
					((NPC)zombie).Movement.Warp(Crypt);
				});
				if (((NPC)zombie).Movement.DoesSpeedControlExist("tmlinfection_shamble"))
				{
					((NPC)zombie).Movement.RemoveSpeedControl("tmlinfection_shamble");
				}
				val.SetVisible(false, true);
			}
		}

		private static Vector3? NearbyPoint(Vector3 centre, float min, float max)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//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_006f: Unknown result type (might be due to invalid IL or missing references)
			float num = (float)(Rng.NextDouble() * Math.PI * 2.0);
			float num2 = min + (float)Rng.NextDouble() * (max - min);
			NavMeshHit val = default(NavMeshHit);
			if (!NavMesh.SamplePosition(centre + new Vector3(Mathf.Cos(num), 0f, Mathf.Sin(num)) * num2, ref val, 4f, -1))
			{
				return null;
			}
			return ((NavMeshHit)(ref val)).position;
		}

		private static void Go(Zombie zombie, Vector3 destination)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			Safely(delegate
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				((NPC)zombie).Movement.SetDestination(destination);
			});
		}

		private static bool Moving(Zombie zombie)
		{
			try
			{
				NPC? obj = Live(zombie);
				return obj != null && obj.Movement.IsMoving;
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static void HideContact(Zombie zombie)
		{
			NPC? obj = Live(zombie);
			MSGConversation val = ((obj != null) ? obj.MSGConversation : null);
			if (val != null && val.uiCreated)
			{
				val.SetEntryVisibility(false);
			}
		}

		private static bool IsNightTime()
		{
			int num = TimeManager.CurrentTime / 100;
			if (Settings.RiseHour >= Settings.DawnHour)
			{
				if (num < Settings.RiseHour)
				{
					return num < Settings.DawnHour;
				}
				return true;
			}
			if (num >= Settings.RiseHour)
			{
				return num < Settings.DawnHour;
			}
			return false;
		}

		private static bool RollsTonight()
		{
			if (Settings.RiseChance >= 100)
			{
				return true;
			}
			return new Random(((TimeManager.CurrentTime / 100 < Settings.DawnHour && Settings.RiseHour > Settings.DawnHour) ? (TimeManager.ElapsedDays - 1) : TimeManager.ElapsedDays) * 7919 + 17).Next(100) < Settings.RiseChance;
		}

		private static bool IsBloodMoonTonight()
		{
			DateTime now = DateTime.Now;
			if (Settings.BloodMoonOnHalloween && now.Month == 10 && now.Day == 31)
			{
				return true;
			}
			if (Settings.BloodMoonEvery > 0)
			{
				return (TimeManager.ElapsedDays + 1) % Settings.BloodMoonEvery == 0;
			}
			return false;
		}

		private static IEnumerable<Zombie> Pool()
		{
			return from z in NPC.All.OfType<Zombie>().Where(delegate(Zombie z)
				{
					if (z.Kind != ZombieKind.PatientZero)
					{
						NPC val = Live(z);
						if (val != null)
						{
							return ((NetworkBehaviour)val).IsSpawned;
						}
						return false;
					}
					return false;
				})
				orderby ((NPC)z).ID
				select z;
		}

		private static Zombie? Boss()
		{
			return NPC.All.OfType<Zombie>().FirstOrDefault(delegate(Zombie z)
			{
				if (z.Kind == ZombieKind.PatientZero)
				{
					NPC val = Live(z);
					if (val != null)
					{
						return ((NetworkBehaviour)val).IsSpawned;
					}
					return false;
				}
				return false;
			});
		}

		private static IEnumerable<Zombie> Everyone()
		{
			Zombie zombie = Boss();
			if (zombie == null)
			{
				return Pool();
			}
			return Pool().Append(zombie);
		}

		private static NPC? Live(Zombie zombie)
		{
			if (!((Object)(object)((NPC)zombie).gameObject == (Object)null))
			{
				return ((NPC)zombie).gameObject.GetComponent<NPC>();
			}
			return null;
		}

		private static void Safely(Action action)
		{
			try
			{
				action();
			}
			catch (Exception ex)
			{
				Core.Debug("Zombie action failed: " + ex.Message);
			}
		}
	}
	internal static class Hud
	{
		private const float BannerSeconds = 4f;

		private static readonly Color Backdrop = new Color(0.06f, 0.07f, 0.06f, 0.72f);

		private static readonly Color Danger = new Color(1f, 0.42f, 0.32f);

		private static readonly Color Calm = new Color(0.85f, 0.88f, 0.8f);

		private static readonly Color Sick = new Color(0.62f, 0.86f, 0.4f);

		private static GameObject? _root;

		private static GameObject? _banner;

		private static GameObject? _meter;

		private static Text? _bannerText;

		private static Text? _meterText;

		private static int _run;

		public static void Announce(string text, bool danger)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			Ensure();
			_bannerText.text = text;
			((Graphic)_bannerText).color = (danger ? Danger : Calm);
			_banner.SetActive(true);
			MelonCoroutines.Start(HideBanner(++_run));
		}

		public static void AnnounceAfter(float seconds, string text, bool danger)
		{
			MelonCoroutines.Start(Later(seconds, text, danger));
		}

		public static void ShowInfection(float level)
		{
			if (!(level <= 0f) || !((Object)(object)_meter == (Object)null))
			{
				Ensure();
				_meter.SetActive(level > 0f);
				_meterText.text = Strings.Infection(Mathf.CeilToInt(level));
			}
		}

		public static void Hide()
		{
			_run++;
			if ((Object)(object)_banner != (Object)null)
			{
				_banner.SetActive(false);
			}
			if ((Object)(object)_meter != (Object)null)
			{
				_meter.SetActive(false);
			}
		}

		private static IEnumerator Later(float seconds, string text, bool danger)
		{
			int run = _run;
			yield return (object)new WaitForSeconds(seconds);
			if (run == _run || (Object)(object)_banner == (Object)null || !_banner.activeSelf)
			{
				Announce(text, danger);
			}
		}

		private static IEnumerator HideBanner(int run)
		{
			yield return (object)new WaitForSeconds(4f);
			if (run == _run && (Object)(object)_banner != (Object)null)
			{
				_banner.SetActive(false);
			}
		}

		private static void Ensure()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_005d: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_root != (Object)null))
			{
				_root = new GameObject("TMLInfectionHud");
				Object.DontDestroyOnLoad((Object)(object)_root);
				Canvas obj = _root.AddComponent<Canvas>();
				obj.renderMode = (RenderMode)0;
				obj.sortingOrder = 500;
				CanvasScaler obj2 = _root.AddComponent<CanvasScaler>();
				obj2.uiScaleMode = (ScaleMode)1;
				obj2.referenceResolution = new Vector2(1920f, 1080f);
				(_banner, _bannerText) = Panel("Banner", new Vector2(0.34f, 0.9f), new Vector2(0.66f, 0.95f), 22, (FontStyle)1);
				(_meter, _meterText) = Panel("Infection", new Vector2(0.44f, 0.86f), new Vector2(0.56f, 0.89f), 16, (FontStyle)0);
				((Graphic)_meterText).color = Sick;
				_banner.SetActive(false);
				_meter.SetActive(false);
			}
		}

		private static (GameObject, Text) Panel(string name, Vector2 min, Vector2 max, int size, FontStyle style)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: 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)
			GameObject val = UIFactory.Panel(name, _root.transform, Backdrop, (Vector2?)min, (Vector2?)max, false);
			Stretch(val.GetComponent<RectTransform>());
			Text val2 = UIFactory.Text("Label", "", val.transform, size, (TextAnchor)4, style);
			RectTransform component = ((Component)val2).GetComponent<RectTransform>();
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.one;
			Stretch(component);
			return (val, val2);
		}

		private static void Stretch(RectTransform rect)
		{
			//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)
			rect.offsetMin = Vector2.zero;
			rect.offsetMax = Vector2.zero;
		}
	}
	internal static class Infection
	{
		private const float Full = 100f;

		public static float Level { get; private set; }

		public static void Bitten()
		{
			Set(Level + Settings.InfectionPerHit);
		}

		public static void OnHour()
		{
			if (Level <= 0f)
			{
				return;
			}
			if (Level < 100f)
			{
				Set(Level - Settings.InfectionDecayPerHour);
			}
			else if (Settings.InfectionDamage > 0)
			{
				Player local = Player.Local;
				if (local != null && !local.IsDead)
				{
					local.Damage(Settings.InfectionDamage);
				}
			}
		}

		public static void Cure()
		{
			Set(0f);
		}

		public static void Set(float level)
		{
			bool flag = Level >= 100f;
			Level = Mathf.Clamp(level, 0f, 100f);
			if (Level >= 100f && !flag)
			{
				Hud.Announce(Strings.Infected, danger: true);
			}
			Hud.ShowInfection(Level);
		}
	}
	internal static class Lang
	{
		private const string Fallback = "en";

		private static Dictionary<string, string> _fallback = new Dictionary<string, string>();

		private static Dictionary<string, string> _active = new Dictionary<string, string>();

		public static string Folder => Path.Combine(MelonEnvironment.UserDataDirectory, "TMLInfection", "lang");

		public static bool Exists(string language)
		{
			if (!File.Exists(Path.Combine(Folder, language + ".json")))
			{
				return Embedded(language) != null;
			}
			return true;
		}

		public static void Load(string language)
		{
			Directory.CreateDirectory(Folder);
			string[] array = new string[2] { "en", "es" };
			for (int i = 0; i < array.Length; i++)
			{
				Export(array[i]);
			}
			_fallback = Read("en");
			_active = ((language == "en") ? _fallback : Read(language));
			if (_active.Count == 0)
			{
				Core.Log.Warning("No translations found for '" + language + "'. Using English.");
				_active = _fallback;
			}
		}

		public static string T(string key)
		{
			if (!_active.TryGetValue(key, out string value))
			{
				if (!_fallback.TryGetValue(key, out string value2))
				{
					return key;
				}
				return value2;
			}
			return value;
		}

		public static string T(string key, params object[] args)
		{
			return string.Format(CultureInfo.InvariantCulture, T(key), args);
		}

		private static Dictionary<string, string> Read(string language)
		{
			Dictionary<string, string> dictionary = Parse(Embedded(language)) ?? new Dictionary<string, string>();
			string text = Path.Combine(Folder, language + ".json");
			if (!File.Exists(text))
			{
				return dictionary;
			}
			Dictionary<string, string> dictionary2 = Parse(File.ReadAllText(text));
			if (dictionary2 == null)
			{
				Core.Log.Warning(text + " isn't valid JSON. Using the built-in text.");
				return dictionary;
			}
			foreach (KeyValuePair<string, string> item in dictionary2)
			{
				item.Deconstruct(out var key, out var value);
				string key2 = key;
				string value2 = value;
				dictionary[key2] = value2;
			}
			return dictionary;
		}

		private static void Export(string language)
		{
			string path = Path.Combine(Folder, language + ".json");
			if (!File.Exists(path))
			{
				string text = Embedded(language);
				if (text != null)
				{
					File.WriteAllText(path, text);
				}
			}
		}

		private static string? Embedded(string language)
		{
			using Stream stream = typeof(Lang).Assembly.GetManifestResourceStream("TMLInfection.lang." + language + ".json");
			if (stream == null)
			{
				return null;
			}
			using StreamReader streamReader = new StreamReader(stream);
			return streamReader.ReadToEnd();
		}

		private static Dictionary<string, string>? Parse(string? json)
		{
			if (string.IsNullOrEmpty(json))
			{
				return null;
			}
			return Json.Read<Dictionary<string, string>>(json);
		}
	}
	internal static class MenuTheme
	{
		private sealed class Drip
		{
			public RectTransform Streak;

			public RectTransform Bulb;

			public float Width;

			public float Length;

			public float Target;

			public float Speed;

			public float Delay;

			public float DropY;

			public float DropSpeed;

			public bool Falling;
		}

		private const string MenuScene = "Menu";

		private const float ZombifySeconds = 20f;

		private const int VignetteSize = 128;

		private const int DripCount = 18;

		private const float BandHeight = 34f;

		private const float DropGravity = 1400f;

		private static readonly Color Blood = new Color(0.55f, 0.03f, 0.03f);

		private static readonly Color DarkBlood = new Color(0.3f, 0.01f, 0.01f);

		private static readonly Color DeadSkin = new Color(0.47f, 0.53f, 0.43f);

		private static readonly Color Tagline = new Color(0.85f, 0.12f, 0.1f);

		private static int _run;

		public static void OnScene(string sceneName)
		{
			_run++;
			if (sceneName == "Menu" && Settings.MenuTheme)
			{
				MelonCoroutines.Start(Haunt(_run));
			}
		}

		private static IEnumerator Haunt(int run)
		{
			yield return null;
			if (run != _run)
			{
				yield break;
			}
			List<(Light light, float intensity)> lights = TintScene();
			List<Drip> drips;
			RawImage vignette = Overlay(out drips);
			bool zombified = false;
			float start = Time.realtimeSinceStartup;
			while (run == _run && (Object)(object)vignette != (Object)null)
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (!zombified && realtimeSinceStartup - start < 20f)
				{
					zombified = Zombify();
				}
				((Graphic)vignette).color = new Color(1f, 1f, 1f, 0.75f + 0.2f * Mathf.Sin(realtimeSinceStartup * 2.2f));
				Flicker(lights);
				Bleed(drips, realtimeSinceStartup - start, Time.unscaledDeltaTime);
				yield return null;
			}
		}

		private static List<(Light light, float intensity)> TintScene()
		{
			//IL_001c: 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_002b: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			List<(Light, float)> list = new List<(Light, float)>();
			foreach (Light item in Object.FindObjectsOfType<Light>())
			{
				item.color = Color.Lerp(item.color, Blood, 0.6f);
				list.Add((item, item.intensity));
			}
			RenderSettings.ambientLight = Color.Lerp(RenderSettings.ambientLight, Blood * 0.5f, 0.6f);
			RenderSettings.fogColor = Blood * 0.4f;
			return list;
		}

		private static void Flicker(List<(Light light, float intensity)> lights)
		{
			foreach (var (val, num) in lights)
			{
				if ((Object)(object)val != (Object)null)
				{
					val.intensity = num * ((Random.value < 0.03f) ? 0.35f : 1f);
				}
			}
		}

		private static bool Zombify()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				MainMenuRig val = Object.FindObjectOfType<MainMenuRig>();
				if ((Object)(object)val == (Object)null || (Object)(object)val.Avatar == (Object)null || !((Component)val.Avatar).gameObject.activeInHierarchy)
				{
					return false;
				}
				val.Avatar.Appearance.SetSkinColor(DeadSkin);
				val.Avatar.Appearance.SetEyeShadowVisible(true);
				return true;
			}
			catch (Exception ex)
			{
				Core.Debug("Could not zombify the menu character: " + ex.Message);
				return true;
			}
		}

		private static RawImage Overlay(out List<Drip> drips)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: 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_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("TMLInfectionMenu");
			Canvas obj = val.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = -10;
			CanvasScaler obj2 = val.AddComponent<CanvasScaler>();
			obj2.uiScaleMode = (ScaleMode)1;
			obj2.referenceResolution = new Vector2(1920f, 1080f);
			RawImage obj3 = Child<RawImage>(val, "Vignette", Vector2.zero, Vector2.one);
			obj3.texture = (Texture)(object)VignetteTexture();
			((Graphic)obj3).raycastTarget = false;
			drips = Drips(val);
			DateTime now = DateTime.Now;
			string text = ((now.Month == 10 && now.Day == 31) ? Strings.MenuHalloween : Strings.MenuTagline);
			Text obj4 = UIFactory.Text("Tagline", text, val.transform, 30, (TextAnchor)8, (FontStyle)1);
			RectTransform component = ((Component)obj4).GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0.55f, 0.095f);
			component.anchorMax = new Vector2(0.98f, 0.145f);
			component.offsetMin = Vector2.zero;
			component.offsetMax = Vector2.zero;
			((Graphic)obj4).color = Tagline;
			((Graphic)obj4).raycastTarget = false;
			return obj3;
		}

		private static T Child<T>(GameObject parent, string name, Vector2 min, Vector2 max) where T : Component
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent.transform, false);
			T result = val.AddComponent<T>();
			RectTransform component = val.GetComponent<RectTransform>();
			component.anchorMin = min;
			component.anchorMax = max;
			component.offsetMin = Vector2.zero;
			component.offsetMax = Vector2.zero;
			return result;
		}

		private static List<Drip> Drips(GameObject root)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			RawImage obj = Child<RawImage>(root, "BloodBand", new Vector2(0f, 1f), Vector2.one);
			((Graphic)obj).rectTransform.offsetMin = new Vector2(0f, -34f);
			obj.texture = (Texture)(object)BandTexture();
			obj.uvRect = new Rect(0f, 0f, 6f, 1f);
			((Graphic)obj).raycastTarget = false;
			Texture2D texture = StreakTexture();
			Texture2D texture2 = BulbTexture();
			List<Drip> list = new List<Drip>();
			for (int i = 0; i < 18; i++)
			{
				float num = ((float)i + 0.5f + Random.Range(-0.35f, 0.35f)) / 18f;
				float num2 = Mathf.Abs(num - 0.5f) * 2f;
				float num3 = Random.Range(7f, 15f);
				RawImage val = Child<RawImage>(root, "Drip", new Vector2(num, 1f), new Vector2(num, 1f));
				val.texture = (Texture)(object)texture;
				((Graphic)val).raycastTarget = false;
				((Graphic)val).rectTransform.pivot = new Vector2(0.5f, 1f);
				((Graphic)val).rectTransform.anchoredPosition = new Vector2(0f, -20.400002f);
				RawImage val2 = Child<RawImage>(root, "DripBulb", new Vector2(num, 1f), new Vector2(num, 1f));
				val2.texture = (Texture)(object)texture2;
				((Graphic)val2).raycastTarget = false;
				((Graphic)val2).rectTransform.sizeDelta = new Vector2(num3 * 1.5f, num3 * 1.8f);
				list.Add(new Drip
				{
					Streak = ((Graphic)val).rectTransform,
					Bulb = ((Graphic)val2).rectTransform,
					Width = num3,
					Target = Random.Range(30f, 110f) + num2 * num2 * Random.Range(150f, 420f),
					Speed = Random.Range(12f, 40f),
					Delay = Random.Range(0f, 5f)
				});
			}
			return list;
		}

		private static void Bleed(List<Drip> drips, float elapsed, float delta)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			foreach (Drip drip in drips)
			{
				if ((Object)(object)drip.Streak == (Object)null || elapsed < drip.Delay)
				{
					continue;
				}
				if (drip.Length < drip.Target)
				{
					drip.Length = Mathf.Min(drip.Target, drip.Length + drip.Speed * delta * (1f - 0.7f * drip.Length / drip.Target));
				}
				drip.Streak.sizeDelta = new Vector2(drip.Width, drip.Length);
				float dropY = -20.400002f - drip.Length;
				if (drip.Falling)
				{
					drip.DropSpeed += 1400f * delta;
					drip.DropY -= drip.DropSpeed * delta;
					if (drip.DropY < -1200f)
					{
						drip.Falling = false;
						drip.Target += Random.Range(5f, 25f);
						drip.DropY = dropY;
					}
				}
				else
				{
					drip.DropY = dropY;
					if (drip.Length >= drip.Target && Random.value < delta * 0.06f)
					{
						drip.Falling = true;
						drip.DropSpeed = 0f;
					}
				}
				float num = Mathf.Clamp01(drip.Length / 25f);
				((Transform)drip.Bulb).localScale = new Vector3(num, num, 1f);
				drip.Bulb.anchoredPosition = new Vector2(0f, drip.DropY);
			}
		}

		private static Color Shade(float across)
		{
			//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_003e: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Exp((0f - (across + 0.35f)) * (across + 0.35f) * 18f) * 0.3f;
			Color val = Color.Lerp(DarkBlood, Blood, 1f - Mathf.Abs(across) * 0.8f);
			return new Color(val.r + num, val.g + num * 0.3f, val.b + num * 0.3f);
		}

		private static Texture2D StreakTexture()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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)
			Texture2D val = new Texture2D(16, 1, (TextureFormat)4, false)
			{
				wrapMode = (TextureWrapMode)1
			};
			for (int i = 0; i < 16; i++)
			{
				float num = ((float)i + 0.5f) / 16f * 2f - 1f;
				Color val2 = Shade(num);
				val2.a = Mathf.Clamp01((1f - Mathf.Abs(num)) * 3f);
				val.SetPixel(i, 0, val2);
			}
			val.Apply();
			return val;
		}

		private static Texture2D BulbTexture()
		{
			//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_0013: Expected O, but got Unknown
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false)
			{
				wrapMode = (TextureWrapMode)1
			};
			for (int i = 0; i < 32; i++)
			{
				for (int j = 0; j < 32; j++)
				{
					float num = ((float)j + 0.5f) / 32f * 2f - 1f;
					float num2 = ((float)i + 0.5f) / 32f * 2f - 1f;
					float num3 = Mathf.Lerp(1f, 0.55f, Mathf.Clamp01(num2));
					float num4 = Mathf.Sqrt(num * num / (num3 * num3) + num2 * num2);
					Color val2 = Shade(num / num3);
					val2.a = Mathf.Clamp01((1f - num4) * 6f);
					val.SetPixel(j, i, val2);
				}
			}
			val.Apply();
			return val;
		}

		private static Texture2D BandTexture()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			//IL_0099: 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_00ad: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(256, 32, (TextureFormat)4, false)
			{
				wrapMode = (TextureWrapMode)0
			};
			for (int i = 0; i < 256; i++)
			{
				float num = (float)i * 2f * (float)Math.PI / 256f;
				float num2 = 0.55f + 0.18f * Mathf.Sin(num * 3f) + 0.12f * Mathf.Sin(num * 7f + 1f) + 0.07f * Mathf.Sin(num * 13f + 2f);
				for (int j = 0; j < 32; j++)
				{
					float num3 = 1f - ((float)j + 0.5f) / 32f;
					Color val2 = Color.Lerp(Blood, DarkBlood, 1f - num3 / num2);
					val2.a = Mathf.Clamp01((num2 - num3) * 12f);
					val.SetPixel(i, j, val2);
				}
			}
			val.Apply();
			return val;
		}

		private static Texture2D VignetteTexture()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false)
			{
				wrapMode = (TextureWrapMode)1
			};
			float num = 63.5f;
			for (int i = 0; i < 128; i++)
			{
				for (int j = 0; j < 128; j++)
				{
					float num2 = ((float)j - num) / num;
					float num3 = ((float)i - num) / num;
					float num4 = Mathf.Clamp01((Mathf.Sqrt(num2 * num2 + num3 * num3) - 0.55f) / 0.6f);
					val.SetPixel(j, i, new Color(Blood.r * 0.6f, 0f, 0f, num4 * num4 * 0.85f));
				}
			}
			val.Apply();
			return val;
		}
	}
	internal static class Settings
	{
		internal sealed class KindStats
		{
			public int Health { get; }

			public float Sight { get; }

			public float Speed { get; }

			public float Damage { get; }

			public KindStats(int health, float sight, float speed, float damage)
			{
				Health = health;
				Sight = sight;
				Speed = speed;
				Damage = damage;
			}
		}

		public const int PoolSize = 24;

		private static MelonPreferences_Category _category = null;

		private static MelonPreferences_Entry<bool> _debugLogs = null;

		private static MelonPreferences_Entry<bool> _enabled = null;

		public static readonly Dictionary<ZombieKind, KindStats> Kinds = new Dictionary<ZombieKind, KindStats>();

		public static bool DebugLogs
		{
			get
			{
				return _debugLogs.Value;
			}
			set
			{
				_debugLogs.Value = value;
				_category.SaveToFile(false);
			}
		}

		public static bool Enabled
		{
			get
			{
				return _enabled.Value;
			}
			set
			{
				_enabled.Value = value;
				_category.SaveToFile(false);
			}
		}

		public static string Language { get; private set; } = "en";

		public static int RiseHour { get; private set; }

		public static int DawnHour { get; private set; }

		public static int RiseChance { get; private set; }

		public static int Zombies { get; private set; }

		public static float SpawnMinDistance { get; private set; }

		public static float SpawnMaxDistance { get; private set; }

		public static float DespawnDistance { get; private set; }

		public static float Spacing { get; private set; }

		public static float Speed { get; private set; }

		public static int RespawnSeconds { get; private set; }

		public static int Damage { get; private set; }

		public static float AttackRange { get; private set; }

		public static bool AttackNpcs { get; private set; }

		public static int NpcDamage { get; private set; }

		public static float AttackCooldown { get; private set; }

		public static float KillReward { get; private set; }

		public static float RewardRange { get; private set; }

		public static float InfectionPerHit { get; private set; }

		public static float InfectionDecayPerHour { get; private set; }

		public static int InfectionDamage { get; private set; }

		public static int BloodMoonEvery { get; private set; }

		public static bool BloodMoonOnHalloween { get; private set; }

		public static int BloodMoonZombies { get; private set; }

		public static float BloodMoonSpeed { get; private set; }

		public static float BloodMoonDamage { get; private set; }

		public static bool BittenRise { get; private set; }

		public static float BittenRiseSeconds { get; private set; }

		public static bool PatientZero { get; private set; }

		public static float PatientZeroReward { get; private set; }

		public static bool ZombifyingDisguise { get; private set; }

		public static float BloodMoonFog { get; private set; }

		public static bool MenuTheme { get; private set; }

		public static bool BloodSky { get; private set; }

		public static Color BloodSkyColor { get; private set; }

		public static float BloodSkyStrength { get; private set; }

		public static float BloodMoonSkyStrength { get; private set; }

		public static float BloodSkyFadeSeconds { get; private set; }

		public static float BloodMoonSize { get; private set; }

		public static void Load()
		{
			//IL_0630: Unknown result type (might be due to invalid IL or missing references)
			_category = MelonPreferences.CreateCategory("TMLInfection", "TML Infection");
			_enabled = _category.CreateEntry<bool>("Enabled", true, (string)null, "Zombies rise at night. Turn off to keep the mod installed but quiet.", false, false, (ValueValidator)null, (string)null);
			_debugLogs = _category.CreateEntry<bool>("DebugLogs", false, (string)null, "Log horde events to the MelonLoader console.", false, false, (ValueValidator)null, (string)null);
			Language = _category.CreateEntry<string>("Language", "en", (string)null, "Text language: en or es. Files are in UserData/TMLInfection/lang.", false, false, (ValueValidator)null, (string)null).Value.Trim().ToLowerInvariant();
			RiseHour = Int("RiseHour", 21, 0, 23, "Hour the dead rise (24-hour clock).");
			DawnHour = Int("DawnHour", 6, 0, 23, "Hour the dead go back to their graves.");
			if (RiseHour == DawnHour)
			{
				Core.Log.Warning($"RiseHour and DawnHour are both {RiseHour}. Using 21 and 6.");
				RiseHour = 21;
				DawnHour = 6;
			}
			RiseChance = Int("RiseChance", 100, 0, 100, "Percent chance the dead rise on a normal night. Blood Moons always rise.");
			Zombies = Int("Zombies", 12, 1, 24, $"Zombies hunting at once on a normal night (most {24}).");
			SpawnMinDistance = Float("SpawnMinDistance", 20f, 5f, 200f, "Closest a zombie rises to a player, in metres.");
			SpawnMaxDistance = Float("SpawnMaxDistance", 80f, 5f, 200f, "Furthest a zombie rises from a player, in metres.");
			if (SpawnMaxDistance < SpawnMinDistance)
			{
				Core.Log.Warning($"SpawnMaxDistance ({SpawnMaxDistance}) is below SpawnMinDistance ({SpawnMinDistance}). Using 20 and 80.");
				SpawnMinDistance = 20f;
				SpawnMaxDistance = 80f;
			}
			DespawnDistance = Float("DespawnDistance", 130f, 20f, 500f, "Zombies further than this from every player sink and rise again nearer.");
			Spacing = Float("Spacing", 15f, 0f, 100f, "Fewest metres between zombies when they rise, so the horde spreads out.");
			Speed = Float("Speed", 0.45f, 0.1f, 2f, "Base zombie walk speed (1 = normal pedestrian). Each type multiplies this.");
			RespawnSeconds = Int("RespawnSeconds", 45, 5, 3600, "Seconds before a killed zombie rises again.");
			Damage = Int("Damage", 8, 0, 100, "Health a zombie bite takes from a player. Each type multiplies this.");
			AttackRange = Float("AttackRange", 1.6f, 0.5f, 5f, "Reach of a zombie bite, in metres.");
			AttackNpcs = _category.CreateEntry<bool>("AttackNpcs", true, (string)null, "Zombies also hunt and bite NPCs, cops included. They never attack each other.", false, false, (ValueValidator)null, (string)null).Value;
			NpcDamage = Int("NpcDamage", 40, 0, 100, "Health a zombie bite takes from an NPC. Each type multiplies this.");
			AttackCooldown = Float("AttackCooldown", 1.5f, 0.2f, 30f, "Seconds between bites from the same zombie.");
			KillReward = Float("KillReward", 40f, 0f, 100000f, "Cash for every player near a zombie when it dies.");
			RewardRange = Float("RewardRange", 20f, 1f, 200f, "Players this close to a dying zombie get the reward.");
			InfectionPerHit = Float("InfectionPerHit", 12f, 0f, 100f, "Infection added by each bite, out of 100.");
			InfectionDecayPerHour = Float("InfectionDecayPerHour", 4f, 0f, 100f, "Infection lost each in-game hour while below 100.");
			InfectionDamage = Int("InfectionDamage", 5, 0, 100, "Health lost each in-game hour while fully infected. Sleep cures it.");
			BloodMoonEvery = Int("BloodMoonEvery", 7, 0, 365, "Every this many nights is a Blood Moon (0 = never).");
			BloodMoonOnHalloween = _category.CreateEntry<bool>("BloodMoonOnHalloween", true, (string)null, "Every night is a Blood Moon on 31 October (real date).", false, false, (ValueValidator)null, (string)null).Value;
			BloodMoonZombies = Int("BloodMoonZombies", 24, 1, 24, "Zombies hunting at once on a Blood Moon.");
			BloodMoonSpeed = Float("BloodMoonSpeed", 0.7f, 0.1f, 2f, "Zombie walk speed on a Blood Moon.");
			BloodMoonDamage = Float("BloodMoonDamage", 1.5f, 0.1f, 10f, "Bite damage multiplier on a Blood Moon.");
			Kind(ZombieKind.Shambler, 100, 2f, 1f, 1f);
			Kind(ZombieKind.Nightwalker, 50, 12f, 1.6f, 0.6f);
			Kind(ZombieKind.Hunter, 100, 8f, 1.2f, 1.2f);
			Kind(ZombieKind.Brute, 200, 4f, 0.7f, 2f);
			Kind(ZombieKind.PatientZero, 1000, 15f, 0.9f, 3f);
			BittenRise = _category.CreateEntry<bool>("BittenRise", true, (string)null, "NPCs killed by a zombie get back up as zombies.", false, false, (ValueValidator)null, (string)null).Value;
			BittenRiseSeconds = Float("BittenRiseSeconds", 6f, 0f, 120f, "Seconds before a bitten NPC rises.");
			PatientZero = _category.CreateEntry<bool>("PatientZero", true, (string)null, "Patient Zero, a giant boss zombie, walks on every Blood Moon.", false, false, (ValueValidator)null, (string)null).Value;
			PatientZeroReward = Float("PatientZeroReward", 2500f, 0f, 1000000f, "Cash for every player near Patient Zero when it dies.");
			ZombifyingDisguise = _category.CreateEntry<bool>("ZombifyingDisguise", true, (string)null, "Zombies ignore players under a product with the Zombifying effect.", false, false, (ValueValidator)null, (string)null).Value;
			BloodMoonFog = Float("BloodMoonFog", 2.5f, 1f, 10f, "How much thicker the fog gets on a Blood Moon (1 = normal).");
			MenuTheme = _category.CreateEntry<bool>("MenuTheme", true, (string)null, "Give the main menu a blood-red, zombified Halloween look.", false, false, (ValueValidator)null, (string)null).Value;
			BloodSky = _category.CreateEntry<bool>("BloodSky", true, (string)null, "The night sky turns blood red while the dead are out.", false, false, (ValueValidator)null, (string)null).Value;
			BloodSkyColor = Hex("BloodSkyColor", "#8A0A0A", "Colour of the blood sky, as a hex code like #8A0A0A.");
			BloodSkyStrength = Float("BloodSkyStrength", 0.6f, 0f, 1f, "How red the sky gets on a normal zombie night (0 = untouched, 1 = full).");
			BloodMoonSkyStrength = Float("BloodMoonSkyStrength", 1f, 0f, 1f, "How red the sky gets on a Blood Moon.");
			BloodSkyFadeSeconds = Float("BloodSkyFadeSeconds", 10f, 0f, 120f, "Seconds the sky takes to turn red at nightfall and back at dawn.");
			BloodMoonSize = Float("BloodMoonSize", 1.6f, 1f, 4f, "How much bigger the moon looks on a Blood Moon (1 = normal).");
		}

		private static float Float(string id, float fallback, float min, float max, string description)
		{
			MelonPreferences_Entry<float> val = _category.CreateEntry<float>(id, fallback, (string)null, description, false, false, (ValueValidator)null, (string)null);
			if (val.Value >= min && val.Value <= max)
			{
				return val.Value;
			}
			Core.Log.Warning($"{id} must be between {min} and {max}, got {val.Value}. Using {fallback}.");
			return fallback;
		}

		private static void Kind(ZombieKind kind, int health, float sight, float speed, float damage)
		{
			string text = kind.ToString();
			Kinds[kind] = new KindStats(Int(text + "Health", health, 1, 5000, "Health of a " + text + "."), Float(text + "Sight", sight, 0.5f, 200f, "Metres at which a " + text + " notices and chases someone."), Float(text + "Speed", speed, 0.1f, 5f, text + " walk speed, as a multiple of Speed."), Float(text + "Damage", damage, 0f, 10f, text + " bite damage, as a multiple of Damage and NpcDamage."));
		}

		private static Color Hex(string id, string fallback, string description)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			MelonPreferences_Entry<string> val = _category.CreateEntry<string>(id, fallback, (string)null, description, false, false, (ValueValidator)null, (string)null);
			if (TryHex(val.Value, out var color))
			{
				return color;
			}
			Core.Log.Warning($"{id} must be a hex colour like {fallback}, got {val.Value}. Using {fallback}.");
			TryHex(fallback, out color);
			return color;
		}

		private static bool TryHex(string value, out Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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)
			color = default(Color);
			string text = value.Trim().TrimStart('#');
			if (text.Length != 6 || !int.TryParse(text, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			color = new Color((float)((result >> 16) & 0xFF) / 255f, (float)((result >> 8) & 0xFF) / 255f, (float)(result & 0xFF) / 255f);
			return true;
		}

		private static int Int(string id, int fallback, int min, int max, string description)
		{
			MelonPreferences_Entry<int> val = _category.CreateEntry<int>(id, fallback, (string)null, description, false, false, (ValueValidator)null, (string)null);
			if (val.Value >= min && val.Value <= max)
			{
				return val.Value;
			}
			Core.Log.Warning($"{id} must be between {min} and {max}, got {val.Value}. Using {fallback}.");
			return fallback;
		}
	}
	internal static class Stats
	{
		private sealed class Record
		{
			public int Nights { get; set; }

			public int BloodMoons { get; set; }

			public int Kills { get; set; }

			public int BestNight { get; set; }
		}

		private static Record _record = new Record();

		private static int _tonight;

		private static bool _night;

		private static bool _died;

		private static string FilePath => Path.Combine(MelonEnvironment.UserDataDirectory, "TMLInfection", "stats.json");

		public static void Load()
		{
			try
			{
				if (File.Exists(FilePath))
				{
					_record = Json.Read<Record>(File.ReadAllText(FilePath)) ?? new Record();
				}
			}
			catch (IOException ex)
			{
				Core.Log.Warning("Could not read survival stats: " + ex.Message + ". Starting fresh.");
			}
		}

		public static void NightStart()
		{
			_night = true;
			_died = false;
			_tonight = 0;
		}

		public static void Kill()
		{
			if (_night)
			{
				_tonight++;
				_record.Kills++;
			}
		}

		public static void Died()
		{
			_died = true;
		}

		public static void Dawn(bool bloodMoon, float delay)
		{
			if (!_night)
			{
				return;
			}
			_night = false;
			_record.BestNight = Math.Max(_record.BestNight, _tonight);
			if (!_died)
			{
				_record.Nights++;
				if (bloodMoon)
				{
					_record.BloodMoons++;
				}
			}
			Save();
			string text = (_died ? Strings.Fallen(_tonight, _record.Kills) : Strings.Survived(_tonight, _record.Nights, _record.Kills, _record.BestNight));
			Hud.AnnounceAfter(delay, text, danger: false);
		}

		public static void Abandon()
		{
			if (_night)
			{
				_night = false;
				Save();
			}
		}

		private static void Save()
		{
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(FilePath));
				File.WriteAllText(FilePath, Json.Write(_record));
			}
			catch (Exception ex) when (((ex is IOException || ex is UnauthorizedAccessException) ? 1 : 0) != 0)
			{
				Core.Log.Warning("Could not save survival stats: " + ex.Message);
			}
		}
	}
	internal static class Strings
	{
		public static string Rise => Lang.T("hud.rise");

		public static string BloodMoon => Lang.T("hud.bloodmoon");

		public static string Dawn => Lang.T("hud.dawn");

		public static string Infected => Lang.T("hud.infected");

		public static string PatientZero => Lang.T("hud.patientzero");

		public static string PatientZeroDown => Lang.T("hud.patientzerodown");

		public static string Disguised => Lang.T("hud.disguised");

		public static string MenuTagline => Lang.T("menu.tagline");

		public static string MenuHalloween => Lang.T("menu.halloween");

		public static string Infection(int percent)
		{
			return Lang.T("hud.infection", percent);
		}

		public static string Survived(int tonight, int nights, int total, int best)
		{
			return Lang.T("hud.survived", tonight, nights, total, best);
		}

		public static string Fallen(int tonight, int total)
		{
			return Lang.T("hud.fallen", tonight, total);
		}
	}
	internal enum ZombieKind
	{
		Shambler,
		Nightwalker,
		Hunter,
		Brute,
		PatientZero
	}
	public abstract class Zombie : NPC
	{
		private static readonly Color[] Skins = (Color[])(object)new Color[5]
		{
			new Color(0.5f, 0.56f, 0.46f),
			new Color(0.44f, 0.47f, 0.43f),
			new Color(0.58f, 0.58f, 0.52f),
			new Color(0.4f, 0.45f, 0.4f),
			new Color(0.52f, 0.54f, 0.38f)
		};

		private static readonly Color[] Tops = (Color[])(object)new Color[5]
		{
			new Color(0.28f, 0.1f, 0.08f),
			new Color(0.3f, 0.28f, 0.24f),
			new Color(0.14f, 0.17f, 0.2f),
			new Color(0.26f, 0.22f, 0.15f),
			new Color(0.2f, 0.22f, 0.18f)
		};

		private static readonly string[] TopLayers = new string[5] { "Avatar/Layers/Top/Buttonup", "Avatar/Layers/Top/FlannelButtonup", "Avatar/Layers/Top/Tucked T", "Avatar/Layers/Top/RolledButtonup", "Avatar/Layers/Top/Overalls" };

		private static readonly Color[] Bottoms = (Color[])(object)new Color[3]
		{
			new Color(0.16f, 0.16f, 0.17f),
			new Color(0.22f, 0.19f, 0.14f),
			new Color(0.12f, 0.14f, 0.18f)
		};

		private static readonly string[] Hair = new string[6] { "Avatar/Hair/Balding/Balding", "Avatar/Hair/Receding/Receding", "Avatar/Hair/MessyBob/MessyBob", "Avatar/Hair/LongCurly/LongCurly", "Avatar/Hair/BuzzCut/BuzzCut", "" };

		private static readonly string[] Mouths = new string[4] { "Avatar/Layers/Face/Face_Agape", "Avatar/Layers/Face/Face_Agitated", "Avatar/Layers/Face/Face_Agape", "Avatar/Layers/Face/Face_Scared" };

		private static readonly string[] Wounds = new string[4] { "Avatar/Layers/Tattoos/Face/Face_Teardrop", "Avatar/Layers/Tattoos/Face/Face_ForeheadCross", "Avatar/Layers/Tattoos/Face/Face_Tribal", "" };

		private static readonly Color Blood = new Color(0.45f, 0.02f, 0.02f);

		private static readonly Color Bruise = new Color(0.22f, 0.14f, 0.2f);

		private static readonly Color DeadHair = new Color(0.32f, 0.31f, 0.28f);

		private static readonly Color Milky = new Color(0.92f, 0.9f, 0.72f);

		private static readonly Color Bloodshot = new Color(0.95f, 0.5f, 0.45f);

		private static readonly Color Hazmat = new Color(0.62f, 0.58f, 0.2f);

		private static readonly Color PatientZeroSkin = new Color(0.3f, 0.34f, 0.3f);

		private static readonly Color Glowing = new Color(1f, 0.05f, 0.05f);

		internal const string IdPrefix = "tmlinfection_zombie_";

		internal const int PatientZeroNumber = 25;

		private static readonly ZombieKind[] Mix = new ZombieKind[6]
		{
			ZombieKind.Shambler,
			ZombieKind.Nightwalker,
			ZombieKind.Shambler,
			ZombieKind.Hunter,
			ZombieKind.Shambler,
			ZombieKind.Brute
		};

		protected abstract int Number { get; }

		internal ZombieKind Kind
		{
			get
			{
				if (Number != 25)
				{
					return Mix[(Number - 1) % Mix.Length];
				}
				return ZombieKind.PatientZero;
			}
		}

		internal Settings.KindStats Stats => Settings.Kinds[Kind];

		public override bool IsPhysical => true;

		protected override void ConfigurePrefab(NPCPrefabBuilder builder)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			bool patientZero = Kind == ZombieKind.PatientZero;
			builder.WithIdentity($"{"tmlinfection_zombie_"}{Number}", patientZero ? "Patient" : "Zombie", patientZero ? "Zero" : $"#{Number}").WithSpawnPosition(Horde.Crypt).WithAppearanceDefaults((Action<AvatarDefaultsBuilder>)delegate(AvatarDefaultsBuilder avatar)
			{
				//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fa: 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_0110: Unknown result type (might be due to invalid IL or missing references)
				//IL_0140: Unknown result type (might be due to invalid IL or missing references)
				//IL_0139: 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_01d9: Unknown result type (might be due to invalid IL or missing references)
				//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
				//IL_020e: Unknown result type (might be due to invalid IL or missing references)
				//IL_021f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0230: Unknown result type (might be due to invalid IL or missing references)
				//IL_0260: Unknown result type (might be due to invalid IL or missing references)
				//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
				//IL_0311: Unknown result type (might be due to invalid IL or missing references)
				//IL_0288: Unknown result type (might be due to invalid IL or missing references)
				//IL_0299: Unknown result type (might be due to invalid IL or missing references)
				//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
				avatar.Gender = ((Number % 3 == 0) ? 1f : 0f);
				AvatarDefaultsBuilder val = avatar;
				val.Height = Kind switch
				{
					ZombieKind.PatientZero => 1.25f, 
					ZombieKind.Brute => 1.1f, 
					ZombieKind.Nightwalker => 1.02f, 
					_ => 0.94f + (float)(Number % 4) * 0.03f, 
				};
				val = avatar;
				float weight;
				switch (Kind)
				{
				case ZombieKind.Brute:
				case ZombieKind.PatientZero:
					weight = 1f;
					break;
				case ZombieKind.Nightwalker:
					weight = 0.05f;
					break;
				default:
					weight = 0.25f + (float)(Number % 5) * 0.12f;
					break;
				}
				val.Weight = weight;
				avatar.SkinColor = Color32.op_Implicit(patientZero ? PatientZeroSkin : Skins[Number % Skins.Length]);
				avatar.LeftEyeLidColor = Bruise;
				avatar.RightEyeLidColor = Bruise;
				avatar.EyeBallTint = (patientZero ? Glowing : ((Number % 2 == 0) ? Bloodshot : Milky));
				avatar.PupilDilation = (patientZero ? 0f : 0.05f);
				avatar.LeftEye = (0.95f, 0.8f);
				avatar.RightEye = ((Number % 3 == 0) ? (0.35f, 0.6f) : (0.9f, 0.75f));
				avatar.EyebrowThickness = 0.6f;
				avatar.HairPath = Hair[Number % Hair.Length];
				avatar.HairColor = DeadHair;
				avatar.WithFaceLayer(Mouths[Number % Mouths.Length], Color.black);
				avatar.WithFaceLayer("Avatar/Layers/Face/TiredEyes", Bruise);
				avatar.WithFaceLayer("Avatar/Layers/Face/EyeShadow", Blood);
				avatar.WithFaceLayer("Avatar/Layers/Face/OldPersonWrinkles", Bruise);
				string text = Wounds[Number % Wounds.Length];
				if (text.Length > 0)
				{
					avatar.WithFaceLayer(text, Blood);
				}
				if (Number % 8 == 0 && !patientZero)
				{
					avatar.WithBodyLayer("Avatar/Layers/Top/HazmatSuit", Hazmat);
					avatar.WithAccessoryLayer("Avatar/Accessories/Waist/HazmatSuit/HazmatSuit", Hazmat);
					avatar.WithAccessoryLayer("Avatar/Accessories/Head/Respirator/Respirator", Color.black);
				}
				else
				{
					avatar.WithBodyLayer(TopLayers[Number % TopLayers.Length], Tops[Number % Tops.Length]);
					avatar.WithBodyLayer("Avatar/Layers/Bottom/CargoPants", Bottoms[Number % Bottoms.Length]);
				}
			});
		}

		protected override void OnCreated()
		{
			((NPC)this).OnCreated();
			((NPC)this).Appearance.Build();
			Toughen();
		}

		internal void Toughen()
		{
			((NPC)this).MaxHealth = Stats.Health;
			((NPC)this).Heal(Stats.Health);
		}
	}
	public sealed class ZombieOne : Zombie
	{
		protected override int Number => 1;
	}
	public sealed class ZombieTwo : Zombie
	{
		protected override int Number => 2;
	}
	public sealed class ZombieThree : Zombie
	{
		protected override int Number => 3;
	}
	public sealed class ZombieFour : Zombie
	{
		protected override int Number => 4;
	}
	public sealed class ZombieFive : Zombie
	{
		protected override int Number => 5;
	}
	public sealed class ZombieSix : Zombie
	{
		protected override int Number => 6;
	}
	public sealed class ZombieSeven : Zombie
	{
		protected override int Number => 7;
	}
	public sealed class ZombieEight : Zombie
	{
		protected override int Number => 8;
	}
	public sealed class ZombieNine : Zombie
	{
		protected override int Number => 9;
	}
	public sealed class ZombieTen : Zombie
	{
		protected override int Number => 10;
	}
	public sealed class ZombieEleven : Zombie
	{
		protected override int Number => 11;
	}
	public sealed class ZombieTwelve : Zombie
	{
		protected override int Number => 12;
	}
	public sealed class ZombieThirteen : Zombie
	{
		protected override int Number => 13;
	}
	public sealed class ZombieFourteen : Zombie
	{
		protected override int Number => 14;
	}
	public sealed class ZombieFifteen : Zombie
	{
		protected override int Number => 15;
	}
	public sealed class ZombieSixteen : Zombie
	{
		protected override int Number => 16;
	}
	public sealed class ZombieSeventeen : Zombie
	{
		protected override int Number => 17;
	}
	public sealed class ZombieEighteen : Zombie
	{
		protected override int Number => 18;
	}
	public sealed class ZombieNineteen : Zombie
	{
		protected override int Number => 19;
	}
	public sealed class ZombieTwenty : Zombie
	{
		protected override int Number => 20;
	}
	public sealed class ZombieTwentyOne : Zombie
	{
		protected override int Number => 21;
	}
	public sealed class ZombieTwentyTwo : Zombie
	{
		protected override int Number => 22;
	}
	public sealed class ZombieTwentyThree : Zombie
	{
		protected override int Number => 23;
	}
	public sealed class ZombieTwentyFour : Zombie
	{
		protected override int Number => 24;
	}
	public sealed class PatientZero : Zombie
	{
		protected override int Number => 25;
	}
	public class ZombieCommand : BaseConsoleCommand
	{
		private static readonly Dictionary<string, string> Help = new Dictionary<string, string>
		{
			["status"] = "show the horde and your infection",
			["rise"] = "the dead rise now",
			["bloodmoon"] = "a Blood Moon rises now",
			["dawn"] = "send the dead back now",
			["infect <0-100>"] = "set your infection",
			["cure"] = "clear your infection",
			["on|off"] = "turn nightly outbreaks on or off",
			["debug on|off"] = "log horde events"
		};

		public override string CommandWord => "zombies";

		public override string CommandDescription => "TML Infection tools. Type 'zombies help' for options.";

		public override string ExampleUsage => "zombies bloodmoon";

		public override void ExecuteCommand(List<string> args)
		{
			string text = ((args.Count > 0) ? args[0].ToLowerInvariant() : "status");
			string text2 = ((args.Count > 1) ? args[1].ToLowerInvariant() : "");
			if (text == "help")
			{
				foreach (var (value, value2) in Help)
				{
					Core.Log.Msg($"zombies {value,-16} {value2}");
				}
				return;
			}
			if (Player.Local == null)
			{
				Core.Log.Warning("Load a save first.");
				return;
			}
			switch (text)
			{
			case "status":
				Core.Log.Msg($"Outbreaks {(Settings.Enabled ? "on" : "off")}. Horde {(Horde.Active ? "active" : "resting")}{(Horde.BloodMoon ? " (Blood Moon)" : "")}: {Horde.RisenCount} risen, {Horde.DeadCount} down. Your infection {Infection.Level:0}%.");
				return;
			case "rise":
				Horde.Rise(bloodMoon: false);
				return;
			case "bloodmoon":
				Horde.Rise(bloodMoon: true);
				return;
			case "dawn":
				Horde.Dawn();
				return;
			case "infect":
			{
				if (float.TryParse(text2, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
				{
					Infection.Set(result);
					return;
				}
				break;
			}
			case "cure":
				Infection.Cure();
				return;
			case "on":
			case "off":
				Settings.Enabled = text == "on";
				Core.Log.Msg("Outbreaks " + text + ".");
				Horde.OnHour();
				return;
			case "debug":
				if ((text2 == "on" || text2 == "off") ? true : false)
				{
					Settings.DebugLogs = text2 == "on";
					Core.Log.Msg("Debug logs " + text2 + ".");
					return;
				}
				break;
			}
			Core.Log.Warning("Unknown option. Type 'zombies help'.");
		}
	}
}