Decompiled source of Shattered Horizons Pirates Addon v1.0.2

BepInEx\patchers\FST.BeholdCompatibility.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Mono.Cecil;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyVersion("0.1.1.0")]
namespace FST.BeholdCompatibility;

public static class Patcher
{
	private static Harmony compatibility;

	private static readonly FieldInfo Container = AccessTools.Field(typeof(PatchClassProcessor), "containerType");

	private static readonly FieldInfo Attributes = AccessTools.Field(typeof(PatchClassProcessor), "containerAttributes");

	private static readonly FieldInfo PatchMethods = AccessTools.Field(typeof(PatchClassProcessor), "patchMethods");

	public static IEnumerable<string> TargetDLLs => new string[1] { "Assembly-CSharp.dll" };

	public static void Patch(AssemblyDefinition assembly)
	{
	}

	public static void Initialize()
	{
		if (Directory.Exists(Paths.PluginPath) && Directory.GetFiles(Paths.PluginPath, "Behold.Pirates.dll", SearchOption.AllDirectories).Length != 0)
		{
			Install();
		}
	}

	public static void Install()
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Expected O, but got Unknown
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Expected O, but got Unknown
		if (compatibility == null)
		{
			if (Container == null || Attributes == null || PatchMethods == null)
			{
				throw new InvalidOperationException("Unsupported HarmonyX class-processor ABI");
			}
			compatibility = new Harmony("fst.behold.startup.compatibility");
			compatibility.Patch((MethodBase)AccessTools.Method(typeof(PatchClassProcessor), "Patch", Type.EmptyTypes, (Type[])null), new HarmonyMethod(typeof(Patcher), "AdaptClass", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
	}

	private static bool AdaptClass(PatchClassProcessor __instance)
	{
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Expected O, but got Unknown
		//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
		Type type = (Type)Container.GetValue(__instance);
		if (type == null || type.Assembly.GetName().Name != "Behold.Pirates")
		{
			return true;
		}
		HarmonyMethod attributes = (HarmonyMethod)Attributes.GetValue(__instance);
		if (attributes == null || attributes.declaringType == null || attributes.declaringType.FullName != "ProjectileManager")
		{
			return true;
		}
		if (type.FullName == "Behold.Pirates.PirateProjectileWaterImpactPatch" && attributes.methodName == "HitWater" && !attributes.declaringType.GetMethods(AccessTools.all).Any((MethodInfo m) => m.Name == "HitWater" && m.GetParameters().Any((ParameterInfo p) => p.Name == "projectile")))
		{
			Logger.CreateLogSource("FST Behold compatibility").LogInfo((object)"Keeping native water impact FX for How to Fish 1.1.3");
			return false;
		}
		if (attributes.methodName == "AddProjectile" || attributes.methodName == "AddProjectiles")
		{
			MethodInfo[] array = (from m in attributes.declaringType.GetMethods(AccessTools.all)
				where m.Name == attributes.methodName && m.GetParameters().Length == 9
				select m).ToArray();
			if (array.Length != 1)
			{
				throw new InvalidOperationException("Unsupported native projectile target: " + attributes.methodName);
			}
			attributes.argumentTypes = (from p in array[0].GetParameters()
				select p.ParameterType).ToArray();
			foreach (object item in (IEnumerable)PatchMethods.GetValue(__instance))
			{
				((HarmonyMethod)AccessTools.Field(item.GetType(), "info").GetValue(item)).argumentTypes = attributes.argumentTypes;
			}
		}
		return true;
	}
}

BepInEx\plugins\ShatteredHorizonsPirates\FST.BeholdPiratesAddon.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using FishNet.Broadcast;
using FishNet.Managing;
using FishNet.Object;
using FishNet.Object.Synchronizing;
using FishNet.Serializing;
using FishNet.Transporting;
using HTF.DLC.FST.Core;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("HTF DLC FST - Behold Pirates Addon")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2")]
[assembly: AssemblyVersion("0.1.3.0")]
namespace HTF.DLC.FST.BeholdPiratesAddon;

internal struct FstPirateState : IBroadcast
{
	internal int Boat;

	internal int Health;

	internal byte[] Weapons;

	internal byte Shooter;

	internal byte Phase;

	internal Vector3 Aim;
}
internal static class FstPirateIntegration
{
	private sealed class AimState
	{
		internal Player Target;

		internal Vector3 Direction;

		internal float Updated;

		internal float Until;

		internal float SendAt;

		internal byte Phase;
	}

	private static Harmony patch;

	private static Type encounter;

	private static Type definition;

	private static Type support;

	private static Type controller;

	private static Type health;

	private static Type visual;

	private static Type animator;

	private static NetworkManager net;

	private static readonly Dictionary<Boat, byte[]> loadouts = new Dictionary<Boat, byte[]>();

	private static readonly Dictionary<Boat, float> pursuitStarted = new Dictionary<Boat, float>();

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

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

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

	private static ConditionalWeakTable<object, AimState> aiming = new ConditionalWeakTable<object, AimState>();

	private static float nextEncounter;

	private static float nextSync;

	private static bool installed;

	private static bool failed;

	private static bool observing;

	private static bool Server
	{
		get
		{
			if (Object.op_Implicit((Object)(object)net))
			{
				return net.IsServerStarted;
			}
			return false;
		}
	}

	private static object Field(object o, string name)
	{
		return AccessTools.Field(o.GetType(), name).GetValue(o);
	}

	private static void Set(object o, string name, object value)
	{
		AccessTools.Field(o.GetType(), name).SetValue(o, value);
	}

	private static object Property(object o, string name)
	{
		return AccessTools.Property(o.GetType(), name).GetValue(o, null);
	}

	internal static int Tier()
	{
		return FstPirateApi.GetProgressionTier();
	}

	internal static bool Install()
	{
		if (!observing)
		{
			AppDomain.CurrentDomain.AssemblyLoad += Loaded;
			observing = true;
		}
		TryInstall();
		if (installed)
		{
			return !failed;
		}
		return false;
	}

	private static void Loaded(object sender, AssemblyLoadEventArgs args)
	{
		if (args.LoadedAssembly.GetName().Name == "Behold.Pirates")
		{
			TryInstall();
		}
	}

	private static void Hook(Type t, string name, string before = null, string after = null, Type[] signature = null)
	{
		//IL_005d: 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)
		MethodBase methodBase = ((name == ".ctor") ? ((MethodBase)AccessTools.Constructor(t, signature, false)) : ((MethodBase)((signature == null) ? AccessTools.Method(t, name, (Type[])null, (Type[])null) : AccessTools.Method(t, name, signature, (Type[])null))));
		if (methodBase == null)
		{
			throw new MissingMethodException(t.FullName, name);
		}
		patch.Patch(methodBase, (before == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(FstPirateIntegration), before, (Type[])null), (after == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(FstPirateIntegration), after, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
	}

	private static void TryInstall()
	{
		//IL_0447: Unknown result type (might be due to invalid IL or missing references)
		//IL_0451: Expected O, but got Unknown
		//IL_017c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0186: Expected O, but got Unknown
		if (installed || failed)
		{
			return;
		}
		Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "Behold.Pirates");
		if (assembly == null)
		{
			return;
		}
		try
		{
			Type type = assembly.GetType("Behold.Pirates.Plugin", throwOnError: true);
			string text = (string)AccessTools.Field(type, "PluginVersion").GetRawConstantValue();
			if (text != "0.14.1")
			{
				throw new InvalidOperationException("Unsupported Behold Pirates " + text + "; expected 0.14.1");
			}
			string[] array = new string[2] { "AddProjectile", "AddProjectiles" };
			foreach (string name in array)
			{
				if (!typeof(ProjectileManager).GetMethods(AccessTools.all).Any((MethodInfo m) => m.Name == name && m.GetParameters().Length == 8))
				{
					throw new InvalidOperationException("FST.Compatibility v2 is required for NPC projectile ABI");
				}
			}
			encounter = assembly.GetType("Behold.Pirates.PirateEncounterController", throwOnError: true);
			definition = assembly.GetType("Behold.Pirates.PirateWeaponDefinition", throwOnError: true);
			support = assembly.GetType("Behold.Pirates.PirateBoatSupport", throwOnError: true);
			controller = assembly.GetType("Behold.Pirates.PirateBoatController", throwOnError: true);
			health = assembly.GetType("Behold.Pirates.PirateCrewHealth", throwOnError: true);
			visual = assembly.GetType("Behold.Pirates.PirateWeaponVisual", throwOnError: true);
			animator = assembly.GetType("Behold.Pirates.PirateCrewAnimator", throwOnError: true);
			patch = new Harmony("fst.pirates.integration");
			Type type2 = assembly.GetType("Behold.Pirates.OpenSeaConfig", throwOnError: true);
			Hook(type2, ".ctor", null, "Configure", new Type[1] { typeof(ConfigFile) });
			Hook(type2, "get_StreamModeEnabled", "ConfigStreamModeEnabled");
			Hook(encounter, "FindPirateWeapons", "Weapons");
			Hook(encounter, "TrySpawnPirates", "EncounterBefore", "EncounterAfter");
			Hook(encounter, "SpawnPirateBoatAt", "SpawnBefore", "SpawnAfter");
			Hook(definition, "FromWeapon", null, "Definition");
			Hook(support, "GetVisualWeapons", "VisualWeapons");
			Hook(support, "CreatePirateVisuals", "CanCreateVisuals");
			Hook(support, "UnregisterServerPirate", null, "Remove");
			Hook(controller, "UpdateShooter", "Aim", null, new Type[6]
			{
				controller.GetNestedType("ShooterState", BindingFlags.NonPublic),
				typeof(Player),
				typeof(Vector3),
				typeof(Vector3),
				typeof(bool),
				health
			});
			Hook(controller, "FireAt", "Fire");
			Hook(controller, "OnDestroy", null, "DestroyController");
			Hook(controller, "FindClosestOceanPlayer", null, "Target");
			Hook(health, "Initialize", "CrewBefore", "CrewAfter");
			Hook(health, "get_MaxHealth", null, "CrewHealth");
			Hook(assembly.GetType("Behold.Pirates.PirateCollectibleSupport", throwOnError: true), "SpawnLivingCreature", null, "CrewValue");
			if (Chainloader.PluginInfos.TryGetValue("behold.howtofish.pirates", out var value) && Object.op_Implicit((Object)(object)value.Instance))
			{
				object value2 = AccessTools.Field(type, "_settings").GetValue(value.Instance);
				if (value2 != null)
				{
					Configure(value2);
				}
				object value3 = AccessTools.Field(type, "_openSeaBoundary").GetValue(value.Instance);
				if (value3 != null)
				{
					FstPirateSettings.ConfigureBoundary(value3);
				}
			}
			installed = true;
			Debug.Log((object)"[FST pirates] Behold 0.14.1 adapter installed; native travel/boats/projectiles retained; FST loadouts and firing policy enforced");
		}
		catch (Exception ex)
		{
			failed = true;
			installed = false;
			FstPirateSettings.Shutdown();
			Debug.LogError((object)("[FST pirates] Integration unavailable; pirate encounters disabled: " + ex));
			if (patch != null)
			{
				patch.UnpatchSelf();
			}
			encounter = assembly.GetType("Behold.Pirates.PirateEncounterController", throwOnError: false);
			if (encounter != null)
			{
				patch = new Harmony("fst.pirates.integration");
				Hook(encounter, "Update", "Disable");
			}
		}
	}

	private static bool Disable()
	{
		return false;
	}

	private static void Configure(object __instance)
	{
		FstPirateSettings.Configure(__instance, !failed);
	}

	private static bool ConfigStreamModeEnabled(ref bool __result)
	{
		__result = false;
		return false;
	}

	private static bool Weapons(ref IReadOnlyList<Weapon> __result)
	{
		__result = (from w in FstPirateRules.Pool(Tier()).Select(delegate(byte id)
			{
				Item spawnable = GameInfo.GetSpawnable(id);
				return (Weapon)(object)((spawnable is Weapon) ? spawnable : null);
			})
			where Object.op_Implicit((Object)(object)w)
			select w).ToArray();
		return false;
	}

	private static bool EncounterBefore(Player __0, ref bool __result)
	{
		__result = false;
		if (Server && Tier() >= 6 && Time.time >= nextEncounter && Object.op_Implicit((Object)(object)__0) && ((NetworkBehaviour)__0).IsServerInitialized && !((NetworkBehaviour)__0).IsDeinitializing && __0.Vitals.Health > 0)
		{
			return !__0.Dying.IsDead;
		}
		return false;
	}

	private static void EncounterAfter(bool __result)
	{
		if (__result)
		{
			nextEncounter = Time.time + (float)((Tier() >= 9) ? 150 : ((Tier() >= 8) ? 180 : 210));
		}
	}

	private static Array Definitions(byte[] ids)
	{
		Array array = Array.CreateInstance(definition, ids.Length);
		for (int i = 0; i < ids.Length; i++)
		{
			Item spawnable = GameInfo.GetSpawnable(ids[i]);
			Weapon val = (Weapon)(object)((spawnable is Weapon) ? spawnable : null);
			if (!Object.op_Implicit((Object)(object)val))
			{
				throw new InvalidOperationException("Missing production pirate weapon " + ids[i]);
			}
			array.SetValue(AccessTools.Method(definition, "FromWeapon", (Type[])null, (Type[])null).Invoke(null, new object[1] { val }), i);
		}
		return array;
	}

	private static bool SpawnBefore(object[] __args)
	{
		//IL_0116: 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)
		if (!Server || Tier() < 6 || loadouts.Keys.Count((Boat val) => Object.op_Implicit((Object)(object)val)) >= ((Tier() < 9) ? 1 : 2))
		{
			return false;
		}
		byte[] array = FstPirateRules.Pool(Tier());
		if (array.Any((byte id) => !(GameInfo.GetSpawnable(id) is Weapon)))
		{
			return false;
		}
		byte b = ((Random.value < ((Tier() >= 8) ? 0.72f : 0.55f)) ? array[0] : array[1]);
		Set(__args[1], "Weapons", Definitions(new byte[1] { b }));
		IList list = AccessTools.Field(typeof(Boat), "_motors").GetValue(__args[0]) as IList;
		Set(__args[1], "MotorIndex", FstPirateApi.PirateMotorIndex(list?.Count ?? 0, Tier()));
		Vector3 point = (Vector3)__args[2];
		return !PlayerManager.Players.Where((Player p) => Object.op_Implicit((Object)(object)p) && ((NetworkBehaviour)p).IsServerInitialized && p.Vitals.Health > 0).ToArray().Any((Player p) => Vector3.Distance(p.Transform.position, point) < 110f);
	}

	private static void SpawnAfter(Boat __result)
	{
		if (Object.op_Implicit((Object)(object)__result) && Server && ((IDictionary)AccessTools.Field(support, "PirateWeapons").GetValue(null))[__result] is IEnumerable source)
		{
			loadouts[__result] = (from object d in source
				select ((Item)(Weapon)Property(d, "VisualSource")).ID).ToArray();
			pursuitStarted[__result] = Time.time;
			FstPirateApi.ConfigurePirateBoat(__result, Tier());
			SendLoadout(__result);
		}
	}

	private static void Definition(Weapon __0, object __result)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)__0) && new byte[4] { 54, 242, 244, 246 }.Contains(((Item)__0).ID))
		{
			FstPirateRules.FireProfile fireProfile = FstPirateRules.Profile(((Item)__0).ID);
			((WeaponInfo)Property(__result, "Projectile")).ProjectileDamage = fireProfile.Damage;
			Set(__result, "<FireInterval>k__BackingField", fireProfile.Interval);
			Set(__result, "<Spread>k__BackingField", fireProfile.Spread);
			Set(__result, "<AttackRange>k__BackingField", fireProfile.Range);
			Set(__result, "<MagazineSize>k__BackingField", fireProfile.Magazine);
			Set(__result, "<ReloadDuration>k__BackingField", fireProfile.Recovery);
			Set(__result, "<EmptyReloadDuration>k__BackingField", fireProfile.Recovery);
		}
	}

	private static bool VisualWeapons(Boat __0)
	{
		if (Server)
		{
			return true;
		}
		((IDictionary)AccessTools.Field(support, "PirateWeapons").GetValue(null))[__0] = (loadouts.TryGetValue(__0, out var value) ? Definitions(value) : Array.CreateInstance(definition, 0));
		return true;
	}

	private static bool CanCreateVisuals(Boat __0)
	{
		if (!Server)
		{
			return loadouts.ContainsKey(__0);
		}
		return true;
	}

	private static void Remove(Boat __0)
	{
		loadouts.Remove(__0);
		pursuitStarted.Remove(__0);
		healthBudgets.Remove(__0);
		salePrices.Remove(__0);
	}

	private static void DestroyController(object __instance)
	{
		object obj = Field(__instance, "_boat");
		Boat val = (Boat)((obj is Boat) ? obj : null);
		if (Object.op_Implicit((Object)(object)val))
		{
			Remove(val);
		}
	}

	private static void Target(object __instance, ref Player __result)
	{
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		object obj = Field(__instance, "_boat");
		Boat val = (Boat)((obj is Boat) ? obj : null);
		if (!Server || !Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)__result) || ((NetworkBehaviour)__result).IsDeinitializing || __result.Vitals.Health <= 0 || __result.Dying.IsDead || Vector3.Distance(val.VisualBoat.position, __result.Transform.position) > 260f || (pursuitStarted.TryGetValue(val, out var value) && Time.time - value > 180f))
		{
			__result = null;
		}
	}

	private static void CrewBefore(Boat __0)
	{
		if (Server && Object.op_Implicit((Object)(object)__0) && !healthBudgets.ContainsKey(__0))
		{
			healthBudgets[__0] = FstPirateRules.Health(Tier());
			int destinationTier = FstPirateApi.GetDestinationTier();
			salePrices[__0] = FstPirateRules.SaleWorth(FstPirateApi.DestinationBaseWorth(destinationTier));
			Debug.Log((object)("[FST pirates] Spawn budget: progression=" + Tier() + ", HP=" + healthBudgets[__0] + ", destination=" + destinationTier + ", base sale=" + salePrices[__0]));
		}
	}

	private static void CrewHealth(object __instance, ref int __result)
	{
		object obj = Field(__instance, "_boat");
		Boat val = (Boat)((obj is Boat) ? obj : null);
		if (Object.op_Implicit((Object)(object)val) && healthBudgets.TryGetValue(val, out var value))
		{
			__result = value;
		}
	}

	private static void CrewAfter(object __instance)
	{
		int num = (int)Property(__instance, "MaxHealth");
		Set(__instance, "_maxHealth", num);
		object obj = Field(__instance, "_creature");
		Creature val = (Creature)((obj is Creature) ? obj : null);
		if (Object.op_Implicit((Object)(object)val))
		{
			AccessTools.Field(typeof(Creature), "_maxHp").SetValue(val, num);
		}
	}

	private static void CrewValue(object __0)
	{
		if (Server)
		{
			object obj = Field(__0, "_creature");
			Creature val = (Creature)((obj is Creature) ? obj : null);
			object obj2 = Field(__0, "_boat");
			Boat val2 = (Boat)((obj2 is Boat) ? obj2 : null);
			if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val2) && salePrices.TryGetValue(val2, out var value) && value > 0)
			{
				((SyncVar<float>)AccessTools.Field(typeof(Item), "_bettingMultiplier").GetValue(val)).Value = (float)value / (float)((Item)val).DefaultWorth;
			}
		}
	}

	private static bool Aim(object __instance, object[] __args)
	{
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Expected O, but got Unknown
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: 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_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: 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_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_0168: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0256: Unknown result type (might be due to invalid IL or missing references)
		//IL_025a: Unknown result type (might be due to invalid IL or missing references)
		//IL_025f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0261: Unknown result type (might be due to invalid IL or missing references)
		//IL_0268: Unknown result type (might be due to invalid IL or missing references)
		//IL_026d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0272: Unknown result type (might be due to invalid IL or missing references)
		//IL_0276: Unknown result type (might be due to invalid IL or missing references)
		//IL_0280: Unknown result type (might be due to invalid IL or missing references)
		//IL_030e: Unknown result type (might be due to invalid IL or missing references)
		//IL_036e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0370: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a5: Unknown result type (might be due to invalid IL or missing references)
		if (!Server || (__args[4] is bool && (bool)__args[4]))
		{
			return false;
		}
		object obj = __args[0];
		object obj2 = __args[1];
		Player val = (Player)((obj2 is Player) ? obj2 : null);
		object obj3 = Field(__instance, "_boat");
		Boat val2 = (Boat)((obj3 is Boat) ? obj3 : null);
		if (!Object.op_Implicit((Object)(object)val2) || !Object.op_Implicit((Object)(object)val) || ((NetworkBehaviour)val).IsDeinitializing || !((NetworkBehaviour)val).IsServerInitialized || val.Vitals.Health <= 0 || val.Dying.IsDead)
		{
			return false;
		}
		Weapon val3 = (Weapon)Property(Field(obj, "Weapon"), "VisualSource");
		FstPirateRules.FireProfile fireProfile = FstPirateRules.Profile(((Item)val3).ID);
		AimState orCreateValue = aiming.GetOrCreateValue(obj);
		float time = Time.time;
		float num = time - orCreateValue.Updated;
		float num2 = Mathf.Clamp(num, 0f, 0.1f);
		orCreateValue.Updated = time;
		Vector3 val4 = val2.VisualBoat.position + Vector3.up * 1.6f;
		Vector3 val5 = (Vector3)__args[2];
		Vector3 val6 = val5 - val4;
		Vector3 normalized = ((Vector3)(ref val6)).normalized;
		if ((Object)(object)orCreateValue.Target != (Object)(object)val || num > 0.4f)
		{
			orCreateValue.Target = val;
			orCreateValue.Direction = val2.VisualBoat.forward;
			orCreateValue.Phase = 1;
			orCreateValue.Until = time + fireProfile.Prepare;
		}
		if (Vector3.Distance(val4, val5) > fireProfile.Range)
		{
			orCreateValue.Phase = 1;
			orCreateValue.Until = time + fireProfile.Prepare;
			return false;
		}
		orCreateValue.Direction = Vector3.RotateTowards(orCreateValue.Direction, normalized, fireProfile.Tracking * ((float)Math.PI / 180f) * num2, 0f);
		if (time >= orCreateValue.Until)
		{
			orCreateValue.Phase = (byte)((orCreateValue.Phase == 1) ? 2u : ((orCreateValue.Phase != 2) ? 1u : 3u));
			orCreateValue.Until = time + ((orCreateValue.Phase == 1) ? fireProfile.Prepare : ((orCreateValue.Phase == 2) ? fireProfile.Fire : fireProfile.Recovery));
		}
		object obj4 = __args[5];
		Component val7 = (Component)(((obj4 is Component) ? obj4 : null) ?? ((object)/*isinst with value type is only supported in some contexts*/));
		Vector3 val8 = val4 + orCreateValue.Direction * Vector3.Distance(val4, val5);
		__args[2] = val8;
		__args[3] = Vector3.zero;
		if (!Object.op_Implicit((Object)(object)val7) || !(bool)Property(val7, "Alive"))
		{
			return false;
		}
		if (Object.op_Implicit((Object)(object)val7))
		{
			Component component = val7.GetComponent(animator);
			if (Object.op_Implicit((Object)(object)component))
			{
				AccessTools.Method(animator, "SetAimTarget", (Type[])null, (Type[])null).Invoke(component, new object[1] { val8 });
			}
		}
		int num3 = (int)Field(obj, "ShooterIndex");
		Present(val2, num3, orCreateValue.Phase, val8, ((Item)val3).ID);
		if (time >= orCreateValue.SendAt)
		{
			orCreateValue.SendAt = time + 0.15f;
			net.ServerManager.Broadcast<FstPirateState>(new FstPirateState
			{
				Boat = ((NetworkBehaviour)val2).ObjectId,
				Shooter = (byte)num3,
				Phase = orCreateValue.Phase,
				Aim = val8,
				Weapons = new byte[1] { ((Item)val3).ID }
			}, true, (Channel)1);
		}
		if (orCreateValue.Phase == 2)
		{
			return Vector3.Angle(orCreateValue.Direction, normalized) < 14f;
		}
		return false;
	}

	private static bool Fire(object __instance, object[] __args)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: 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)
		if (!Server)
		{
			return false;
		}
		if (aiming.GetOrCreateValue(__args[0]).Phase != 2)
		{
			return false;
		}
		Vector3 val = (Vector3)__args[3];
		Vector3 val2 = (Vector3)__args[4];
		RaycastHit val3 = default(RaycastHit);
		if (!Physics.Linecast(val, val2, ref val3, -1, (QueryTriggerInteraction)1))
		{
			return true;
		}
		Player componentInParent = ((Component)((RaycastHit)(ref val3)).collider).GetComponentInParent<Player>();
		object obj = __args[1];
		return (Object)(object)componentInParent == (Object)((obj is Player) ? obj : null);
	}

	private static void Present(Boat boat, int index, byte phase, Vector3 aim, byte weapon)
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		Component[] array = (from Component c in ((Component)boat).GetComponentsInChildren(health, true)
			where (bool)Property(c, "IsShooter")
			select c).ToArray();
		if (index >= 0 && index < array.Length)
		{
			Component val = array[index];
			Component component = val.GetComponent(animator);
			if (Object.op_Implicit((Object)(object)component))
			{
				AccessTools.Method(animator, "SetAimTarget", (Type[])null, (Type[])null).Invoke(component, new object[1] { aim });
			}
			(val.GetComponent<FstPirateSignal>() ?? val.gameObject.AddComponent<FstPirateSignal>()).Show(phase, weapon);
		}
	}

	private static void SendLoadout(Boat boat)
	{
		if (Server && ((NetworkBehaviour)boat).IsServerInitialized && loadouts.TryGetValue(boat, out var value))
		{
			net.ServerManager.Broadcast<FstPirateState>(new FstPirateState
			{
				Boat = ((NetworkBehaviour)boat).ObjectId,
				Weapons = value,
				Health = (healthBudgets.TryGetValue(boat, out var value2) ? value2 : FstPirateRules.Health(Tier())),
				Phase = byte.MaxValue
			}, true, (Channel)0);
		}
	}

	private static void Receive(FstPirateState state, Channel channel)
	{
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		if (Server || state.Weapons == null || state.Weapons.Length < 1 || state.Weapons.Length > 2 || state.Weapons.Any((byte id) => !new byte[4] { 54, 242, 244, 246 }.Contains(id)))
		{
			return;
		}
		Boat val = ((IEnumerable<Boat>)Object.FindObjectsByType<Boat>((FindObjectsInactive)0)).FirstOrDefault((Func<Boat, bool>)((Boat b) => ((NetworkBehaviour)b).ObjectId == state.Boat));
		if (!Object.op_Implicit((Object)(object)val))
		{
			if (state.Phase == byte.MaxValue && pending.Count < 16)
			{
				pending[state.Boat] = state;
			}
		}
		else if (state.Phase == byte.MaxValue)
		{
			if (state.Health >= 1600 && state.Health <= 5200)
			{
				bool num = !loadouts.ContainsKey(val);
				loadouts[val] = state.Weapons;
				healthBudgets[val] = state.Health;
				if (num)
				{
					AccessTools.Method(support, "CreatePirateVisuals", (Type[])null, (Type[])null).Invoke(null, new object[1] { val });
				}
			}
		}
		else if (state.Phase >= 1 && state.Phase <= 3)
		{
			Present(val, state.Shooter, state.Phase, state.Aim, state.Weapons[0]);
		}
	}

	internal static void Tick(NetworkManager manager)
	{
		if (!installed || failed || !Object.op_Implicit((Object)(object)manager))
		{
			return;
		}
		if ((Object)(object)net != (Object)(object)manager)
		{
			if (Object.op_Implicit((Object)(object)net))
			{
				net.ClientManager.UnregisterBroadcast<FstPirateState>((Action<FstPirateState, Channel>)Receive);
			}
			net = manager;
			GenericWriter<FstPirateState>.SetWrite((Action<Writer, FstPirateState>)delegate(Writer w, FstPirateState p)
			{
				//IL_0032: Unknown result type (might be due to invalid IL or missing references)
				w.WriteInt32(p.Boat);
				w.WriteInt32(p.Health);
				w.WriteUInt8Unpacked(p.Shooter);
				w.WriteUInt8Unpacked(p.Phase);
				w.WriteVector3(p.Aim);
				byte[] weapons = p.Weapons;
				w.WriteUInt8Unpacked((byte)((weapons != null) ? ((uint)weapons.Length) : 0u));
				if (p.Weapons != null)
				{
					byte[] weapons2 = p.Weapons;
					foreach (byte b in weapons2)
					{
						w.WriteUInt8Unpacked(b);
					}
				}
			});
			GenericReader<FstPirateState>.SetRead((Func<Reader, FstPirateState>)delegate(Reader r)
			{
				//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)
				FstPirateState result = new FstPirateState
				{
					Boat = r.ReadInt32(),
					Health = r.ReadInt32(),
					Shooter = r.ReadUInt8Unpacked(),
					Phase = r.ReadUInt8Unpacked(),
					Aim = r.ReadVector3()
				};
				int num2 = r.ReadUInt8Unpacked();
				if (num2 > 2)
				{
					throw new InvalidOperationException("Invalid pirate loadout packet");
				}
				result.Weapons = new byte[num2];
				for (int i = 0; i < num2; i++)
				{
					result.Weapons[i] = r.ReadUInt8Unpacked();
				}
				return result;
			});
			net.ClientManager.RegisterBroadcast<FstPirateState>((Action<FstPirateState, Channel>)Receive);
		}
		if (!net.IsServerStarted && !net.IsClientStarted)
		{
			loadouts.Clear();
			pursuitStarted.Clear();
			healthBudgets.Clear();
			salePrices.Clear();
			pending.Clear();
			aiming = new ConditionalWeakTable<object, AimState>();
			nextEncounter = 0f;
		}
		else
		{
			if (Time.unscaledTime < nextSync)
			{
				return;
			}
			nextSync = Time.unscaledTime + 2f;
			Boat[] array = loadouts.Keys.ToArray();
			foreach (Boat val in array)
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					Remove(val);
				}
				else if (Server)
				{
					SendLoadout(val);
				}
			}
			FstPirateState[] array2 = pending.Values.ToArray();
			for (int num = 0; num < array2.Length; num++)
			{
				FstPirateState state = array2[num];
				pending.Remove(state.Boat);
				Receive(state, (Channel)0);
			}
		}
	}

	internal static void Shutdown()
	{
		if (observing)
		{
			AppDomain.CurrentDomain.AssemblyLoad -= Loaded;
		}
		observing = false;
		if (patch != null)
		{
			patch.UnpatchSelf();
		}
		FstPirateSettings.Shutdown();
		if (Object.op_Implicit((Object)(object)net))
		{
			net.ClientManager.UnregisterBroadcast<FstPirateState>((Action<FstPirateState, Channel>)Receive);
		}
		net = null;
		loadouts.Clear();
		pursuitStarted.Clear();
		healthBudgets.Clear();
		salePrices.Clear();
		pending.Clear();
		installed = false;
	}
}
internal sealed class FstPirateSignal : MonoBehaviour
{
	private Light lamp;

