Decompiled source of Fishmen Invasion Mod v0.2.6

BepInEx/plugins/FishmenInvasionMod/FishmenInvasionMod.dll

Decompiled 8 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using FishNet;
using FishNet.Broadcast;
using FishNet.Connection;
using FishNet.Managing;
using FishNet.Managing.Object;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Transporting;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Networking;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[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 FishmenInvasionMod
{
	public enum FishmanKind
	{
		Fishman,
		Brute,
		Leaper,
		Deepjaw,
		BarnacleColossus,
		Tidecaller,
		PufferTitan
	}
	public static class Rules
	{
		public const int MinCatchGoal = 5;

		public const int MaxCatchGoal = 20;

		public const float MinionMinSize = 0.5f;

		public const float MinionMaxSize = 1.5f;

		public const float BossMinSize = 2.5f;

		public const float BossMaxSize = 3f;

		public static readonly byte[] ItemIds = new byte[7] { 244, 243, 242, 241, 240, 239, 238 };

		public static readonly ushort[] PrefabCollections = new ushort[7] { 42426, 42427, 42428, 42429, 42430, 42431, 42432 };

		public static bool EligibleIsland(byte current, byte maximum)
		{
			if (maximum >= 2 && current >= 2)
			{
				return current <= 5;
			}
			return false;
		}

		public static int CatchTarget(int baseGoal, float frequency)
		{
			return Math.Max(1, (int)Math.Ceiling((float)Math.Max(5, Math.Min(20, baseGoal)) / Math.Max(0.5f, Math.Min(3f, frequency))));
		}

		public static int Amount(int wave, float multiplier)
		{
			return Math.Max(1, Math.Min(40, (int)Math.Round((3.0 + Math.Floor((double)Math.Max(0, wave - 1) * 0.75)) * (double)Math.Max(0.5f, Math.Min(3f, multiplier)))));
		}

		public static int Rounds(int wave)
		{
			if (wave >= 3)
			{
				return Math.Min(4, 2 + (wave - 3) / 3);
			}
			return 1;
		}

		public static int Health(int wave, float multiplier, FishmanKind kind)
		{
			float num = ((kind == FishmanKind.Brute) ? 2.2f : ((kind == FishmanKind.Leaper) ? 0.78f : ((kind >= FishmanKind.Deepjaw) ? 8f : 1f)));
			return Math.Max(1, Math.Min(50000, (int)Math.Round(35.0 * (1.0 + (double)Math.Max(0, wave - 1) * 0.12) * (double)Math.Max(0.5f, Math.Min(3f, multiplier)) * (double)num)));
		}

		public static int Attack(int wave, float multiplier, FishmanKind kind)
		{
			float num = ((kind == FishmanKind.Brute) ? 1.55f : ((kind == FishmanKind.Leaper) ? 0.82f : ((kind >= FishmanKind.Deepjaw) ? 2.4f : 1f)));
			return Math.Max(1, Math.Min(1000, (int)Math.Round(8.0 * (1.0 + (double)Math.Max(0, wave - 1) * 0.01) * (double)Math.Max(0.25f, Math.Min(2f, multiplier)) * (double)num)));
		}

		public static int Worth(int wave, FishmanKind kind)
		{
			float num = ((kind == FishmanKind.Brute) ? 1.65f : ((kind == FishmanKind.Leaper) ? 1.25f : ((kind >= FishmanKind.Deepjaw) ? 10f : 1f)));
			return Math.Max(250, Math.Min(250000, (int)Math.Round(750.0 * (1.0 + (double)Math.Max(0, wave - 1) * 0.08) * (double)num)));
		}

		public static float SpeedForSize(float size)
		{
			return Math.Max(0.58f, Math.Min(1.2f, 1.2f - (size - 0.5f) * 0.248f));
		}
	}
	[Serializable]
	public sealed class BossRecord
	{
		public string name;

		public int kind;

		public bool revengeDefeated;
	}
	[Serializable]
	public sealed class WorldData
	{
		public string name;

		public int completedWaves;

		public int completedBosses;

		public int catches;

		public int nextBaseGoal;

		public List<BossRecord> bosses = new List<BossRecord>();
	}
	[Serializable]
	public sealed class SaveData
	{
		public int version = 2;

		public List<WorldData> worlds = new List<WorldData>();
	}
	[Serializable]
	public sealed class EventState
	{
		public int protocol = 2;

		public int wave;

		public int completed;

		public int total;

		public int alive;

		public int normalAlive;

		public int bruteAlive;

		public int leaperAlive;

		public int round = 1;

		public int totalRounds = 1;

		public int roundSignal;

		public int thunderSignal;

		public int bossHp;

		public int bossMaxHp;

		public int bossKind;

		public int bossNumber;

		public bool active;

		public bool intro;

		public bool boss;

		public bool revenge;

		public bool eventsEnabled = true;

		public bool musicEnabled = true;

		public bool lightningEnabled = true;

		public float musicVolume = 0.7f;

		public string bossName = "";
	}
	public struct StateMessage : IBroadcast
	{
		public string json;
	}
	public static class Codec
	{
		private static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
		{
			TypeNameHandling = (TypeNameHandling)0,
			MetadataPropertyHandling = (MetadataPropertyHandling)2
		};

		public static string ToJson<T>(T value)
		{
			return JsonConvert.SerializeObject((object)value, Settings);
		}

		public static T FromJson<T>(string value)
		{
			return JsonConvert.DeserializeObject<T>(value, Settings);
		}
	}
	public static class Assets
	{
		public static readonly Dictionary<FishmanKind, Mesh> Meshes = new Dictionary<FishmanKind, Mesh>();

		public static readonly Dictionary<FishmanKind, Texture2D> Textures = new Dictionary<FishmanKind, Texture2D>();

		public static readonly Dictionary<FishmanKind, Texture2D> Icons = new Dictionary<FishmanKind, Texture2D>();

		private static readonly string[] Keys = new string[7] { "fishman", "brute", "leaper", "deepjaw", "barnacle", "tidecaller", "puffertitan" };

		private static byte[] Read(string name)
		{
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name);
			if (stream == null)
			{
				throw new Exception("Missing embedded resource " + name);
			}
			byte[] array = new byte[stream.Length];
			int num;
			for (int i = 0; i < array.Length; i += num)
			{
				if ((num = stream.Read(array, i, array.Length - i)) <= 0)
				{
					break;
				}
			}
			return array;
		}

		private static Mesh ReadMesh(byte[] data, string name)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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_00b6: 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_00e6: 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_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: 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_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Expected O, but got Unknown
			using MemoryStream input = new MemoryStream(data);
			using BinaryReader binaryReader = new BinaryReader(input);
			int num = binaryReader.ReadInt32();
			int num2 = binaryReader.ReadInt32();
			Vector3[] array = (Vector3[])(object)new Vector3[num];
			Vector3[] array2 = (Vector3[])(object)new Vector3[num];
			Vector2[] array3 = (Vector2[])(object)new Vector2[num];
			int[] array4 = new int[num2];
			for (int i = 0; i < num; i++)
			{
				array[i] = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
			}
			for (int j = 0; j < num; j++)
			{
				array2[j] = new Vector3(binaryReader.ReadSingle(), binaryReader.ReadSingle(), binaryReader.ReadSingle());
			}
			for (int k = 0; k < num; k++)
			{
				array3[k] = new Vector2(binaryReader.ReadSingle(), binaryReader.ReadSingle());
			}
			for (int l = 0; l < num2; l++)
			{
				array4[l] = (int)binaryReader.ReadUInt32();
			}
			Mesh val = new Mesh
			{
				name = name,
				indexFormat = (IndexFormat)1,
				vertices = array,
				normals = array2,
				uv = array3,
				triangles = array4
			};
			val.RecalculateBounds();
			val.UploadMeshData(false);
			return val;
		}

		public static void Load()
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Expected O, but got Unknown
			if (Meshes.Count != 0)
			{
				return;
			}
			for (int i = 0; i < Keys.Length; i++)
			{
				FishmanKind key = (FishmanKind)i;
				string text = Keys[i];
				Meshes[key] = ReadMesh(Read("FishmenInvasion." + text + ".meshbin"), key.ToString() + " model");
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
				{
					name = key.ToString() + " texture",
					wrapMode = (TextureWrapMode)0
				};
				if (!ImageConversion.LoadImage(val, Read("FishmenInvasion." + text + "-texture.jpg"), false))
				{
					throw new Exception(key.ToString() + " texture could not be decoded.");
				}
				Textures[key] = val;
				Texture2D val2 = new Texture2D(2, 2, (TextureFormat)4, false)
				{
					name = key.ToString() + " HUD icon",
					wrapMode = (TextureWrapMode)1
				};
				if (!ImageConversion.LoadImage(val2, Read("FishmenInvasion." + text + "-icon.png"), false))
				{
					throw new Exception(key.ToString() + " icon could not be decoded.");
				}
				Icons[key] = val2;
			}
		}
	}
	public static class FishmanItem
	{
		public static readonly Dictionary<FishmanKind, AttackingFish> Prefabs = new Dictionary<FishmanKind, AttackingFish>();

		private static GameObject storage;

		private static readonly string[] Names = new string[7] { "Fishman", "Fishman Brute", "Fishman Leaper", "Deepjaw", "Barnacle Colossus", "Tidecaller", "Puffer Titan" };

		public static bool Ready => Prefabs.Count == 7;

		public static bool Is(Item item)
		{
			if ((Object)(object)item != (Object)null && Array.IndexOf(Rules.ItemIds, item.ID) >= 0)
			{
				return (Object)(object)((Component)item).GetComponent<FishmanVisual>() != (Object)null;
			}
			return false;
		}

		public static FishmanKind Kind(Item item)
		{
			int num = (((Object)(object)item == (Object)null) ? (-1) : Array.IndexOf(Rules.ItemIds, item.ID));
			if (num >= 0)
			{
				return (FishmanKind)num;
			}
			return FishmanKind.Fishman;
		}

		public static string Name(Item item)
		{
			return Names[(int)Kind(item)];
		}

		public static void Register()
		{
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Expected O, but got Unknown
			if (Ready || (Object)(object)InstanceFinder.NetworkManager == (Object)null)
			{
				return;
			}
			Dictionary<byte, Item> dictionary = (Dictionary<byte, Item>)AccessTools.Field(typeof(GameInfo), "_idToSpawnable").GetValue(null);
			if (dictionary.Count == 0)
			{
				return;
			}
			Assets.Load();
			for (int i = 0; i < Rules.ItemIds.Length; i++)
			{
				if (dictionary.ContainsKey(Rules.ItemIds[i]))
				{
					throw new Exception("Fishmen Invasion item ID " + Rules.ItemIds[i] + " is already in use.");
				}
			}
			List<AttackingFish> source = (from x in dictionary.Values.OfType<AttackingFish>()
				where (Object)(object)x != (Object)null && (Object)(object)((Component)x).GetComponent<NetworkObject>() != (Object)null
				select x).Distinct().ToList();
			AttackingFish val = ((IEnumerable<AttackingFish>)source).FirstOrDefault((Func<AttackingFish, bool>)((AttackingFish x) => (int)((Creature)x).BossType != 0 && (((Object)x).name ?? "").IndexOf("tuna", StringComparison.OrdinalIgnoreCase) >= 0)) ?? ((IEnumerable<AttackingFish>)source).FirstOrDefault((Func<AttackingFish, bool>)((AttackingFish x) => (((Object)x).name ?? "").IndexOf("tuna", StringComparison.OrdinalIgnoreCase) >= 0)) ?? ((IEnumerable<AttackingFish>)source).FirstOrDefault((Func<AttackingFish, bool>)((AttackingFish x) => (int)((Creature)x).BossType > 0)) ?? source.FirstOrDefault();
			if ((Object)(object)val == (Object)null)
			{
				throw new Exception("No vanilla attacking-fish prefab was found.");
			}
			List<AttackingFish> list = source.Where((AttackingFish x) => (int)((Creature)x).BossType > 0).ToList();
			if (list.Count == 0)
			{
				list.Add(val);
			}
			storage = new GameObject("Fishmen Invasion prefab storage");
			storage.SetActive(false);
			Object.DontDestroyOnLoad((Object)(object)storage);
			for (int num = 0; num < 7; num++)
			{
				RegisterOne((FishmanKind)num, (num < 3) ? val : list[Random.Range(0, list.Count)], dictionary);
			}
			Plugin.Log("Registered seven Fishmen variants using Tuna/boss attacking-fish behavior.");
		}

		private static void RegisterOne(FishmanKind kind, AttackingFish source, Dictionary<byte, Item> ids)
		{
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_041b: Unknown result type (might be due to invalid IL or missing references)
			int num = (int)kind;
			AttackingFish val = Object.Instantiate<AttackingFish>(source, storage.transform);
			((Object)val).name = Names[num];
			AccessTools.Field(typeof(Item), "_id").SetValue(val, Rules.ItemIds[num]);
			AccessTools.Field(typeof(Item), "_cost").SetValue(val, 0);
			AccessTools.Field(typeof(Item), "_worth").SetValue(val, Rules.Worth(1, kind));
			AccessTools.Field(typeof(Item), "_ignoredBySeagulls").SetValue(val, false);
			AccessTools.Field(typeof(Item), "_ignoredByMoneyNPC").SetValue(val, false);
			AccessTools.Field(typeof(Item), "_heldPos").SetValue(val, (object)new Vector3(0.18f, -0.45f, 0.84f));
			AccessTools.Field(typeof(Item), "_heldRot").SetValue(val, (object)new Vector3(-8f, 165f, 82f));
			AccessTools.Field(typeof(Creature), "_maxHp").SetValue(val, Rules.Health(1, 1f, kind));
			AccessTools.Field(typeof(Creature), "_excludeFromJournal").SetValue(val, true);
			AccessTools.Field(typeof(Creature), "_skipRandomizedWeight").SetValue(val, true);
			AccessTools.Field(typeof(Creature), "_ignoreDeathByWater").SetValue(val, true);
			AccessTools.Field(typeof(Creature), "_bossType").SetValue(val, (object)(BossType)0);
			AccessTools.Field(typeof(AttackingFish), "_onHitDamage").SetValue(val, Rules.Attack(1, 1f, kind));
			AccessTools.Field(typeof(AttackingFish), "_setNewTargetDelay").SetValue(val, 0.35f);
			AccessTools.Field(typeof(AttackingFish), "_attackInAir").SetValue(val, true);
			AccessTools.Field(typeof(AttackingFish), "_towardsPlayerForce").SetValue(val, (kind == FishmanKind.Brute) ? 10f : 12f);
			AccessTools.Field(typeof(AttackingFish), "_airTowardsPlayerForce").SetValue(val, (kind == FishmanKind.Leaper) ? 12f : 7f);
			AccessTools.Field(typeof(Fish), "_jumpForce").SetValue(val, (kind == FishmanKind.Leaper) ? 14f : 8f);
			AccessTools.Field(typeof(Fish), "_jumpOnWater").SetValue(val, true);
			Renderer[] componentsInChildren = ((Component)val).GetComponentsInChildren<Renderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				componentsInChildren[i].enabled = false;
			}
			Collider[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				componentsInChildren2[i].enabled = false;
			}
			FishmanVisual fishmanVisual = ((Component)val).gameObject.AddComponent<FishmanVisual>();
			fishmanVisual.Build(kind);
			((Component)val).gameObject.AddComponent<FishmanBrain>().kind = kind;
			if (kind >= FishmanKind.Deepjaw)
			{
				((Component)val).gameObject.AddComponent<FishmanBossDeathEffect>();
			}
			AccessTools.Field(typeof(Item), "_mesh").SetValue(val, Assets.Meshes[kind]);
			AccessTools.Field(typeof(Item), "_inventoryMeshScale").SetValue(val, 0.58f);
			AccessTools.Field(typeof(Item), "_inventoryMeshPos").SetValue(val, (object)new Vector3(0f, -0.05f, 0f));
			AccessTools.Field(typeof(Item), "_inventoryMeshRot").SetValue(val, (object)new Vector3(-8f, 155f, 0f));
			AccessTools.Field(typeof(Item), "_renderers").SetValue(val, (from r in ((Component)fishmanVisual).GetComponentsInChildren<Renderer>(true)
				where r.enabled
				select r).ToList());
			NetworkObject component = ((Component)val).GetComponent<NetworkObject>();
			component.SetIsSpawnable(true);
			PrefabObjects prefabObjects = InstanceFinder.NetworkManager.GetPrefabObjects<SinglePrefabObjects>(Rules.PrefabCollections[num], true);
			if (prefabObjects.GetObjectCount() != 0)
			{
				throw new Exception("Fishmen network prefab collection " + Rules.PrefabCollections[num] + " is occupied.");
			}
			prefabObjects.AddObject(component, false, true);
			ids.Add(Rules.ItemIds[num], (Item)(object)val);
			((Dictionary<byte, Item>)AccessTools.Field(typeof(GameInfo), "_allItems").GetValue(null)).Add(Rules.ItemIds[num], (Item)(object)val);
			((Dictionary<string, Item>)AccessTools.Field(typeof(GameInfo), "_nameToSpawnable").GetValue(null)).Add("fishmeninvasion_" + kind.ToString().ToLowerInvariant(), (Item)(object)val);
			Prefabs[kind] = val;
		}

		public static AttackingFish Spawn(Vector3 position, int wave, float healthMulti, float attackMulti, FishmanKind kind, float size, bool revenge = false)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: 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_012e: 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_0134: 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_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: 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_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			if (!Prefabs.TryGetValue(kind, out var value))
			{
				return null;
			}
			AttackingFish val = Object.Instantiate<AttackingFish>(value, position, Quaternion.identity, Server.Instance.DynamicObjectsHolder);
			int num = Rules.Health(wave, healthMulti, kind);
			if (revenge)
			{
				num = (int)((float)num * 1.65f);
			}
			AccessTools.Field(typeof(Creature), "_maxHp").SetValue(val, num);
			((Creature)val)._hp.Value = num;
			AccessTools.Field(typeof(AttackingFish), "_onHitDamage").SetValue(val, (int)((float)Rules.Attack(wave, attackMulti, kind) * (revenge ? 1.35f : 1f)));
			AccessTools.Field(typeof(Item), "_worth").SetValue(val, Rules.Worth(wave, kind));
			((Item)val)._syncedRandomWeight.Value = size;
			FishmanBrain component = ((Component)val).GetComponent<FishmanBrain>();
			if ((Object)(object)component != (Object)null)
			{
				component.Configure(kind, size, revenge, position);
			}
			InstanceFinder.ServerManager.Spawn(((Component)val).gameObject, (NetworkConnection)null, default(Scene));
			((Item)val).RigidbodySync.ServerSetSyncedSimulator(((NetworkBehaviour)Server.Instance).Owner);
			Vector3 val2 = Island.IslandPos - position;
			val2.y = 0f;
			if (((Vector3)(ref val2)).sqrMagnitude > 0.01f)
			{
				((Item)val).Rig.linearVelocity = ((Vector3)(ref val2)).normalized * 10f + Vector3.up * 7f;
			}
			return val;
		}
	}
	public sealed class FishmanVisual : MonoBehaviour
	{
		private Transform model;

		private Renderer modelRenderer;

		private MaterialPropertyBlock block;

		private Rigidbody rig;

		private Creature creature;

		private Vector3 facing = Vector3.forward;

		private FishmanKind kind;

		private float visualSpin;

		public void Build(FishmanKind value)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			kind = value;
			GameObject val = new GameObject("FishmanModel");
			val.transform.SetParent(((Component)this).transform, false);
			val.AddComponent<MeshFilter>().sharedMesh = Assets.Meshes[kind];
			modelRenderer = (Renderer)(object)val.AddComponent<MeshRenderer>();
			Material val2 = new Material(Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Standard"));
			val2.mainTexture = (Texture)(object)Assets.Textures[kind];
			if (val2.HasProperty("_BaseMap"))
			{
				val2.SetTexture("_BaseMap", (Texture)(object)Assets.Textures[kind]);
			}
			modelRenderer.sharedMaterial = val2;
			BuildHitboxes(val, Assets.Meshes[kind]);
			model = val.transform;
		}

		private static void BuildHitboxes(GameObject root, Mesh mesh)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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)
			//IL_008f: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: 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_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: 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_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: 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_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			Vector3[] vertices = mesh.vertices;
			Bounds bounds = mesh.bounds;
			List<Vector3>[] array = new List<Vector3>[24];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = new List<Vector3>();
			}
			Vector3 min = ((Bounds)(ref bounds)).min;
			Vector3 size = ((Bounds)(ref bounds)).size;
			Vector3[] array2 = vertices;
			foreach (Vector3 val in array2)
			{
				int num = Mathf.Clamp((int)((val.x - min.x) / Mathf.Max(0.001f, size.x) * 3f), 0, 2);
				int num2 = Mathf.Clamp((int)((val.y - min.y) / Mathf.Max(0.001f, size.y) * 4f), 0, 3);
				int num3 = Mathf.Clamp((int)((val.z - min.z) / Mathf.Max(0.001f, size.z) * 2f), 0, 1);
				array[num + 3 * (num2 + 4 * num3)].Add(val);
			}
			List<Vector3>[] array3 = array;
			Bounds val2 = default(Bounds);
			foreach (List<Vector3> list in array3)
			{
				if (list.Count >= 8)
				{
					((Bounds)(ref val2))..ctor(list[0], Vector3.zero);
					for (int k = 1; k < list.Count; k++)
					{
						((Bounds)(ref val2)).Encapsulate(list[k]);
					}
					Vector3 val3 = ((Bounds)(ref val2)).size * 0.01f;
					Vector3 val4 = Vector3.Max(((Bounds)(ref bounds)).min, ((Bounds)(ref val2)).min - val3);
					Vector3 val5 = Vector3.Min(((Bounds)(ref bounds)).max, ((Bounds)(ref val2)).max + val3);
					Vector3 val6 = val5 - val4;
					if (!(val6.x < 0.03f) && !(val6.y < 0.03f) && !(val6.z < 0.03f))
					{
						BoxCollider obj = root.AddComponent<BoxCollider>();
						obj.center = (val4 + val5) * 0.5f;
						obj.size = val6;
					}
				}
			}
		}

		private void Awake()
		{
			rig = ((Component)this).GetComponent<Rigidbody>();
			creature = ((Component)this).GetComponent<Creature>();
			if ((Object)(object)model == (Object)null)
			{
				model = ((Component)this).transform.Find("FishmanModel");
				if ((Object)(object)model != (Object)null)
				{
					modelRenderer = ((Component)model).GetComponent<Renderer>();
				}
			}
			kind = FishmanItem.Kind((Item)(object)creature);
		}

		private void LateUpdate()
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: 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_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_025d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: 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_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Expected O, but got Unknown
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: 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_01b4: 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_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)model == (Object)null || (Object)(object)modelRenderer == (Object)null)
			{
				return;
			}
			if ((Object)(object)creature == (Object)null)
			{
				creature = ((Component)this).GetComponent<Creature>();
			}
			if ((Object)(object)rig == (Object)null)
			{
				rig = ((Component)this).GetComponent<Rigidbody>();
			}
			float num = 1.035f * Mathf.Clamp(((Object)(object)creature == (Object)null) ? 1f : ((Item)creature).RandomizedWeight, 0.5f, 3f);
			model.localScale = Vector3.one * num;
			if ((Object)(object)creature != (Object)null && !creature.IsDead && (Object)(object)((Item)creature).SyncedHolder == (Object)null)
			{
				Vector3 val = (((Object)(object)rig == (Object)null) ? Vector3.zero : rig.linearVelocity);
				Vector3 val2 = val;
				val2.y = 0f;
				if (((Vector3)(ref val2)).sqrMagnitude > 0.2f)
				{
					facing = Vector3.Slerp(facing, ((Vector3)(ref val2)).normalized, Time.deltaTime * 9f);
				}
				model.position = ((Component)this).transform.position + Vector3.up * (0.78f * num);
				Quaternion val3 = Quaternion.LookRotation(facing, Vector3.up);
				if (((Vector3)(ref val)).sqrMagnitude > 3f)
				{
					if (kind == FishmanKind.Brute)
					{
						visualSpin += Time.deltaTime * 430f;
						val3 *= Quaternion.AngleAxis(visualSpin, Vector3.forward);
					}
					else if (kind == FishmanKind.Fishman)
					{
						visualSpin += Time.deltaTime * 340f;
						val3 *= Quaternion.AngleAxis(visualSpin, Vector3.up);
					}
				}
				model.rotation = val3;
			}
			else
			{
				model.localPosition = Vector3.zero;
				model.localRotation = Quaternion.identity;
			}
			float num2 = (((Object)(object)creature == (Object)null) ? 0f : ((Item)creature).Cookness);
			Color val4 = ((num2 < 1f) ? Color.Lerp(Color.white, new Color(0.42f, 0.2f, 0.08f), Mathf.Clamp01(num2)) : Color.Lerp(new Color(0.42f, 0.2f, 0.08f), new Color(0.025f, 0.018f, 0.012f), Mathf.Clamp01(num2 - 1f)));
			if (block == null)
			{
				block = new MaterialPropertyBlock();
			}
			modelRenderer.GetPropertyBlock(block);
			block.SetColor("_BaseColor", val4);
			block.SetColor("_Color", val4);
			block.SetFloat("_Cookness", num2);
			modelRenderer.SetPropertyBlock(block);
		}
	}
	public sealed class FishmanBrain : MonoBehaviour
	{
		public FishmanKind kind;

		public float size = 1f;

		public bool revenge;

		private AttackingFish creature;

		private Rigidbody rig;

		private Vector3 last;

		private Vector3 spawnPoint;

		private float nextAttack;

		private float nextCheck;

		private float stillFor;

		private float noProgressFor;

		private float lastTargetDistance = float.MaxValue;

		private float deadSince = -1f;

		private void Awake()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_002c: 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)
			creature = ((Component)this).GetComponent<AttackingFish>();
			rig = ((Component)this).GetComponent<Rigidbody>();
			last = (spawnPoint = ((Component)this).transform.position);
			if ((Object)(object)rig != (Object)null)
			{
				rig.maxAngularVelocity = 18f;
			}
		}

		public void Configure(FishmanKind value, float scale, bool isRevenge, Vector3 spawn)
		{
			//IL_0016: 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_001e: 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)
			kind = value;
			size = scale;
			revenge = isRevenge;
			spawnPoint = spawn;
			last = spawn;
			nextAttack = Time.time + 0.15f;
		}

		private void FixedUpdate()
		{
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_038d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_045d: Unknown result type (might be due to invalid IL or missing references)
			//IL_047c: Unknown result type (might be due to invalid IL or missing references)
			//IL_049c: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0334: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0322: Unknown result type (might be due to invalid IL or missing references)
			if (!InstanceFinder.IsServerStarted || (Object)(object)creature == (Object)null || (Object)(object)rig == (Object)null)
			{
				return;
			}
			if (((Creature)creature)._hp.Value <= 0)
			{
				if (deadSince < 0f)
				{
					deadSince = Time.time;
				}
				if (kind >= FishmanKind.Deepjaw && Time.time - deadSince > 1.15f)
				{
					if (!((NetworkBehaviour)creature).IsDeinitializing)
					{
						((Item)creature).DestroyItem((byte)0, byte.MaxValue);
					}
					return;
				}
				if (Time.time - deadSince > 4f && !((Item)creature).IsInInventory && !((Item)creature).HasPlayerHolder)
				{
					Vector3 linearVelocity = rig.linearVelocity;
					if (((Vector3)(ref linearVelocity)).sqrMagnitude < 0.25f)
					{
						rig.Sleep();
					}
				}
				if (Time.time - deadSince > 180f && !((Item)creature).IsInInventory && !((Item)creature).HasPlayerHolder)
				{
					((Item)creature).DestroyItem((byte)0, byte.MaxValue);
				}
				return;
			}
			Player nearestAlivePlayer = PlayerManager.GetNearestAlivePlayer(rig.worldCenterOfMass);
			if (Time.time >= nextAttack && (Object)(object)nearestAlivePlayer != (Object)null)
			{
				float num = ((kind == FishmanKind.Leaper) ? Random.Range(0.28f, 0.48f) : ((kind == FishmanKind.Brute) ? Random.Range(0.4f, 0.65f) : Random.Range(0.42f, 0.68f)));
				nextAttack = Time.time + num;
				Vector3 val = nearestAlivePlayer.Transform.position - rig.worldCenterOfMass;
				val.y = 0f;
				if (((Vector3)(ref val)).sqrMagnitude > 0.1f)
				{
					((Vector3)(ref val)).Normalize();
					float num2 = ((kind == FishmanKind.Brute) ? 0.82f : ((kind == FishmanKind.Leaper) ? 1.15f : 1f));
					float num3 = Rules.SpeedForSize(size) * num2 * (revenge ? 1.2f : 1f);
					float num4 = ((kind == FishmanKind.Brute) ? 11f : ((kind == FishmanKind.Leaper) ? 10.5f : 10f));
					float num5 = ((kind == FishmanKind.Leaper) ? 14f : ((kind == FishmanKind.Brute) ? 4.5f : 8f));
					rig.linearVelocity = new Vector3(val.x * num4 * num3, Mathf.Max(rig.linearVelocity.y, num5 * num3), val.z * num4 * num3);
					if (kind == FishmanKind.Brute)
					{
						rig.angularVelocity = Vector3.Cross(Vector3.up, val) * 10f;
					}
					else if (kind == FishmanKind.Leaper)
					{
						rig.angularVelocity = new Vector3(Random.Range(-3f, 3f), Random.Range(-5f, 5f), Random.Range(-3f, 3f));
					}
					else
					{
						rig.angularVelocity = new Vector3(val.z * 7f, Random.Range(-8f, 8f), (0f - val.x) * 7f);
					}
				}
			}
			if (Time.time >= nextCheck)
			{
				nextCheck = Time.time + 1f;
				float num6 = Vector3.Distance(((Component)this).transform.position, last);
				stillFor = ((num6 < 0.3f) ? (stillFor + 1f) : 0f);
				last = ((Component)this).transform.position;
				float num7 = (((Object)(object)nearestAlivePlayer == (Object)null) ? 0f : Vector3.Distance(rig.worldCenterOfMass, nearestAlivePlayer.Transform.position));
				if ((Object)(object)nearestAlivePlayer != (Object)null && num7 > 4f)
				{
					noProgressFor = ((num7 > lastTargetDistance - 0.45f) ? (noProgressFor + 1f) : 0f);
					lastTargetDistance = num7;
				}
				else
				{
					noProgressFor = 0f;
					lastTargetDistance = num7;
				}
				bool flag = ((Component)this).transform.position.y < WaterManager.WaterHeight - 7f;
				bool flag2 = Physics.CheckSphere(rig.worldCenterOfMass, 0.18f * Mathf.Clamp(size, 0.5f, 1.5f), LayerMask.op_Implicit(GameInfo.LevelLayer), (QueryTriggerInteraction)1);
				if (flag || flag2 || stillFor >= 4f || noProgressFor >= 6f)
				{
					Recover(flag ? "under map" : (flag2 ? "inside terrain" : "unable to reach a player"));
				}
			}
		}

		private void Recover(string reason)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: 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_0022: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: 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_007d: 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_008c: 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)
			//IL_00a1: 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)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = (spawnPoint = (((Object)(object)Plugin.Instance == (Object)null) ? spawnPoint : Plugin.Instance.GetEnemySpawnPosition()));
			((Component)this).transform.position = val;
			rig.position = val;
			Vector3 val2 = Island.IslandPos - val;
			val2.y = 0f;
			if (((Vector3)(ref val2)).sqrMagnitude < 0.01f)
			{
				val2 = Vector3.forward;
			}
			rig.linearVelocity = ((Vector3)(ref val2)).normalized * 10f + Vector3.up * 7f;
			rig.angularVelocity = Vector3.zero;
			stillFor = (noProgressFor = 0f);
			lastTargetDistance = float.MaxValue;
			last = val;
			Plugin.Log("Recovered a stuck " + kind.ToString() + " (" + reason + ") at a clear waterline spawn.");
		}
	}
	public sealed class FishmanBossDeathEffect : MonoBehaviour
	{
		private Creature creature;

		private Rigidbody rig;

		private bool played;

		private void Awake()
		{
			creature = ((Component)this).GetComponent<Creature>();
			rig = ((Component)this).GetComponent<Rigidbody>();
		}

		private void Update()
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (played || (Object)(object)creature == (Object)null || creature._hp.Value > 0)
			{
				return;
			}
			played = true;
			BossManager.BossExplosion(((Object)(object)rig == (Object)null) ? ((Component)this).transform.position : rig.worldCenterOfMass, false);
			Transform val = ((Component)this).transform.Find("FishmanModel");
			if ((Object)(object)val != (Object)null)
			{
				Renderer[] componentsInChildren = ((Component)val).GetComponentsInChildren<Renderer>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					componentsInChildren[i].enabled = false;
				}
				Collider[] componentsInChildren2 = ((Component)val).GetComponentsInChildren<Collider>(true);
				for (int i = 0; i < componentsInChildren2.Length; i++)
				{
					componentsInChildren2[i].enabled = false;
				}
			}
			Plugin.Log("Played the vanilla BossDeath explosion for " + FishmanItem.Name((Item)(object)creature) + ".");
		}
	}
	public static class EventAudio
	{
		private static AudioClip music;

		private static AudioClip bossMusic;

		private static AudioClip thunder;

		private static AudioSource musicSource;

		private static AudioSource fxSource;

		private static bool loading;

		private static bool eventActive;

		private static bool bossActive;

		private static bool pendingThunder;

		public static IEnumerator Load()
		{
			if (!loading && !((Object)(object)music != (Object)null))
			{
				loading = true;
				string folder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
				yield return LoadOne(Find(folder, "fishmen-event-music.mp3", "FishmenInvasionEventMusic.mp3"), delegate(AudioClip c)
				{
					music = c;
				});
				yield return LoadOne(Find(folder, "fishmen-boss-music.mp3", "FishmanInvasionBossFightMusic.mp3"), delegate(AudioClip c)
				{
					bossMusic = c;
				});
				yield return LoadOne(Find(folder, "fishmen-event-start.mp3", "Thunderclapeventstartsound.mp3"), delegate(AudioClip c)
				{
					thunder = c;
				});
				loading = false;
			}
		}

		private static string Find(string folder, string packaged, string original)
		{
			string text = Path.Combine(folder, packaged);
			if (File.Exists(text))
			{
				return text;
			}
			return Path.Combine(folder, "assets", original);
		}

		private static IEnumerator LoadOne(string path, Action<AudioClip> assign)
		{
			if (!File.Exists(path))
			{
				Plugin.Log("Missing audio file: " + path);
				yield break;
			}
			UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(new Uri(path).AbsoluteUri, (AudioType)13);
			try
			{
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					Plugin.Log("Audio load failed: " + request.error);
				}
				else
				{
					assign(DownloadHandlerAudioClip.GetContent(request));
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		private static void Ensure()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)musicSource != (Object)null))
			{
				GameObject val = new GameObject("Fishmen Invasion audio");
				Object.DontDestroyOnLoad((Object)val);
				musicSource = val.AddComponent<AudioSource>();
				musicSource.loop = true;
				musicSource.playOnAwake = false;
				musicSource.spatialBlend = 0f;
				fxSource = val.AddComponent<AudioSource>();
				fxSource.playOnAwake = false;
				fxSource.spatialBlend = 0f;
			}
		}

		public static void Begin(bool boss, bool playThunder)
		{
			Ensure();
			eventActive = true;
			SetBoss(boss);
			pendingThunder = playThunder && (Object)(object)thunder == (Object)null;
			if (playThunder && (Object)(object)thunder != (Object)null)
			{
				fxSource.PlayOneShot(thunder, 0.58f);
			}
			PlayCorrectClip();
		}

		public static void SetBoss(bool boss)
		{
			if (bossActive != boss)
			{
				bossActive = boss;
				if (eventActive)
				{
					PlayCorrectClip();
				}
			}
		}

		private static void PlayCorrectClip()
		{
			Ensure();
			AudioClip val = ((bossActive && (Object)(object)bossMusic != (Object)null) ? bossMusic : music);
			if ((Object)(object)val != (Object)null && (Object)(object)musicSource.clip != (Object)(object)val)
			{
				musicSource.Stop();
				musicSource.clip = val;
				musicSource.Play();
			}
			else if ((Object)(object)val != (Object)null && !musicSource.isPlaying)
			{
				musicSource.clip = val;
				musicSource.Play();
			}
		}

		public static void End()
		{
			eventActive = false;
		}

		public static void Tick(bool allowMusic, float volume)
		{
			Ensure();
			if (pendingThunder && (Object)(object)thunder != (Object)null)
			{
				pendingThunder = false;
				fxSource.PlayOneShot(thunder, 0.58f);
			}
			float num = ((eventActive && allowMusic) ? (0.42f * Mathf.Clamp01(volume)) : 0f);
			musicSource.volume = Mathf.MoveTowards(musicSource.volume, num, Time.unscaledDeltaTime * ((num > musicSource.volume) ? 0.18f : 0.11f));
			if (!eventActive && musicSource.volume <= 0.001f && musicSource.isPlaying)
			{
				musicSource.Stop();
			}
			else if (eventActive && allowMusic)
			{
				PlayCorrectClip();
			}
		}

		public static void Destroy()
		{
			if ((Object)(object)musicSource != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)musicSource).gameObject);
			}
			musicSource = null;
			fxSource = null;
			music = null;
			bossMusic = null;
			thunder = null;
		}
	}
	[BepInPlugin("ponyfisher.howtofish.fishmeninvasion", "Fishmen Invasion Mod", "0.2.6")]
	[DefaultExecutionOrder(29600)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "ponyfisher.howtofish.fishmeninvasion";

		public static Plugin Instance;

		public EventState State = new EventState();

		public string Status = "Fishmen invasions are waiting on internal Islands 2 through 5.";

		private ConfigEntry<bool> eventsEnabled;

		private ConfigEntry<bool> lightningEnabled;

		private ConfigEntry<bool> musicEnabled;

		private ConfigEntry<float> musicVolume;

		private ConfigEntry<float> amountMultiplier;

		private ConfigEntry<float> healthMultiplier;

		private ConfigEntry<float> attackMultiplier;

		private ConfigEntry<float> frequencyMultiplier;

		private SaveData save = new SaveData();

		private WorldData world;

		private string savePath;

		private NetworkManager manager;

		private Harmony harmony;

		private readonly List<AttackingFish> active = new List<AttackingFish>();

		private readonly List<AttackingFish> spawned = new List<AttackingFish>();

		private AttackingFish boss;

		private float introEnds;

		private float nextBroadcast;

		private float nextSave;

		private float flashStarted = -10f;

		private float nextBossThunder;

		private float nextMinionFill;

		private float nextWaveSpawn;

		private int pendingRoundEnemies;

		private bool registrationFailed;

		private bool wipeEnding;

		private string activeBossBaseName = "";

		private BossRecord activeBossRecord;

		private static readonly string[] BossNames = new string[20]
		{
			"Captain Gillbeard", "Admiral Chum Bucket", "Baron von Bubbles", "Scurvy McScale", "Fin Diesel", "The Codfather", "Salmon Ella", "Long John Silverfish", "Davy Jones' Tuna", "Sir Bites-a-Lot",
			"Mackerelmore", "Captain Flounderpants", "Blackbeard Bass", "Grouper Trooper", "Sushi Sam", "The Briny Bully", "Marlin Monroe", "Anchovy Annie", "Reef Richards", "Pike Tyson"
		};

		private FishmanKind activeBossKind = FishmanKind.Deepjaw;

		private bool optionsOpen;

		private bool draftsLoaded;

		private float amountDraft = 1f;

		private float healthDraft = 1f;

		private float attackDraft = 1f;

		private float frequencyDraft = 1f;

		private float musicVolumeDraft = 0.7f;

		private float nextGuiTimingLog;

		private GUIStyle titleStyle;

		private GUIStyle labelStyle;

		private GUIStyle headerStyle;

		private GUIStyle buttonStyle;

		private GUIStyle barStyle;

		private GUIStyle invasionButtonStyle;

		private Rect optionsRect;

		public bool EventsEnabled => eventsEnabled.Value;

		public bool LightningEnabled => lightningEnabled.Value;

		public bool MusicEnabled => musicEnabled.Value;

		public float MusicVolume => musicVolume.Value;

		public bool EffectiveMusicEnabled
		{
			get
			{
				if (!InstanceFinder.IsServerStarted)
				{
					return State.musicEnabled;
				}
				return MusicEnabled;
			}
		}

		public bool EffectiveLightningEnabled
		{
			get
			{
				if (!InstanceFinder.IsServerStarted)
				{
					return State.lightningEnabled;
				}
				return LightningEnabled;
			}
		}

		public float EffectiveMusicVolume
		{
			get
			{
				if (!InstanceFinder.IsServerStarted)
				{
					return State.musicVolume;
				}
				return MusicVolume;
			}
		}

		public float AmountMultiplier => amountMultiplier.Value;

		public float HealthMultiplier => healthMultiplier.Value;

		public float AttackMultiplier => attackMultiplier.Value;

		public float FrequencyMultiplier => frequencyMultiplier.Value;

		public float FlashAge => Time.unscaledTime - flashStarted;

		public bool OptionsOpen => optionsOpen;

		public static void Log(string value)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogInfo((object)value);
			}
		}

		private void Awake()
		{
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Expected O, but got Unknown
			Instance = this;
			((MonoBehaviour)this).useGUILayout = true;
			eventsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Event", "Enabled", true, "Host: enable Fishmen invasions.");
			lightningEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Effects", "LightningFlashes", true, "Host: show lightning flashes for the lobby.");
			musicEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Effects", "EventMusic", true, "Host: play invasion music for the lobby.");
			musicVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Effects", "EventMusicVolume", 0.7f, "Host event music volume (0-1). Default is 30% quieter.");
			amountMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Balance", "FishmenAmountMultiplier", 1f, "Host event amount multiplier (0.5-3.0).");
			healthMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Balance", "FishmenHealthMultiplier", 1f, "Host health multiplier (0.5-3.0).");
			attackMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Balance", "FishmenAttackMultiplier", 1f, "Host attack multiplier (0.25-2.0).");
			frequencyMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Balance", "InvasionFrequencyMultiplier", 1f, "Host frequency multiplier (0.5-3.0; higher triggers sooner).");
			ClampConfig();
			savePath = Path.Combine(Paths.ConfigPath, "FishmenInvasion-worlds.json");
			LoadSave();
			GenericWriter<StateMessage>.SetWrite((Action<Writer, StateMessage>)delegate(Writer w, StateMessage v)
			{
				w.WriteString(v.json);
			});
			GenericReader<StateMessage>.SetRead((Func<Reader, StateMessage>)((Reader r) => new StateMessage
			{
				json = r.ReadStringAllocated()
			}));
			harmony = new Harmony("ponyfisher.howtofish.fishmeninvasion");
			harmony.PatchAll(typeof(Plugin).Assembly);
			PatchDevIslandTrigger();
			((MonoBehaviour)this).StartCoroutine(EventAudio.Load());
			Log("Fishmen Invasion Mod 0.2.6 loaded. Eligible internal island indices: 2-5.");
		}

		private void LoadSave()
		{
			try
			{
				if (!File.Exists(savePath))
				{
					return;
				}
				save = Codec.FromJson<SaveData>(File.ReadAllText(savePath));
				if (save == null || save.worlds == null)
				{
					throw new Exception("Unrecognized save format.");
				}
				save.version = 2;
				foreach (WorldData world in save.worlds)
				{
					if (world.bosses == null)
					{
						world.bosses = new List<BossRecord>();
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)ex);
				save = new SaveData();
				Status = "The invasion save was unreadable; a fresh event record will be used.";
			}
		}

		private void PatchDevIslandTrigger()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			try
			{
				Type type = AccessTools.TypeByName("DevIslandUnlocked.Plugin");
				MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "QueueDevIslandFromTrigger", new Type[2]
				{
					typeof(Collider),
					typeof(string)
				}, (Type[])null));
				MethodInfo methodInfo2 = AccessTools.Method(typeof(Plugin), "AllowDevIslandTrigger", (Type[])null, (Type[])null);
				if (methodInfo != null && methodInfo2 != null)
				{
					harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log("Protected saved player spawns from the Dev Island travel trigger.");
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not install optional Dev Island spawn protection: " + ex.Message));
			}
		}

		private static bool AllowDevIslandTrigger(Collider __0)
		{
			if ((Object)(object)__0 == (Object)null)
			{
				return true;
			}
			bool num = string.Equals(((Object)__0).name, "MenuBoat", StringComparison.OrdinalIgnoreCase) || (Object)(object)((Component)__0).GetComponentInParent<MainMenuManager>() != (Object)null;
			Player componentInParent = ((Component)__0).GetComponentInParent<Player>();
			if (!num && (Object)(object)componentInParent == (Object)null)
			{
				return true;
			}
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogInfo((object)("Ignored Dev Island startup trigger from " + ((Object)__0).name + " so the saved profile island remains authoritative."));
			}
			return false;
		}

		private void ClampConfig()
		{
			amountMultiplier.Value = Mathf.Clamp(amountMultiplier.Value, 0.5f, 3f);
			healthMultiplier.Value = Mathf.Clamp(healthMultiplier.Value, 0.5f, 3f);
			attackMultiplier.Value = Mathf.Clamp(attackMultiplier.Value, 0.25f, 2f);
			frequencyMultiplier.Value = Mathf.Clamp(frequencyMultiplier.Value, 0.5f, 3f);
			musicVolume.Value = Mathf.Clamp01(musicVolume.Value);
		}

		public void SaveOptions(float amount, float health, float attack, float frequency, float volume)
		{
			amountMultiplier.Value = Mathf.Clamp(amount, 0.5f, 3f);
			healthMultiplier.Value = Mathf.Clamp(health, 0.5f, 3f);
			attackMultiplier.Value = Mathf.Clamp(attack, 0.25f, 2f);
			frequencyMultiplier.Value = Mathf.Clamp(frequency, 0.5f, 3f);
			musicVolume.Value = Mathf.Clamp01(volume);
			((BaseUnityPlugin)this).Config.Save();
			Status = (InstanceFinder.IsServerStarted ? "Host invasion settings saved." : "Local preferences saved. Gameplay scaling is controlled by the host.");
		}

		public void SetToggle(string key, bool value)
		{
			switch (key)
			{
			case "events":
				eventsEnabled.Value = value;
				break;
			case "lightning":
				lightningEnabled.Value = value;
				break;
			case "music":
				musicEnabled.Value = value;
				break;
			}
			((BaseUnityPlugin)this).Config.Save();
			if (key == "events" && !value && InstanceFinder.IsServerStarted && State.active)
			{
				CancelEvent(destroyBodies: false);
			}
		}

		private void RegisterNetwork()
		{
			if (!((Object)(object)manager == (Object)(object)InstanceFinder.NetworkManager))
			{
				if ((Object)(object)manager != (Object)null)
				{
					manager.ClientManager.UnregisterBroadcast<StateMessage>((Action<StateMessage, Channel>)ReceiveState);
				}
				manager = InstanceFinder.NetworkManager;
				if ((Object)(object)manager != (Object)null)
				{
					manager.ClientManager.RegisterBroadcast<StateMessage>((Action<StateMessage, Channel>)ReceiveState);
				}
			}
		}

		private void Update()
		{
			RegisterNetwork();
			if (!registrationFailed && !FishmanItem.Ready)
			{
				try
				{
					FishmanItem.Register();
				}
				catch (Exception ex)
				{
					registrationFailed = true;
					Status = "Fishman registration failed; check the BepInEx log.";
					((BaseUnityPlugin)this).Logger.LogError((object)ex);
				}
			}
			EventAudio.Tick(EffectiveMusicEnabled, EffectiveMusicVolume);
			if (InstanceFinder.IsServerStarted && SaveManager.CurServerSave != null)
			{
				LoadWorld(SaveManager.CurServerSave.Name);
				TickServer();
				if (Time.unscaledTime >= nextBroadcast)
				{
					nextBroadcast = Time.unscaledTime + 0.2f;
					Broadcast();
				}
				if (Time.unscaledTime >= nextSave)
				{
					nextSave = Time.unscaledTime + 8f;
					Persist();
				}
			}
			else if (!InstanceFinder.IsClientStarted)
			{
				world = null;
				if (State.active)
				{
					ApplyState(new EventState());
				}
			}
		}

		private void LoadWorld(string name)
		{
			if (world == null || !(world.name == name))
			{
				if (State.active)
				{
					CancelEvent(destroyBodies: false);
				}
				world = save.worlds.FirstOrDefault((WorldData w) => w.name == name);
				if (world == null)
				{
					world = new WorldData
					{
						name = name
					};
					save.worlds.Add(world);
				}
				if (world.bosses == null)
				{
					world.bosses = new List<BossRecord>();
				}
				world.catches = 0;
				world.nextBaseGoal = Random.Range(5, 21);
				State = new EventState
				{
					completed = world.completedWaves
				};
				active.Clear();
				spawned.Clear();
				boss = null;
				Status = "Invasion watch reset: defeat " + Rules.CatchTarget(world.nextBaseGoal, FrequencyMultiplier) + " fish to draw the next wave.";
				Persist();
				Broadcast();
			}
		}

		public void ServerCreatureKilled(Creature creature)
		{
			if (InstanceFinder.IsServerStarted && world != null && !State.active && EventsEnabled && !((Object)(object)creature == (Object)null) && !((Object)(object)((Item)creature).Fish == (Object)null) && !FishmanItem.Is((Item)(object)creature) && !((Object)(object)OnlineIslandManager.Instance == (Object)null) && Rules.EligibleIsland(OnlineIslandManager.CurIsland, OnlineIslandManager.MaxIslandUnlocked))
			{
				world.catches++;
				int num = Rules.CatchTarget(world.nextBaseGoal, FrequencyMultiplier);
				Status = "Invasion watch: " + world.catches + " / " + num + " fish defeated.";
				if (world.catches >= num)
				{
					StartEvent();
				}
			}
		}

		private void StartEvent(bool forceBoss = false)
		{
			if (world != null && FishmanItem.Ready)
			{
				int num = world.completedWaves + 1;
				world.catches = 0;
				world.nextBaseGoal = Random.Range(5, 21);
				active.Clear();
				spawned.Clear();
				boss = null;
				activeBossRecord = null;
				activeBossBaseName = "";
				bool flag = forceBoss || num % 10 == 0;
				EventState eventState = new EventState
				{
					active = true,
					intro = true,
					wave = num,
					completed = world.completedWaves,
					total = Rules.Amount(num, AmountMultiplier),
					alive = Rules.Amount(num, AmountMultiplier),
					round = 1,
					totalRounds = (flag ? 1 : Rules.Rounds(num)),
					roundSignal = State.roundSignal + 1,
					thunderSignal = State.thunderSignal + 1,
					boss = flag,
					bossNumber = (flag ? (world.completedBosses + 1) : 0)
				};
				if (flag)
				{
					PrepareBoss(eventState);
				}
				introEnds = Time.time + 3.2f;
				ApplyState(eventState);
				Status = (flag ? (eventState.bossName + " has invaded the island!") : "The sky cracks open. Fishmen are invading!");
				Broadcast();
			}
		}

		private void PrepareBoss(EventState next)
		{
			FishmanKind fishmanKind = (FishmanKind)Random.Range(3, 7);
			next.total = 10;
			next.alive = 10;
			float value = Random.value;
			List<BossRecord> list = world.bosses.Where((BossRecord x) => !x.revengeDefeated).ToList();
			if (value < 0.1f && list.Count > 0)
			{
				activeBossRecord = list[Random.Range(0, list.Count)];
				fishmanKind = (FishmanKind)activeBossRecord.kind;
				activeBossBaseName = activeBossRecord.name;
				next.bossName = "Revenge of " + activeBossBaseName;
				next.revenge = true;
			}
			else if (value < 0.19f && world.bosses.Count > 0)
			{
				BossRecord bossRecord = world.bosses[Random.Range(0, world.bosses.Count)];
				activeBossBaseName = UnusedBossName();
				next.bossName = "SPAWN OF " + bossRecord.name + ": " + activeBossBaseName;
			}
			else
			{
				activeBossBaseName = UnusedBossName();
				next.bossName = activeBossBaseName;
			}
			next.bossMaxHp = Rules.Health(next.wave, HealthMultiplier, fishmanKind) * (next.revenge ? 165 : 100) / 100;
			next.bossHp = next.bossMaxHp;
			next.bossKind = (int)fishmanKind;
			activeBossKind = fishmanKind;
		}

		private string UnusedBossName()
		{
			HashSet<string> used = new HashSet<string>(world.bosses.Select((BossRecord x) => x.name));
			List<string> list = BossNames.Where((string x) => !used.Contains(x)).ToList();
			if (list.Count <= 0)
			{
				return "Captain Scale-" + Random.Range(100, 999);
			}
			return list[Random.Range(0, list.Count)];
		}

		public void ForceStartEvent()
		{
			if (!InstanceFinder.IsServerStarted)
			{
				Status = "Only the host can force-start an invasion.";
			}
			else if (world == null || SaveManager.CurServerSave == null)
			{
				Status = "Load into a world before starting an invasion.";
			}
			else if (State.active)
			{
				Status = "A Fishmen invasion is already active.";
			}
			else if (!EventsEnabled)
			{
				Status = "Enable Fishmen invasions before force-starting a wave.";
			}
			else if ((Object)(object)OnlineIslandManager.Instance == (Object)null || !Rules.EligibleIsland(OnlineIslandManager.CurIsland, OnlineIslandManager.MaxIslandUnlocked))
			{
				Status = "Force Start Wave is available only on internal Islands 2 through 5 after Island 2 is unlocked.";
			}
			else
			{
				StartEvent();
			}
		}

		public Vector3 GetEnemySpawnPosition()
		{
			//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_002d: Unknown result type (might be due to invalid IL or missing references)
			//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_0097: 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_00a2: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Max(18f, Island.IslandSize * 0.95f + 10f);
			Vector3 result = Island.IslandPos + Vector3.forward * (num + 12f);
			result.y = WaterManager.WaterHeight + 0.6f;
			Vector3 val = default(Vector3);
			for (int i = 0; i < 28; i++)
			{
				float num2 = Random.value * (float)Math.PI * 2f;
				float num3 = Random.Range(0f, 10f) + (float)(i / 8) * 4f;
				((Vector3)(ref val))..ctor(Mathf.Cos(num2), 0f, Mathf.Sin(num2));
				Vector3 val2 = Island.IslandPos + val * (num + num3);
				val2.y = WaterManager.WaterHeight + 0.6f;
				if (!Physics.CheckSphere(val2, 2.8f, LayerMask.op_Implicit(GameInfo.LevelLayer), (QueryTriggerInteraction)1))
				{
					return val2;
				}
				result = val2;
			}
			return result;
		}

		private FishmanKind RandomMinionKind()
		{
			if (State.wave < 3)
			{
				return FishmanKind.Fishman;
			}
			float value = Random.value;
			float num = Mathf.Clamp01(0.18f + (float)(State.wave - 3) * 0.025f);
			if (value > num)
			{
				return FishmanKind.Fishman;
			}
			if (!(Random.value < 0.52f))
			{
				return FishmanKind.Leaper;
			}
			return FishmanKind.Brute;
		}

		private AttackingFish SpawnEnemy(FishmanKind kind, bool isBoss = false)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			float size = (isBoss ? Random.Range(2.5f, 3f) : Random.Range(0.5f, 1.5f));
			AttackingFish val = FishmanItem.Spawn(GetEnemySpawnPosition(), State.wave, HealthMultiplier, AttackMultiplier, kind, size, isBoss && State.revenge);
			if ((Object)(object)val != (Object)null)
			{
				spawned.Add(val);
				if (!isBoss)
				{
					active.Add(val);
				}
			}
			return val;
		}

		public void ForceStartBossEvent()
		{
			if (!InstanceFinder.IsServerStarted)
			{
				Status = "Only the host can force-start a boss event.";
			}
			else if (world == null || SaveManager.CurServerSave == null)
			{
				Status = "Load into a world before starting a boss event.";
			}
			else if (State.active)
			{
				Status = "An invasion is already active.";
			}
			else if (!EventsEnabled)
			{
				Status = "Enable Fishmen invasions before force-starting a boss.";
			}
			else if ((Object)(object)OnlineIslandManager.Instance == (Object)null || !Rules.EligibleIsland(OnlineIslandManager.CurIsland, OnlineIslandManager.MaxIslandUnlocked))
			{
				Status = "Force Random Boss is available only on internal Islands 2 through 5 after Island 2 is unlocked.";
			}
			else
			{
				StartEvent(forceBoss: true);
			}
		}

		private void SpawnCurrentRound()
		{
			if (State.active && State.intro)
			{
				active.Clear();
				if (State.boss)
				{
					boss = SpawnEnemy(activeBossKind, isBoss: true);
					FillBossMinions();
					nextBossThunder = Time.time + Random.Range(4f, 8f);
				}
				else
				{
					pendingRoundEnemies = State.total;
					nextWaveSpawn = Time.time;
				}
				State.intro = false;
				UpdateVariantCounts();
				Broadcast();
			}
		}

		private void FillRoundQueue()
		{
			if (!State.boss && pendingRoundEnemies > 0 && active.Count < 10 && !(Time.time < nextWaveSpawn))
			{
				if ((Object)(object)SpawnEnemy(RandomMinionKind()) != (Object)null)
				{
					pendingRoundEnemies--;
				}
				nextWaveSpawn = Time.time + 0.5f;
			}
		}

		private void FillBossMinions()
		{
			if (!State.boss || (Object)(object)boss == (Object)null || ((Creature)boss)._hp.Value <= 0)
			{
				return;
			}
			for (int num = active.Count - 1; num >= 0; num--)
			{
				if ((Object)(object)active[num] == (Object)null || ((NetworkBehaviour)active[num]).IsDeinitializing || ((Creature)active[num])._hp.Value <= 0)
				{
					active.RemoveAt(num);
				}
			}
			while (active.Count < 10 && !((Object)(object)SpawnEnemy(RandomMinionKind()) == (Object)null))
			{
			}
			State.total = 10;
			UpdateVariantCounts();
		}

		private void TickServer()
		{
			if (State.active && (!EventsEnabled || (Object)(object)OnlineIslandManager.Instance == (Object)null || !Rules.EligibleIsland(OnlineIslandManager.CurIsland, OnlineIslandManager.MaxIslandUnlocked)))
			{
				CancelEvent(destroyBodies: false);
			}
			else
			{
				if (!State.active)
				{
					return;
				}
				if (PlayerManager.Players.Count > 0 && PlayerManager.AlivePlayers.Count == 0)
				{
					wipeEnding = true;
					CancelEvent(destroyBodies: true);
					return;
				}
				if (State.intro)
				{
					if (Time.time >= introEnds)
					{
						SpawnCurrentRound();
					}
					return;
				}
				for (int num = active.Count - 1; num >= 0; num--)
				{
					AttackingFish val = active[num];
					if ((Object)(object)val == (Object)null || ((NetworkBehaviour)val).IsDeinitializing || ((Creature)val)._hp.Value <= 0)
					{
						active.RemoveAt(num);
					}
				}
				if (State.boss)
				{
					if ((Object)(object)boss == (Object)null || ((NetworkBehaviour)boss).IsDeinitializing || ((Creature)boss)._hp.Value <= 0)
					{
						CompleteBoss();
						return;
					}
					State.bossHp = ((Creature)boss)._hp.Value;
					State.bossMaxHp = Mathf.Max(State.bossMaxHp, ((Creature)boss)._hp.Value);
					if (Time.time >= nextMinionFill)
					{
						nextMinionFill = Time.time + 1f;
						FillBossMinions();
					}
					if (Time.time >= nextBossThunder)
					{
						nextBossThunder = Time.time + Random.Range(4f, 8f);
						State.thunderSignal++;
					}
					UpdateVariantCounts();
					return;
				}
				FillRoundQueue();
				UpdateVariantCounts();
				if (active.Count == 0 && pendingRoundEnemies == 0)
				{
					if (State.round < State.totalRounds)
					{
						BeginNextRound();
					}
					else
					{
						CompleteEvent();
					}
				}
			}
		}

		private void UpdateVariantCounts()
		{
			State.alive = active.Count + ((!State.boss) ? pendingRoundEnemies : 0);
			State.normalAlive = active.Count((AttackingFish x) => (Object)(object)x != (Object)null && FishmanItem.Kind((Item)(object)x) == FishmanKind.Fishman);
			State.bruteAlive = active.Count((AttackingFish x) => (Object)(object)x != (Object)null && FishmanItem.Kind((Item)(object)x) == FishmanKind.Brute);
			State.leaperAlive = active.Count((AttackingFish x) => (Object)(object)x != (Object)null && FishmanItem.Kind((Item)(object)x) == FishmanKind.Leaper);
		}

		private void BeginNextRound()
		{
			State.round++;
			State.roundSignal++;
			State.thunderSignal++;
			State.intro = true;
			pendingRoundEnemies = 0;
			State.alive = State.total;
			introEnds = Time.time + 2.4f;
			Status = "Wave " + State.wave + ", round " + State.round + " incoming!";
			Broadcast();
		}

		private void CompleteBoss()
		{
			world.completedBosses++;
			foreach (AttackingFish item in active)
			{
				if ((Object)(object)item != (Object)null && !((NetworkBehaviour)item).IsDeinitializing)
				{
					((Item)item).DestroyItem((byte)0, byte.MaxValue);
				}
			}
			active.Clear();
			if (State.revenge && activeBossRecord != null)
			{
				activeBossRecord.revengeDefeated = true;
			}
			else if (!string.IsNullOrEmpty(activeBossBaseName) && world.bosses.All((BossRecord x) => x.name != activeBossBaseName))
			{
				world.bosses.Add(new BossRecord
				{
					name = activeBossBaseName,
					kind = (int)activeBossKind
				});
			}
			CompleteEvent();
		}

		private void CompleteEvent()
		{
			if (State.active)
			{
				bool flag = State.boss;
				int bossNumber = State.bossNumber;
				world.completedWaves++;
				Status = (flag ? ("Boss " + bossNumber + " defeated! Wave " + world.completedWaves + " complete.") : ("Fishmen invasion defeated! Wave " + world.completedWaves + " complete."));
				ApplyState(new EventState
				{
					completed = world.completedWaves,
					roundSignal = State.roundSignal,
					thunderSignal = State.thunderSignal
				});
				active.Clear();
				pendingRoundEnemies = 0;
				boss = null;
				Persist();
				Broadcast();
			}
		}

		private void CancelEvent(bool destroyBodies)
		{
			AttackingFish[] array = (destroyBodies ? spawned : active).ToArray();
			foreach (AttackingFish val in array)
			{
				if ((Object)(object)val != (Object)null && !((NetworkBehaviour)val).IsDeinitializing)
				{
					((Item)val).DestroyItem((byte)0, byte.MaxValue);
				}
			}
			active.Clear();
			pendingRoundEnemies = 0;
			if (destroyBodies)
			{
				spawned.Clear();
			}
			boss = null;
			Status = (wipeEnding ? "The invasion ended when every player fell." : "Fishmen invasions disabled.");
			wipeEnding = false;
			ApplyState(new EventState
			{
				completed = ((world != null) ? world.completedWaves : 0),
				roundSignal = State.roundSignal,
				thunderSignal = State.thunderSignal
			});
			Persist();
			Broadcast();
		}

		private void ApplyState(EventState next)
		{
			bool num = !State.active && next.active;
			bool flag = State.active && !next.active;
			bool flag2 = next.active && (next.thunderSignal != State.thunderSignal || next.roundSignal != State.roundSignal);
			State = next;
			if (num || flag2)
			{
				flashStarted = Time.unscaledTime;
				EventAudio.Begin(next.boss, playThunder: true);
			}
			if (flag)
			{
				EventAudio.End();
			}
			else if (next.active)
			{
				EventAudio.SetBoss(next.boss);
			}
		}

		private void ReceiveState(StateMessage message, Channel channel)
		{
			if (InstanceFinder.IsServerStarted || string.IsNullOrEmpty(message.json) || message.json.Length > 8192)
			{
				return;
			}
			try
			{
				EventState eventState = Codec.FromJson<EventState>(message.json);
				if (eventState != null && eventState.protocol == 2)
				{
					ApplyState(eventState);
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Rejected invasion state: " + ex.Message));
			}
		}

		private void Broadcast()
		{
			if (InstanceFinder.IsServerStarted)
			{
				State.eventsEnabled = EventsEnabled;
				State.musicEnabled = MusicEnabled;
				State.lightningEnabled = LightningEnabled;
				State.musicVolume = MusicVolume;
			}
			if ((Object)(object)manager != (Object)null && InstanceFinder.IsServerStarted)
			{
				manager.ServerManager.Broadcast<StateMessage>(new StateMessage
				{
					json = Codec.ToJson(State)
				}, true, (Channel)0);
			}
		}

		private void Persist()
		{
			if (world == null || !InstanceFinder.IsServerStarted)
			{
				return;
			}
			try
			{
				string text = savePath + ".tmp";
				File.WriteAllText(text, Codec.ToJson(save));
				if (File.Exists(savePath))
				{
					File.Replace(text, savePath, savePath + ".bak");
				}
				else
				{
					File.Move(text, savePath);
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not save invasion progress: " + ex.Message));
			}
		}

		public void SaveNow()
		{
			Persist();
		}

		private void OnDestroy()
		{
			Persist();
			EventAudio.Destroy();
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Instance = null;
		}

		private Texture2D Solid(Color color)
		{
			//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_000a: 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_0017: Expected O, but got Unknown
			Texture2D val = new Texture2D(1, 1);
			val.SetPixel(0, 0, color);
			val.Apply();
			return val;
		}

		private void Styles()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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_007c: Expected O, but got Unknown
			//IL_0096: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Expected O, but got Unknown
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Expected O, but got Unknown
			//IL_017a: 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_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			if (titleStyle == null)
			{
				titleStyle = new GUIStyle(GUI.skin.label)
				{
					fontSize = 26,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)4
				};
				titleStyle.normal.textColor = new Color(0.28f, 0.92f, 1f);
				headerStyle = new GUIStyle(GUI.skin.label)
				{
					fontSize = 16,
					fontStyle = (FontStyle)1
				};
				headerStyle.normal.textColor = new Color(1f, 0.68f, 0.18f);
				labelStyle = new GUIStyle(GUI.skin.label)
				{
					fontSize = 14
				};
				labelStyle.normal.textColor = Color.white;
				buttonStyle = new GUIStyle(GUI.skin.button)
				{
					fontSize = 14,
					fontStyle = (FontStyle)1
				};
				barStyle = new GUIStyle(GUI.skin.label)
				{
					fontSize = 16,
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)4
				};
				barStyle.normal.textColor = Color.white;
				invasionButtonStyle = new GUIStyle(GUI.skin.button)
				{
					fontSize = 15,
					fontStyle = (FontStyle)1
				};
				invasionButtonStyle.normal.background = Solid(new Color(0.02f, 0.48f, 0.5f, 0.98f));
				invasionButtonStyle.hover.background = Solid(new Color(0.04f, 0.64f, 0.65f, 0.98f));
				invasionButtonStyle.active.background = Solid(new Color(0.01f, 0.36f, 0.39f, 0.98f));
				invasionButtonStyle.normal.textColor = new Color(1f, 0.86f, 0.12f);
				invasionButtonStyle.hover.textColor = new Color(1f, 0.94f, 0.28f);
				invasionButtonStyle.active.textColor = Color.yellow;
			}
		}

		private void OnGUI()
		{
			//IL_008f: 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_00b4: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: 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)
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			Styles();
			if (optionsOpen)
			{
				if (((Rect)(ref optionsRect)).width < 1f)
				{
					optionsRect = new Rect((float)(Screen.width - 720) / 2f, (float)(Screen.height - 705) / 2f, (float)Mathf.Min(720, Screen.width - 40), (float)Mathf.Min(705, Screen.height - 40));
				}
				GUI.depth = -30000;
				optionsRect = GUI.ModalWindow(765421, optionsRect, new WindowFunction(DrawOptionsWindow), "", GUI.skin.window);
				RecordGuiTiming(realtimeSinceStartup);
			}
			else
			{
				DrawAtmosphere();
				DrawEventMeter();
				DrawPauseButton();
			}
		}

		private void RecordGuiTiming(float started)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			if ((int)Event.current.type == 7)
			{
				float num = (Time.realtimeSinceStartup - started) * 1000f;
				if (num >= 8f && Time.unscaledTime >= nextGuiTimingLog)
				{
					nextGuiTimingLog = Time.unscaledTime + 10f;
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Fishmen options GUI repaint took " + num.ToString("0.0") + " ms. This diagnostic is throttled to once per 10 seconds."));
				}
			}
		}

		private void DrawAtmosphere()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			if (!State.active)
			{
				return;
			}
			Color color = GUI.color;
			GUI.depth = 900;
			GUI.color = new Color(0.025f, 0.045f, 0.075f, 0.13f);
			GUI.DrawTexture(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), (Texture)(object)Texture2D.whiteTexture);
			if (EffectiveLightningEnabled)
			{
				float flashAge = FlashAge;
				float num = 0f;
				if (flashAge >= 0f && flashAge < 0.18f)
				{
					num = 0.78f * (1f - flashAge / 0.18f);
				}
				else if (flashAge > 0.38f && flashAge < 0.55f)
				{
					num = 0.48f * (1f - (flashAge - 0.38f) / 0.17f);
				}
				else if (flashAge > 0.78f && flashAge < 0.96f)
				{
					num = 0.3f * (1f - (flashAge - 0.78f) / 0.18f);
				}
				if (num > 0f)
				{
					GUI.color = new Color(0.82f, 0.92f, 1f, num);
					GUI.DrawTexture(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), (Texture)(object)Texture2D.whiteTexture);
				}
			}
			GUI.color = color;
			GUI.depth = 0;
		}

		private void DrawEventMeter()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_031d: Unknown result type (might be due to invalid IL or missing references)
			//IL_033b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0361: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_040f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b1: Unknown result type (might be due to invalid IL or missing references)
			if (!State.active)
			{
				return;
			}
			float num = Mathf.Min(720f, (float)Screen.width - 80f);
			float num2 = ((float)Screen.width - num) / 2f;
			float num3 = 24f;
			GUI.Box(new Rect(num2 - 8f, num3 - 6f, num + 16f, (float)(State.boss ? 150 : 106)), GUIContent.none);
			GUI.Label(new Rect(num2, num3, num, 24f), "FISHMEN INVASION  •  WAVE " + State.wave + "  •  ROUND " + State.round + " / " + State.totalRounds + "  •  " + State.completed + " COMPLETED", barStyle);
			DrawRoster(num2 + num / 2f - 145f, num3 + 25f, FishmanKind.Fishman, State.normalAlive);
			DrawRoster(num2 + num / 2f - 35f, num3 + 25f, FishmanKind.Brute, State.bruteAlive);
			DrawRoster(num2 + num / 2f + 75f, num3 + 25f, FishmanKind.Leaper, State.leaperAlive);
			int num4 = Mathf.Max(1, State.total);
			int num5 = Mathf.Clamp(State.alive, 0, num4);
			float num6 = 3f;
			float num7 = (num - num6 * (float)(num4 - 1)) / (float)num4;
			for (int i = 0; i < num4; i++)
			{
				Color color = GUI.color;
				GUI.color = ((i < num5) ? new Color(0.08f, 0.78f, 0.87f) : new Color(0.16f, 0.19f, 0.22f));
				GUI.DrawTexture(new Rect(num2 + (float)i * (num7 + num6), num3 + 66f, num7, 20f), (Texture)(object)Texture2D.whiteTexture);
				GUI.color = color;
			}
			GUI.Label(new Rect(num2, num3 + 64f, num, 24f), State.intro ? "INCOMING..." : (num5 + " / " + num4 + " FISHMEN REMAINING"), barStyle);
			if (State.boss)
			{
				float num8 = ((State.bossMaxHp <= 0) ? 0f : Mathf.Clamp01((float)State.bossHp / (float)State.bossMaxHp));
				Color color2 = GUI.color;
				GUI.color = (State.revenge ? new Color(0.95f, 0.05f, 0.04f) : new Color(0.78f, 0.18f, 0.62f));
				GUI.DrawTexture(new Rect(num2, num3 + 108f, num * num8, 24f), (Texture)(object)Texture2D.whiteTexture);
				GUI.color = new Color(0.12f, 0.08f, 0.13f);
				GUI.DrawTexture(new Rect(num2 + num * num8, num3 + 108f, num * (1f - num8), 24f), (Texture)(object)Texture2D.whiteTexture);
				GUI.color = color2;
				FishmanKind key = (FishmanKind)Mathf.Clamp(State.bossKind, 3, 6);
				if (Assets.Icons.TryGetValue(key, out var value))
				{
					GUI.DrawTexture(new Rect(num2 + 92f, num3 + 104f, 34f, 34f), (Texture)(object)value, (ScaleMode)2, true);
				}
				GUI.Label(new Rect(num2 + 8f, num3 + 107f, 82f, 26f), "BOSS " + State.bossNumber, barStyle);
				GUI.Label(new Rect(num2, num3 + 107f, num, 26f), State.bossName + "  •  " + State.bossHp + " / " + State.bossMaxHp, barStyle);
			}
		}

		private void DrawRoster(float x, float y, FishmanKind kind, int count)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			if (Assets.Icons.TryGetValue(kind, out var value))
			{
				GUI.DrawTexture(new Rect(x, y, 28f, 28f), (Texture)(object)value, (ScaleMode)2, true);
			}
			GUI.Label(new Rect(x + 30f, y + 2f, 72f, 24f), "x" + count, barStyle);
		}

		private void DrawPauseButton()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (PauseManager.IsPaused && !optionsOpen)
			{
				float num = 330f;
				GUI.depth = -20000;
				if (GUI.Button(new Rect(((float)Screen.width - num) / 2f, 72f, num, 42f), "FISHMEN INVASION OPTIONS", invasionButtonStyle))
				{
					OpenOptions();
				}
			}
		}

		private void OpenOptions()
		{
			optionsOpen = true;
			LoadDrafts();
			SetVanillaPauseVisible(visible: false);
		}

		private void SetVanillaPauseVisible(bool visible)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			try
			{
				PauseManager val = Object.FindAnyObjectByType<PauseManager>();
				if (!((Object)(object)val == (Object)null))
				{
					GameObject val2 = (GameObject)AccessTools.Field(typeof(PauseManager), "_mainScreen").GetValue(val);
					GameObject val3 = (GameObject)AccessTools.Field(typeof(PauseManager), "_optionsScreen").GetValue(val);
					if ((Object)(object)val2 != (Object)null)
					{
						val2.SetActive(visible && !MainMenuManager.IsInMenu);
					}
					if ((Object)(object)val3 != (Object)null)
					{
						val3.SetActive(visible && MainMenuManager.IsInMenu);
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not toggle the background pause menu: " + ex.Message));
			}
		}

		private void LoadDrafts()
		{
			amountDraft = AmountMultiplier;
			healthDraft = HealthMultiplier;
			attackDraft = AttackMultiplier;
			frequencyDraft = FrequencyMultiplier;
			musicVolumeDraft = MusicVolume;
			draftsLoaded = true;
		}

		private float SliderRow(float x, float y, float width, string name, float value, float min, float max)
		{
			//IL_000c: 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_007f: Unknown result type (might be due to invalid IL or missing references)
			GUI.Label(new Rect(x, y, 245f, 24f), name + ":  " + value.ToString("0.00") + "x", labelStyle);
			value = GUI.HorizontalSlider(new Rect(x + 235f, y + 6f, width - 355f, 20f), value, min, max);
			if (GUI.Button(new Rect(x + width - 108f, y - 4f, 108f, 30f), "DEFAULT", buttonStyle))
			{
				value = 1f;
			}
			return value;
		}

		private float VolumeRow(float x, float y, float width, float value)
		{
			//IL_000c: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			GUI.Label(new Rect(x, y, 245f, 24f), "Event music volume:  " + Mathf.RoundToInt(value * 100f) + "%", labelStyle);
			value = GUI.HorizontalSlider(new Rect(x + 235f, y + 6f, width - 355f, 20f), value, 0f, 1f);
			if (GUI.Button(new Rect(x + width - 108f, y - 4f, 108f, 30f), "DEFAULT", buttonStyle))
			{
				value = 0.7f;
			}
			return value;
		}

		private void DrawOptionsWindow(int id)
		{
			//IL_001a: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_044c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0482: Unknown result type (might be due to invalid IL or missing references)
			if (!draftsLoaded)
			{
				LoadDrafts();
			}
			float width = ((Rect)(ref optionsRect)).width;
			Color color = GUI.color;
			GUI.color = new Color(0.025f, 0.075f, 0.1f, 0.985f);
			GUI.DrawTexture(new Rect(0f, 0f, ((Rect)(ref optionsRect)).width, ((Rect)(ref optionsRect)).height), (Texture)(object)Texture2D.whiteTexture);
			GUI.color = color;
			GUI.Label(new Rect(20f, 18f, width - 40f, 38f), "FISHMEN INVASION OPTIONS", titleStyle);
			if (GUI.Button(new Rect(width - 48f, 14f, 34f, 30f), "X", buttonStyle))
			{
				CloseOptions();
				return;
			}
			GUI.Label(new Rect(28f, 62f, width - 56f, 44f), "Only the host can change invasion gameplay, lightning, music, and volume settings for the lobby.", labelStyle);
			bool isServerStarted = InstanceFinder.IsServerStarted;
			bool enabled = GUI.enabled;
			GUI.enabled = isServerStarted;
			bool flag = (isServerStarted ? EventsEnabled : State.eventsEnabled);
			bool flag2 = GUI.Toggle(new Rect(35f, 118f, 310f, 28f), flag, " Enable Fishmen invasions");
			if (isServerStarted && flag2 != EventsEnabled)
			{
				SetToggle("events", flag2);
			}
			bool flag3 = (isServerStarted ? LightningEnabled : State.lightningEnabled);
			bool flag4 = GUI.Toggle(new Rect(365f, 118f, 160f, 28f), flag3, " Lightning flashes");
			if (isServerStarted && flag4 != LightningEnabled)
			{
				SetToggle("lightning", flag4);
			}
			bool flag5 = (isServerStarted ? MusicEnabled : State.musicEnabled);
			bool flag6 = GUI.Toggle(new Rect(535f, 118f, 145f, 28f), flag5, " Event music");
			if (isServerStarted && flag6 != MusicEnabled)
			{
				SetToggle("music", flag6);
			}
			GUI.Label(new Rect(30f, 158f, width - 60f, 28f), "HOST INVASION SETTINGS", headerStyle);
			amountDraft = SliderRow(35f, 195f, width - 70f, "Fishmen amount per round", amountDraft, 0.5f, 3f);
			healthDraft = SliderRow(35f, 245f, width - 70f, "Fishmen health", healthDraft, 0.5f, 3f);
			attackDraft = SliderRow(35f, 295f, width - 70f, "Fishmen attack", attackDraft, 0.25f, 2f);
			frequencyDraft = SliderRow(35f, 345f, width - 70f, "Invasion frequency", frequencyDraft, 0.5f, 3f);
			musicVolumeDraft = VolumeRow(35f, 395f, width - 70f, musicVolumeDraft);
			if (GUI.Button(new Rect(35f, 445f, width - 70f, 42f), isServerStarted ? "APPLY AND SAVE HOST SETTINGS" : "HOST CONTROLS ALL EVENT SETTINGS", buttonStyle) && isServerStarted)
			{
				SaveOptions(amountDraft, healthDraft, attackDraft, frequencyDraft, musicVolumeDraft);
			}
			if (GUI.Button(new Rect(35f, 497f, width - 70f, 42f), State.active ? "INVASION ALREADY ACTIVE" : "FORCE START WAVE", buttonStyle) && isServerStarted && !State.active)
			{
				ForceStartEvent();
			}
			if (GUI.Button(new Rect(35f, 549f, width - 70f, 42f), State.active ? "INVASION ALREADY ACTIVE" : "FORCE RANDOM BOSS EVENT", buttonStyle) && isServerStarted && !State.active)
			{
				ForceStartBossEvent();
			}
			GUI.enabled = enabled;
			GUI.Label(new Rect(35f, 604f, width - 70f, 64f), isServerStarted ? Status : "These values are controlled by the host and synchronized to every player.", labelStyle);
			GUI.DragWindow(new Rect(0f, 0f, width - 55f, 58f));
		}

		public void CloseOptions()
		{
			optionsOpen = false;
			SetVanillaPauseVisible(visible: true);
		}
	}
	[HarmonyPatch(typeof(GameInfo), "Awake")]
	internal static class RegisterFishman
	{
		private static void Postfix()
		{
			try
			{
				FishmanItem.Register();
			}
			catch (Exception ex)
			{
				Plugin.Log("Fishman registration deferred: " + ex.Message);
			}
		}
	}
	[HarmonyPatch(typeof(Creature), "ServerChangeHp")]
	internal static class CountFishDefeated
	{
		private static void Prefix(Creature __instance, out int __state)
		{
			__state = ((!((Object)(object)__instance == (Object)null)) ? __instance._hp.Value : 0);
		}

		private static void Postfix(Creature __instance, int __state)
		{
			if ((Object)(object)__instance != (Object)null && __state > 0 && __instance._hp.Value <= 0 && (Object)(object)Plugin.Instance != (Object)null)
			{
				Plugin.Instance.ServerCreatureKilled(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Item), "GetName")]
	internal static class FishmanName
	{
		private static bool Prefix(Item __instance, ref string __result)
		{
			if (!FishmanItem.Is(__instance))
			{
				return true;
			}
			__result = FishmanItem.Name(__instance);
			return false;
		}
	}
	[HarmonyPatch(typeof(SaveManager), "SaveServer")]
	internal static class SaveInvasion
	{
		private static void Postfix()
		{
			if ((Object)(object)Plugin.Instance != (Object)null)
			{
				Plugin.Instance.SaveNow();
			}
		}
	}
	[HarmonyPatch(typeof(PauseManager), "PauseInput")]
	internal static class CloseInvasionOptions
	{
		private static bool Prefix(CallbackContext context)
		{
			if ((Object)(object)Plugin.Instance == (Object)null || !Plugin.Instance.OptionsOpen)
			{
				return true;
			}
			if (((CallbackContext)(ref context)).performed)
			{
				Plugin.Instance.CloseOptions();
			}
			return false;
		}
	}
}