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;
}
}