	private Transform rotor;

	private byte phase;

	private float until;

	private AudioSource motor;

	internal void Show(byte value, byte weapon)
	{
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Expected O, but got Unknown
		//IL_0064: 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)
		byte b = phase;
		phase = value;
		until = Time.time + 0.5f;
		if (!Object.op_Implicit((Object)(object)lamp))
		{
			GameObject val = new GameObject("FST firing preparation light");
			val.transform.SetParent(((Component)this).transform, false);
			val.transform.localPosition = new Vector3(0f, 0.8f, 0.25f);
			lamp = val.AddComponent<Light>();
			lamp.type = (LightType)2;
			lamp.range = 2f;
			lamp.color = new Color(1f, 0.42f, 0.08f);
			lamp.shadows = (LightShadows)0;
		}
		if (weapon != 246)
		{
			return;
		}
		if (!Object.op_Implicit((Object)(object)rotor))
		{
			rotor = ((IEnumerable<Transform>)((Component)this).GetComponentsInChildren<Transform>(true)).FirstOrDefault((Func<Transform, bool>)((Transform t) => ((Object)t).name == "Rotor"));
		}
		AudioClip val2 = FstPirateApi.MinigunAudio(0);
		AudioClip val3 = FstPirateApi.MinigunAudio(1);
		AudioClip val4 = FstPirateApi.MinigunAudio(2);
		if (!Object.op_Implicit((Object)(object)val2))
		{
			return;
		}
		if (!Object.op_Implicit((Object)(object)motor))
		{
			motor = ((Component)this).gameObject.AddComponent<AudioSource>();
			motor.spatialBlend = 1f;
			motor.minDistance = 3f;
			motor.maxDistance = 90f;
			motor.volume = 0.32f;
			motor.loop = true;
			motor.clip = val2;
		}
		if (b == phase)
		{
			return;
		}
		if (phase == 1)
		{
			if (Object.op_Implicit((Object)(object)val3))
			{
				motor.PlayOneShot(val3);
			}
			motor.Play();
		}
		else if (phase == 3)
		{
			motor.Stop();
			if (Object.op_Implicit((Object)(object)val4))
			{
				motor.PlayOneShot(val4);
			}
		}
	}

