using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using Arachnophilia.Patches;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CSync.Extensions;
using CSync.Lib;
using HarmonyLib;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.AI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("Arachnophilia")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Arachnophilia")]
[assembly: AssemblyCopyright("Copyright © 2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("f42920e9-4be4-48ee-a6f2-6b7ca9c992d0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace Arachnophilia
{
[BepInPlugin("impulse.Arachnophilia", "Arachnophilia", "1.8.1")]
public class Arachnophilia : BaseUnityPlugin
{
private const string modGUID = "impulse.Arachnophilia";
private const string modName = "Arachnophilia";
private const string modVersion = "1.8.1";
private readonly Harmony harmony = new Harmony("impulse.Arachnophilia");
public ManualLogSource mls;
public static Arachnophilia instance;
public static SyncConfig Config;
private void Awake()
{
instance = this;
Config = new SyncConfig(((BaseUnityPlugin)this).Config);
harmony.PatchAll(typeof(SandSpiderStartPatch));
harmony.PatchAll(typeof(SandSpiderAttemptPlaceWebTrapPatch));
harmony.PatchAll(typeof(SandSpiderState));
harmony.PatchAll(typeof(SandSpiderUpdatePatch));
harmony.PatchAll(typeof(SandSpiderUpdateTPatch));
harmony.PatchAll(typeof(SandSpiderOnCollideWithPlayerPatch));
harmony.PatchAll(typeof(SandSpiderGetWallPositionForSpiderMeshPatch));
harmony.PatchAll(typeof(SandSpiderAIGrabBodyPatch));
mls = Logger.CreateLogSource("impulse.Arachnophilia");
}
}
[DataContract]
public class SyncConfig : SyncedConfig<SyncConfig>
{
[DataMember]
public SyncedEntry<int> MinWebCount { get; private set; }
[DataMember]
public SyncedEntry<int> MaxWebCount { get; private set; }
[DataMember]
public SyncedEntry<float> MinWebLength { get; private set; }
[DataMember]
public SyncedEntry<float> MaxWebLength { get; private set; }
[DataMember]
public SyncedEntry<float> MinWebDistance { get; private set; }
[DataMember]
public SyncedEntry<float> SpiderSetupSpeed { get; private set; }
[DataMember]
public SyncedEntry<float> SpiderChaseSpeed { get; private set; }
[DataMember]
public SyncedEntry<float> WebTimeRange { get; private set; }
[DataMember]
public SyncedEntry<float> FailedWebTrapTime { get; private set; }
[DataMember]
public SyncedEntry<float> WebPlaceInterval { get; private set; }
[DataMember]
public SyncedEntry<int> SpiderDamage { get; private set; }
[DataMember]
public SyncedEntry<int> SpiderHp { get; private set; }
[DataMember]
public SyncedEntry<int> MinSpooledBodies { get; private set; }
[DataMember]
public SyncedEntry<int> MaxSpooledBodies { get; private set; }
[DataMember]
public SyncedEntry<float> MinWallHeight { get; private set; }
[DataMember]
public SyncedEntry<float> MaxWallHeight { get; private set; }
[DataMember]
public SyncedEntry<float> FloorCheck { get; private set; }
public SyncConfig(ConfigFile cfg)
: base("Arachnophilia")
{
ConfigManager.Register<SyncConfig>(this);
SpiderSetupSpeed = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "1.General", "Spider Setup Speed", 1.5f, "Speed multiplier applied to the spider while it is not chasing a player.");
SpiderChaseSpeed = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "1.General", "Spider Chase Speed", 1.25f, "Speed multiplier applied to the spider while it is chasing a player.");
SpiderDamage = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "1.General", "Spider Damage", 35, "The damage dealt to players by the spider.");
SpiderHp = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "1.General", "Spider HP", 6, "Health of the spider.");
MinSpooledBodies = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "4.Cocoons", "Min Extra Cocoons", 3, "The minimum number of bodies the spider spawns in and decorates its nest with during its setup phase. Set both min and max to 0 to disable. Keep in mind that this value should be higher than Min Web Count, excessively high values may result in lag, Min should be less than Max.");
MaxSpooledBodies = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "4.Cocoons", "Max Extra Cocoons", 4, "The maximum number of bodies the spider spawns in and decorates its nest with during its setup phase. Set both min and max to 0 to disable. Keep in mind that this value should be higher than Min Web Count, excessively high values may result in lag, Min should be less than Max.");
MinWebCount = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "2.Webs", "Min Web Count", 20, "The minimum amount of webs a given spider can place, Min should be less than Max.");
MaxWebCount = SyncedBindingExtensions.BindSyncedEntry<int>(cfg, "2.Webs", "Max Web Count", 25, "The maximum amount of webs a given spider can place, Min should be less than Max.");
MinWebLength = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "2.Webs", "Min Web Length", 2f, "The minimum length a web can be in units, !! Keep in mind that the smaller the difference between the min and max length, the less spots a spider could successfully place a web!!");
MaxWebLength = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "2.Webs", "Max Web Length", 10f, "The maximum length a web can be in units, !! Keep in mind that the smaller the differnce between the min and max length, the less spots a spider could successfully place a web!!");
MinWebDistance = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "2.Webs", "Min Distance Between Webs", 0.5f, "This controls the distance in units a spider must be from the closest web in order to place a new one, a lower value will make webs closely knit, a higher value will make webs spaced further apart.");
WebPlaceInterval = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "2.Webs", "Web Placement Interval", 2f, "How long in seconds the spider should wait after placing a web to place another");
WebTimeRange = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "2.Webs", "Web Placement Interval Variation", 0.5f, "This increases the interval and gives it some variation, the Web Placement Interval plus this value will be the max interval, the min interval will be the Web Placement Interval plus half of this value. (2 & 0.5 for these settings will result in a 2.25-2.5 second interval, 10 & 7 will result in a 13.5-17 second interval. etc)");
FailedWebTrapTime = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "2.Webs", "Failed Web Placement Cooldown", 0.1f, "How long in seconds spider should wait after failing to place a web before attempting to place another.");
MinWallHeight = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "3.Walls", "Min Wall Height", 2f, "This value more or less controls the absolute smallest vertical surface that the spider can consider to be a wall. Keep in mind that if this value is too high, the spider will be unable to find valid walls and its ai will break. If it is too low then the spider will try to climb small props like chairs. Must be larger than 1.3.");
MaxWallHeight = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "3.Walls", "Max Wall Height", 22f, "This value more or less controls the absolute tallest vertical surface that the spider can consider to be a wall. Min should be less than Max.");
FloorCheck = SyncedBindingExtensions.BindSyncedEntry<float>(cfg, "3.Walls", "Floor check", 12f, "When the spider finds a possible wall, it will check if there is a floor below it. If there is no floor within this value below the wall position, the spider will not climb the wall.");
}
}
}
namespace Arachnophilia.Patches
{
[HarmonyPatch(typeof(SandSpiderAI), "Start")]
internal class SandSpiderStartPatch
{
private static void Postfix(SandSpiderAI __instance)
{
__instance.maxWebTrapsToPlace = Random.Range(SyncedInstance<SyncConfig>.Instance.MinWebCount.Value - 1 - SyncedInstance<SyncConfig>.Instance.MinSpooledBodies.Value / 3, SyncedInstance<SyncConfig>.Instance.MaxWebCount.Value - SyncedInstance<SyncConfig>.Instance.MaxSpooledBodies.Value / 2);
((EnemyAI)__instance).enemyHP = SyncedInstance<SyncConfig>.Instance.SpiderHp.Value;
}
}
[HarmonyPatch(typeof(SandSpiderAI), "AttemptPlaceWebTrap")]
internal class SandSpiderAttemptPlaceWebTrapPatch : GrabbableObject
{
private static bool Prefix(SandSpiderAI __instance)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_009d: 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_00a6: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
//IL_0141: Unknown result type (might be due to invalid IL or missing references)
//IL_0164: 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_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)
//IL_019a: 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_01a9: Unknown result type (might be due to invalid IL or missing references)
//IL_01ae: 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_01b6: 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)
for (int i = 0; i < __instance.webTraps.Count; i++)
{
if (Vector3.Distance(((Component)__instance.webTraps[i]).transform.position, __instance.abdomen.position) < SyncedInstance<SyncConfig>.Instance.MinWebDistance.Value)
{
return false;
}
}
float num = Random.Range(-1f, 1f);
float num2 = Random.Range(-1f, 1f);
float num3 = Random.Range(0f, 1f);
Vector3 val = new Vector3(num, num3, num2);
Vector3 normalized = ((Vector3)(ref val)).normalized;
Ray val2 = default(Ray);
((Ray)(ref val2))..ctor(__instance.abdomen.position + Vector3.up * 0.4f, normalized);
RaycastHit val3 = default(RaycastHit);
if (Physics.Raycast(val2, ref val3, SyncedInstance<SyncConfig>.Instance.MaxWebLength.Value, StartOfRound.Instance.collidersAndRoomMask))
{
if (((RaycastHit)(ref val3)).distance < SyncedInstance<SyncConfig>.Instance.MinWebLength.Value)
{
return false;
}
Arachnophilia.instance.mls.LogInfo((object)$"Current time: {DateTime.Now} - Got spider web raycast; end point: {((RaycastHit)(ref val3)).point}; {((RaycastHit)(ref val3)).distance}");
Vector3 point = ((RaycastHit)(ref val3)).point;
if (Physics.Raycast(__instance.abdomen.position, Vector3.down, ref val3, 10f, StartOfRound.Instance.collidersAndRoomMask))
{
Vector3 val4 = ((RaycastHit)(ref val3)).point + Vector3.up * 0.2f;
__instance.SpawnWebTrapServerRpc(val4, point);
}
}
return false;
}
}
internal class SandSpiderState
{
public Dictionary<SandSpiderAI, int> bodyCounts = new Dictionary<SandSpiderAI, int>();
public Dictionary<SandSpiderAI, bool> coroutineStartedFlags = new Dictionary<SandSpiderAI, bool>();
public Dictionary<SandSpiderAI, int> randomSpooledBodies = new Dictionary<SandSpiderAI, int>();
}
[HarmonyPatch(typeof(SandSpiderAI), "Update")]
internal class SandSpiderUpdatePatch
{
private static SandSpiderState state = new SandSpiderState();
private static void Postfix(SandSpiderAI __instance)
{
lock (state.bodyCounts)
{
lock (state.coroutineStartedFlags)
{
if (!state.bodyCounts.ContainsKey(__instance))
{
state.bodyCounts[__instance] = 0;
state.coroutineStartedFlags[__instance] = false;
state.randomSpooledBodies[__instance] = Random.Range(SyncedInstance<SyncConfig>.Instance.MinSpooledBodies.Value, SyncedInstance<SyncConfig>.Instance.MaxSpooledBodies.Value + 1);
}
}
}
int num = state.randomSpooledBodies[__instance];
float value = SyncedInstance<SyncConfig>.Instance.SpiderSetupSpeed.Value;
float value2 = SyncedInstance<SyncConfig>.Instance.SpiderChaseSpeed.Value;
switch (((EnemyAI)__instance).currentBehaviourStateIndex)
{
case 0:
{
NavMeshAgent agent2 = ((EnemyAI)__instance).agent;
agent2.speed *= value;
__instance.spiderSpeed *= value;
lock (state.coroutineStartedFlags)
{
state.coroutineStartedFlags[__instance] = false;
break;
}
}
case 1:
{
NavMeshAgent agent3 = ((EnemyAI)__instance).agent;
agent3.speed *= value;
__instance.spiderSpeed *= value;
lock (state.bodyCounts)
{
lock (state.coroutineStartedFlags)
{
if (state.bodyCounts[__instance] < num && !state.coroutineStartedFlags[__instance])
{
((MonoBehaviour)__instance).StartCoroutine(InstantiateRagdoll(__instance));
state.coroutineStartedFlags[__instance] = true;
}
break;
}
}
}
case 2:
{
NavMeshAgent agent = ((EnemyAI)__instance).agent;
agent.speed *= value2;
__instance.spiderSpeed *= value2;
if ((Object)(object)__instance.currentlyHeldBody != (Object)null)
{
__instance.CancelSpoolingBody();
}
lock (state.coroutineStartedFlags)
{
state.coroutineStartedFlags[__instance] = false;
break;
}
}
}
}
private static IEnumerator InstantiateRagdoll(SandSpiderAI __instance)
{
while (((EnemyAI)__instance).isOutside)
{
yield return null;
}
while (((EnemyAI)__instance).currentBehaviourStateIndex == 2)
{
yield return null;
}
while ((Object)(object)__instance.currentlyHeldBody != (Object)null)
{
yield return null;
}
yield return (object)new WaitForSeconds(3f);
if (((EnemyAI)__instance).isEnemyDead)
{
yield break;
}
GameObject ragdollPrefab = StartOfRound.Instance.playerRagdolls[0];
GameObject ragdoll = Object.Instantiate<GameObject>(ragdollPrefab, __instance.homeNode.position, Quaternion.identity);
DeadBodyInfo deadBodyInfo = ragdoll.GetComponent<DeadBodyInfo>();
ragdoll.AddComponent<NetworkObject>();
Transform bodyScanNode = ragdoll.transform.Find("ScanNode");
if ((Object)(object)bodyScanNode != (Object)null)
{
((Component)bodyScanNode).gameObject.SetActive(false);
}
Transform bodyMapDot = ragdoll.transform.Find("MapDot");
if ((Object)(object)bodyMapDot != (Object)null)
{
((Component)bodyMapDot).gameObject.SetActive(false);
}
BoxCollider bodyBoxCollider = ragdoll.GetComponent<BoxCollider>();
if ((Object)(object)bodyBoxCollider != (Object)null)
{
((Collider)bodyBoxCollider).enabled = false;
}
__instance.spooledPlayerBody = false;
__instance.decidedChanceToHangBodyEarly = false;
__instance.turnBodyIntoWebCoroutine = null;
if ((Object)(object)__instance.currentlyHeldBody != (Object)null)
{
__instance.SpiderTurnBodyIntoWebServerRpc();
}
if ((Object)(object)__instance.currentlyHeldBody != (Object)null)
{
__instance.SpiderHangBodyServerRpc();
}
__instance.GrabBody(deadBodyInfo);
Arachnophilia.instance.mls.LogInfo((object)$"Current time: {DateTime.Now} - Spoolable prey instantiated");
lock (state.bodyCounts)
{
lock (state.coroutineStartedFlags)
{
state.bodyCounts[__instance]++;
state.coroutineStartedFlags[__instance] = false;
}
}
if (!__instance.spoolingPlayerBody)
{
((EnemyAI)__instance).currentBehaviourStateIndex = 0;
}
}
}
[HarmonyPatch(typeof(SandSpiderAI), "Update")]
public static class SandSpiderUpdateTPatch
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
for (int i = 0; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Ldc_R4)
{
float num = (float)list[i].operand;
if (num == 4f)
{
list[i].operand = SyncedInstance<SyncConfig>.Instance.WebPlaceInterval.Value;
}
else if (num == 0.5f)
{
list[i].operand = SyncedInstance<SyncConfig>.Instance.WebTimeRange.Value / 2f;
}
else if (num == 1f)
{
list[i].operand = SyncedInstance<SyncConfig>.Instance.WebTimeRange.Value;
}
else if (num == 0.17f)
{
list[i].operand = SyncedInstance<SyncConfig>.Instance.FailedWebTrapTime.Value;
}
}
}
return list;
}
}
[HarmonyPatch(typeof(SandSpiderAI), "OnCollideWithPlayer")]
public static class SandSpiderOnCollideWithPlayerPatch
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
for (int i = 0; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Ldc_I4_S && (sbyte)list[i].operand == 90)
{
list[i].operand = (sbyte)SyncedInstance<SyncConfig>.Instance.SpiderDamage.Value;
}
}
return list.AsEnumerable();
}
}
[HarmonyPatch(typeof(SandSpiderAI), "GetWallPositionForSpiderMesh")]
public static class SandSpiderGetWallPositionForSpiderMeshPatch
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
for (int i = 0; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Ldc_R4)
{
float num = (float)list[i].operand;
if (num == 6f)
{
list[i].operand = SyncedInstance<SyncConfig>.Instance.MinWallHeight.Value;
}
else if (num == 22f)
{
list[i].operand = SyncedInstance<SyncConfig>.Instance.MaxWallHeight.Value;
}
else if (num == 10f)
{
list[i].operand = 100f;
}
else if (num == 10.1f)
{
list[i].operand = 100.1f;
}
else if (num == 7f)
{
list[i].operand = SyncedInstance<SyncConfig>.Instance.FloorCheck.Value;
}
}
}
return list.AsEnumerable();
}
}
[HarmonyPatch(typeof(SandSpiderAI), "GrabBody")]
public static class SandSpiderAIGrabBodyPatch
{
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Expected O, but got Unknown
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
for (int i = 0; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Call && (MethodInfo)list[i].operand == AccessTools.Method(typeof(SandSpiderAI), "GrabBody", (Type[])null, (Type[])null))
{
list.Insert(i, new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(SandSpiderAIGrabBodyPatch), "ModifiedGrabBody", (Type[])null, (Type[])null)));
break;
}
}
return list.AsEnumerable();
}
public static void ModifiedGrabBody(SandSpiderAI __instance, DeadBodyInfo body)
{
if (!((EnemyAI)__instance).isOutside)
{
__instance.currentlyHeldBody = body;
__instance.currentlyHeldBody.attachedLimb = __instance.currentlyHeldBody.bodyParts[6];
__instance.currentlyHeldBody.attachedTo = __instance.mouthTarget;
__instance.currentlyHeldBody.matchPositionExactly = true;
}
}
}
}