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 bosses = new List(); } [Serializable] public sealed class SaveData { public int version = 2; public List worlds = new List(); } [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 value) { return JsonConvert.SerializeObject((object)value, Settings); } public static T FromJson(string value) { return JsonConvert.DeserializeObject(value, Settings); } } public static class Assets { public static readonly Dictionary Meshes = new Dictionary(); public static readonly Dictionary Textures = new Dictionary(); public static readonly Dictionary Icons = new Dictionary(); 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 Prefabs = new Dictionary(); 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() != (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 dictionary = (Dictionary)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 source = (from x in dictionary.Values.OfType() where (Object)(object)x != (Object)null && (Object)(object)((Component)x).GetComponent() != (Object)null select x).Distinct().ToList(); AttackingFish val = ((IEnumerable)source).FirstOrDefault((Func)((AttackingFish x) => (int)((Creature)x).BossType != 0 && (((Object)x).name ?? "").IndexOf("tuna", StringComparison.OrdinalIgnoreCase) >= 0)) ?? ((IEnumerable)source).FirstOrDefault((Func)((AttackingFish x) => (((Object)x).name ?? "").IndexOf("tuna", StringComparison.OrdinalIgnoreCase) >= 0)) ?? ((IEnumerable)source).FirstOrDefault((Func)((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 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 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(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(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = false; } Collider[] componentsInChildren2 = ((Component)val).GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { componentsInChildren2[i].enabled = false; } FishmanVisual fishmanVisual = ((Component)val).gameObject.AddComponent(); fishmanVisual.Build(kind); ((Component)val).gameObject.AddComponent().kind = kind; if (kind >= FishmanKind.Deepjaw) { ((Component)val).gameObject.AddComponent(); } 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(true) where r.enabled select r).ToList()); NetworkObject component = ((Component)val).GetComponent(); component.SetIsSpawnable(true); PrefabObjects prefabObjects = InstanceFinder.NetworkManager.GetPrefabObjects(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)AccessTools.Field(typeof(GameInfo), "_allItems").GetValue(null)).Add(Rules.ItemIds[num], (Item)(object)val); ((Dictionary)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(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(); 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().sharedMesh = Assets.Meshes[kind]; modelRenderer = (Renderer)(object)val.AddComponent(); 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[] array = new List[24]; for (int i = 0; i < array.Length; i++) { array[i] = new List(); } 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[] array3 = array; Bounds val2 = default(Bounds); foreach (List 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(); obj.center = (val4 + val5) * 0.5f; obj.size = val6; } } } } private void Awake() { rig = ((Component)this).GetComponent(); creature = ((Component)this).GetComponent(); if ((Object)(object)model == (Object)null) { model = ((Component)this).transform.Find("FishmanModel"); if ((Object)(object)model != (Object)null) { modelRenderer = ((Component)model).GetComponent(); } } 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(); } if ((Object)(object)rig == (Object)null) { rig = ((Component)this).GetComponent(); } 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(); rig = ((Component)this).GetComponent(); 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(); rig = ((Component)this).GetComponent(); } 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(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].enabled = false; } Collider[] componentsInChildren2 = ((Component)val).GetComponentsInChildren(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 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(); musicSource.loop = true; musicSource.playOnAwake = false; musicSource.spatialBlend = 0f; fxSource = val.AddComponent(); 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 eventsEnabled; private ConfigEntry lightningEnabled; private ConfigEntry musicEnabled; private ConfigEntry musicVolume; private ConfigEntry amountMultiplier; private ConfigEntry healthMultiplier; private ConfigEntry attackMultiplier; private ConfigEntry frequencyMultiplier; private SaveData save = new SaveData(); private WorldData world; private string savePath; private NetworkManager manager; private Harmony harmony; private readonly List active = new List(); private readonly List spawned = new List(); 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("Event", "Enabled", true, "Host: enable Fishmen invasions."); lightningEnabled = ((BaseUnityPlugin)this).Config.Bind("Effects", "LightningFlashes", true, "Host: show lightning flashes for the lobby."); musicEnabled = ((BaseUnityPlugin)this).Config.Bind("Effects", "EventMusic", true, "Host: play invasion music for the lobby."); musicVolume = ((BaseUnityPlugin)this).Config.Bind("Effects", "EventMusicVolume", 0.7f, "Host event music volume (0-1). Default is 30% quieter."); amountMultiplier = ((BaseUnityPlugin)this).Config.Bind("Balance", "FishmenAmountMultiplier", 1f, "Host event amount multiplier (0.5-3.0)."); healthMultiplier = ((BaseUnityPlugin)this).Config.Bind("Balance", "FishmenHealthMultiplier", 1f, "Host health multiplier (0.5-3.0)."); attackMultiplier = ((BaseUnityPlugin)this).Config.Bind("Balance", "FishmenAttackMultiplier", 1f, "Host attack multiplier (0.25-2.0)."); frequencyMultiplier = ((BaseUnityPlugin)this).Config.Bind("Balance", "InvasionFrequencyMultiplier", 1f, "Host frequency multiplier (0.5-3.0; higher triggers sooner)."); ClampConfig(); savePath = Path.Combine(Paths.ConfigPath, "FishmenInvasion-worlds.json"); LoadSave(); GenericWriter.SetWrite((Action)delegate(Writer w, StateMessage v) { w.WriteString(v.json); }); GenericReader.SetRead((Func)((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(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(); } } } 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() != (Object)null; Player componentInParent = ((Component)__0).GetComponentInParent(); 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((Action)ReceiveState); } manager = InstanceFinder.NetworkManager; if ((Object)(object)manager != (Object)null) { manager.ClientManager.RegisterBroadcast((Action)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(); } 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 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 used = new HashSet(world.bosses.Select((BossRecord x) => x.name)); List 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(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(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(); 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; } } }