	private void Update()
	{
		bool flag = phase == 1 && Time.time < until;
		if (Object.op_Implicit((Object)(object)lamp))
		{
			lamp.intensity = (flag ? 1.3f : 0f);
		}
		if (Object.op_Implicit((Object)(object)rotor) && Time.time < until && (phase == 1 || phase == 2))
		{
			rotor.Rotate(0f, 0f, (float)((phase == 1) ? 700 : 1800) * Time.deltaTime, (Space)1);
		}
		if (Object.op_Implicit((Object)(object)motor))
		{
			motor.pitch = ((phase == 1) ? 0.65f : 1f);
			if (Time.time >= until && motor.isPlaying)
			{
				motor.Stop();
			}
		}
	}
}
internal static class FstPirateRules
{
	internal struct FireProfile
	{
		internal int Damage;

		internal int Magazine;

		internal float Interval;

		internal float Spread;

		internal float Range;

		internal float Prepare;

		internal float Fire;

		internal float Recovery;

		internal float Tracking;

		internal FireProfile(int damage, int magazine, float interval, float spread, float range, float prepare, float fire, float recovery, float tracking)
		{
			Damage = damage;
			Magazine = magazine;
			Interval = interval;
			Spread = spread;
			Range = range;
			Prepare = prepare;
			Fire = fire;
			Recovery = recovery;
			Tracking = tracking;
		}
	}

	internal static int Health(int tier)
	{
		if (tier < 9)
		{
			if (tier < 8)
			{
				if (tier < 7)
				{
					return 1600;
				}
				return 2400;
			}
			return 3600;
		}
		return 5200;
	}

	internal static int SaleWorth(int baseline)
	{
		if (baseline > 0)
		{
			return (int)Math.Min(2147483647L, (long)baseline * 3L / 2);
		}
		return 0;
	}

	internal static byte[] Pool(int tier)
	{
		if (tier < 9)
		{
			if (tier >= 8)
			{
				return new byte[2] { 244, 242 };
			}
			return new byte[2] { 242, 54 };
		}
		return new byte[2] { 246, 244 };
	}

	internal static FireProfile Profile(byte id)
	{
		return id switch
		{
			242 => new FireProfile(26, 12, 0.15f, 3.8f, 62f, 0.65f, 0.9f, 1.6f, 48f), 
			244 => new FireProfile(32, 24, 0.13f, 4.2f, 72f, 0.95f, 2.1f, 2.7f, 32f), 
			246 => new FireProfile(24, 48, 0.08f, 4.8f, 78f, 1.6f, 3.2f, 4.2f, 22f), 
			_ => new FireProfile(22, 10, 0.18f, 3.4f, 58f, 0.55f, 0.75f, 1.5f, 52f), 
		};
	}
}
internal static class FstPirateSettings
{
	private static readonly Dictionary<ConfigEntryBase, object> enforced = new Dictionary<ConfigEntryBase, object>();

	internal static void Configure(object config, bool enabled)
	{
		foreach (KeyValuePair<string, object> item in new Dictionary<string, object>
		{
			{ "PiratesEnabled", enabled },
			{ "DistanceBeyondIsland", 110f },
			{ "DifficultyMultiplier", 0.5f },
			{ "MaximumActiveBoats", 2 },
			{ "BoundaryMarkersEnabled", false }
		})
		{
			PropertyInfo propertyInfo = AccessTools.Property(config.GetType(), item.Key);
			Enforce((ConfigEntryBase)(((propertyInfo == null) ? null : /*isinst with value type is only supported in some contexts*/) ?? throw new InvalidOperationException("Unsupported Behold setting: " + item.Key)), item.Value);
		}
	}

	internal static void ConfigureBoundary(object boundary)
	{
		FieldInfo fieldInfo = AccessTools.Field(boundary.GetType(), "_distanceBeyondIsland");
		Enforce((ConfigEntryBase)(object)(((fieldInfo == null) ? null : (fieldInfo.GetValue(boundary) as ConfigEntry<float>)) ?? throw new InvalidOperationException("Unsupported Behold boundary distance setting")), 110f);
	}

	private static void Enforce(ConfigEntryBase entry, object value)
	{
		if (!enforced.ContainsKey(entry))
		{
			((object)entry).GetType().GetEvent("SettingChanged").AddEventHandler(entry, new EventHandler(Changed));
		}
		enforced[entry] = value;
		entry.BoxedValue = value;
	}

	private static void Changed(object sender, EventArgs args)
	{
		ConfigEntryBase val = (ConfigEntryBase)((sender is ConfigEntryBase) ? sender : null);
		if (val != null && enforced.TryGetValue(val, out var value) && !object.Equals(val.BoxedValue, value))
		{
			val.BoxedValue = value;
		}
	}

	internal static void Shutdown()
	{
		foreach (ConfigEntryBase key in enforced.Keys)
		{
			((object)key).GetType().GetEvent("SettingChanged").RemoveEventHandler(key, new EventHandler(Changed));
		}
		enforced.Clear();
	}
}
[BepInPlugin("fst.htf.dlc.behold.addon", "HTF DLC FST - Behold Pirates Addon", "0.1.3")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class Plugin : BaseUnityPlugin
{
	private bool active;

	private bool reported;

	private float nextCheck;

	private void Awake()
	{
		TryActivate();
	}

	private void TryActivate()
	{
		if (!Chainloader.PluginInfos.TryGetValue("behold.howtofish.pirates", out var value) || !Object.op_Implicit((Object)(object)value.Instance) || !((Behaviour)value.Instance).isActiveAndEnabled)
		{
			ReportInactive();
			return;
		}
		if (value.Metadata.Version != new Version(0, 14, 1) || !Harmony.HasAnyPatches("fst.behold.startup.compatibility"))
		{
			if (!reported)
			{
				reported = true;
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Behold 0.14.1 and the addon's startup compatibility patcher are required; pirate integration inactive.");
			}
			return;
		}
		Type type = ((object)value.Instance).GetType();
		string[] array = new string[3] { "_settings", "_openSeaBoundary", "_pirateEncounterController" };
		foreach (string text in array)
		{
			FieldInfo fieldInfo = AccessTools.Field(type, text);
			if (fieldInfo == null || fieldInfo.GetValue(value.Instance) == null)
			{
				ReportInactive();
				return;
			}
		}
		try
		{
			if (FstPirateIntegration.Install())
			{
				active = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Optional Behold integration activated after framework initialization");
			}
		}
		catch (Exception ex)
		{
			FstPirateIntegration.Shutdown();
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Optional Behold integration remains disabled: " + ex.Message));
		}
	}

	private void ReportInactive()
	{
		if (!reported)
		{
			reported = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Behold is absent or not initialized; pirate addon inactive. Core FST is independent.");
		}
	}

	private void Update()
	{
		if (!active)
		{
			if (Time.unscaledTime >= nextCheck)
			{
				nextCheck = Time.unscaledTime + 2f;
				TryActivate();
			}
		}
		else if (NetworkManager.Instances.Count == 1)
		{
			FstPirateIntegration.Tick(NetworkManager.Instances[0]);
		}
	}

	private void OnDestroy()
	{
		FstPirateIntegration.Shutdown();
		active = false;
	}
}