using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using STFixes.Patches;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("Fixes Spike Traps")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SpikeTrapFixes")]
[assembly: AssemblyCopyright("Spookybuddy 2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("28f2f16b-6471-4c7e-b0a4-007e3a2450d4")]
[assembly: AssemblyFileVersion("1.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.1.0.0")]
namespace STFixes
{
internal class ConfigControl : SyncedInstance<ConfigControl>
{
public ConfigEntry<bool> cfgModEnabled;
public ConfigEntry<bool> cfgSTEnabled;
public ConfigEntry<STFixModBase.EnumOptions> cfgTypes;
public ConfigEntry<bool> cfgClamp;
public ConfigEntry<float> cfgMin;
public ConfigEntry<float> cfgMax;
public ConfigEntry<bool> cfgScan;
public ConfigEntry<bool> cfgMove;
public ConfigEntry<int> cfgRange;
internal bool Mod
{
get
{
if (cfgModEnabled.Value)
{
return true;
}
return false;
}
set
{
cfgModEnabled.Value = value;
}
}
internal bool Traps
{
get
{
if (cfgSTEnabled.Value)
{
return true;
}
return false;
}
set
{
cfgSTEnabled.Value = value;
}
}
internal STFixModBase.EnumOptions Types
{
get
{
if (cfgTypes == null)
{
return (STFixModBase.EnumOptions)((ConfigEntryBase)cfgTypes).DefaultValue;
}
return cfgTypes.Value;
}
set
{
cfgTypes.Value = value;
}
}
internal bool Clamp
{
get
{
if (cfgClamp.Value)
{
return true;
}
return false;
}
set
{
cfgClamp.Value = value;
}
}
internal float Minimum
{
get
{
if (cfgMin.Value > 0.7f)
{
return cfgMin.Value;
}
return (float)((ConfigEntryBase)cfgMin).DefaultValue;
}
set
{
cfgMin.Value = value;
}
}
internal float Maximum
{
get
{
if (cfgMax.Value > cfgMin.Value)
{
return cfgMax.Value;
}
return (float)((ConfigEntryBase)cfgMax).DefaultValue;
}
set
{
cfgMax.Value = value;
}
}
internal bool Scans
{
get
{
if (cfgScan.Value)
{
return true;
}
return false;
}
set
{
cfgScan.Value = value;
}
}
internal bool Move
{
get
{
if (cfgMove.Value)
{
return true;
}
return false;
}
set
{
cfgMove.Value = value;
}
}
internal int ScanRange
{
get
{
if (cfgRange.Value > 1)
{
return cfgRange.Value;
}
return (int)((ConfigEntryBase)cfgRange).DefaultValue;
}
set
{
cfgRange.Value = value;
}
}
public ConfigControl(ConfigFile cfg)
{
InitInstance(this);
cfgModEnabled = cfg.Bind<bool>("Enabled", "Mod Enabled", true, "Turn the mod on.");
cfgSTEnabled = cfg.Bind<bool>("Enabled", "Traps Enabled", true, "When disabled turns the spike traps into harmless decor.");
cfgTypes = cfg.Bind<STFixModBase.EnumOptions>("Enabled", "Trap types", STFixModBase.EnumOptions.Both, "What types of traps should spawn.");
cfgClamp = cfg.Bind<bool>("Clamp", "Enabled", true, "Enable the slam rate limits listed below. \n\nWhen disabled the spike traps will use the values they spawned with.");
cfgMin = cfg.Bind<float>("Clamp", "Minimum", 1.5f, "The fastest slam interval. \n\nBase game's quickest possible is 0.71 sec.");
cfgMax = cfg.Bind<float>("Clamp", "Maximum", 25f, "The slowest slam interval. \n\nBase game's slowest possible is 26.15 sec.");
cfgScan = cfg.Bind<bool>("Scan Node", "Enabled", true, "Add a scanable node to the spike traps, similar to landmines and turrets. \n\nBase game does not have a scan node.");
cfgMove = cfg.Bind<bool>("Scan Node", "Move with spikes", true, "The scan node will move with the spikes. Otherwise it will remain in a fixed position.");
cfgRange = cfg.Bind<int>("Scan Node", "Range", 9, "The max distance the scan node can be scanned.");
}
}
[BepInPlugin("SpikeTrapFixes", "SpikeTrapFixes", "1.1.2")]
public class STFixModBase : BaseUnityPlugin
{
internal enum EnumOptions
{
IntervalOnly,
DetectionOnly,
Both
}
public const string modGUID = "SpikeTrapFixes";
private const string modName = "SpikeTrapFixes";
private const string modVersion = "1.1.2";
private readonly Harmony harmony = new Harmony("SpikeTrapFixes");
internal ConfigControl Configuration;
internal static STFixModBase Instance;
internal static ManualLogSource mls;
private void Awake()
{
if ((Object)(object)Instance == (Object)null)
{
Instance = this;
}
mls = Logger.CreateLogSource("SpikeTrapFixes");
Configuration = new ConfigControl(((BaseUnityPlugin)this).Config);
harmony.PatchAll(typeof(STFixModBase));
harmony.PatchAll(typeof(SpikeRoofTrapPatch));
harmony.PatchAll(typeof(ConfigControl));
mls.LogInfo((object)"Spike Trap Fixes was loaded.");
}
}
[Serializable]
public class SyncedInstance<T>
{
[NonSerialized]
protected static int IntSize = 4;
internal static CustomMessagingManager MessageManager => NetworkManager.Singleton.CustomMessagingManager;
internal static bool IsClient => NetworkManager.Singleton.IsClient;
internal static bool IsHost => NetworkManager.Singleton.IsHost;
public static T Default { get; private set; }
public static T Instance { get; private set; }
public static bool Synced { get; internal set; }
protected void InitInstance(T instance)
{
Default = instance;
Instance = instance;
IntSize = 4;
}
internal static void SyncInstance(byte[] data)
{
Instance = DeserializeFromBytes(data);
Synced = true;
}
internal static void RevertSync()
{
Instance = Default;
Synced = false;
}
public static byte[] SerializeToBytes(T val)
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
MemoryStream memoryStream = new MemoryStream();
try
{
binaryFormatter.Serialize(memoryStream, val);
return memoryStream.ToArray();
}
catch (Exception arg)
{
STFixModBase.mls.LogError((object)$"Error serializing instance: {arg}");
return null;
}
}
public static T DeserializeFromBytes(byte[] data)
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
MemoryStream serializationStream = new MemoryStream(data);
try
{
return (T)binaryFormatter.Deserialize(serializationStream);
}
catch (Exception arg)
{
STFixModBase.mls.LogError((object)$"Error deserializing instance: {arg}");
return default(T);
}
}
public static void RequestSync()
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
if (IsClient)
{
FastBufferWriter val = default(FastBufferWriter);
((FastBufferWriter)(ref val))..ctor(IntSize, (Allocator)2, -1);
MessageManager.SendNamedMessage("SpikeTrapFixes_OnRequestConfigSync", 0uL, val, (NetworkDelivery)3);
}
}
public static void OnRequestSync(ulong clientId, FastBufferReader _)
{
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
if (!IsHost)
{
return;
}
STFixModBase.mls.LogInfo((object)$"Config sync request received from client: {clientId}");
byte[] array = SerializeToBytes(Instance);
int num = array.Length;
FastBufferWriter val = default(FastBufferWriter);
((FastBufferWriter)(ref val))..ctor(num + IntSize, (Allocator)2, -1);
try
{
((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives));
((FastBufferWriter)(ref val)).WriteBytesSafe(array, -1, 0);
MessageManager.SendNamedMessage("SpikeTrapFixes_OnReceiveConfigSync", clientId, val, (NetworkDelivery)3);
}
catch (Exception arg)
{
STFixModBase.mls.LogInfo((object)$"Error occurred syncing config with client: {clientId}\n{arg}");
}
}
public static void OnReceiveSync(ulong _, FastBufferReader reader)
{
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
if (!((FastBufferReader)(ref reader)).TryBeginRead(IntSize))
{
STFixModBase.mls.LogError((object)"Config sync error: Could not begin reading buffer.");
return;
}
int num = default(int);
((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref num, default(ForPrimitives));
if (!((FastBufferReader)(ref reader)).TryBeginRead(num))
{
STFixModBase.mls.LogError((object)"Config sync error: Host could not sync.");
return;
}
byte[] data = new byte[num];
((FastBufferReader)(ref reader)).ReadBytesSafe(ref data, num, 0);
SyncInstance(data);
STFixModBase.mls.LogInfo((object)"Successfully synced config with host.");
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameNetworkManager), "SteamMatchmaking_OnLobbyMemberJoined")]
public static void InitializeLocalPlayer()
{
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Expected O, but got Unknown
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Expected O, but got Unknown
if (IsHost)
{
MessageManager.RegisterNamedMessageHandler("SpikeTrapFixes_OnRequestConfigSync", new HandleNamedMessageDelegate(OnRequestSync));
Synced = true;
}
else
{
Synced = false;
MessageManager.RegisterNamedMessageHandler("SpikeTrapFixes_OnReceiveConfigSync", new HandleNamedMessageDelegate(OnReceiveSync));
RequestSync();
}
}
[HarmonyPostfix]
[HarmonyPatch(typeof(GameNetworkManager), "StartDisconnect")]
public static void PlayerLeave()
{
SyncedInstance<ConfigControl>.RevertSync();
}
}
}
namespace STFixes.Patches
{
[HarmonyPatch(typeof(SpikeRoofTrap))]
internal class SpikeRoofTrapPatch
{
internal static readonly Vector3 moving = new Vector3(-0.48f, 0f, -0.378f);
internal static readonly Vector3 still = new Vector3(0f, 3f, 1.8f);
[HarmonyPatch("Start")]
[HarmonyPrefix]
private static void BeginningFix(SpikeRoofTrap __instance)
{
ConfigControl instance = SyncedInstance<ConfigControl>.Instance;
if (!instance.Mod)
{
STFixModBase.mls.LogWarning((object)"Mod disabled.");
}
else if (!instance.Traps)
{
Object.Destroy((Object)(object)__instance);
STFixModBase.mls.LogInfo((object)"Removed script from spike trap.");
}
}
[HarmonyPatch("Start")]
[HarmonyPostfix]
private static void StartFix(SpikeRoofTrap __instance, ref float ___slamInterval, ref bool ___slamOnIntervals)
{
//IL_0202: Unknown result type (might be due to invalid IL or missing references)
//IL_01db: Unknown result type (might be due to invalid IL or missing references)
ConfigControl instance = SyncedInstance<ConfigControl>.Instance;
if (!instance.Mod)
{
return;
}
if (!instance.Traps)
{
Object.Destroy((Object)(object)__instance);
return;
}
if (!instance.Types.Equals(STFixModBase.EnumOptions.Both))
{
STFixModBase.mls.LogInfo((object)("Changing trap to " + instance.Types));
___slamOnIntervals = instance.Types.Equals(STFixModBase.EnumOptions.IntervalOnly);
}
if (instance.Clamp && !instance.Types.Equals(STFixModBase.EnumOptions.DetectionOnly))
{
if (___slamInterval < instance.Minimum)
{
STFixModBase.mls.LogInfo((object)("Raised interval from " + ___slamInterval + "sec to " + instance.Minimum + "sec."));
}
if (___slamInterval > instance.Maximum)
{
STFixModBase.mls.LogInfo((object)("Lowered interval from " + ___slamInterval + "sec to " + instance.Maximum + "sec."));
}
___slamInterval = Mathf.Clamp(___slamInterval, instance.Minimum, instance.Maximum);
}
if (instance.Scans)
{
GameObject val = GameObject.CreatePrimitive((PrimitiveType)3);
if (instance.Move)
{
val.transform.parent = __instance.stickingPointsContainer;
val.transform.localPosition = moving;
}
else
{
val.transform.parent = __instance.laserEye;
val.transform.localPosition = still;
}
val.tag = "DoNotSet";
val.layer = 22;
Object.Destroy((Object)(object)val.GetComponent<MeshFilter>());
Object.Destroy((Object)(object)val.GetComponent<MeshRenderer>());
ScanNodeProperties val2 = val.AddComponent<ScanNodeProperties>();
val2.maxRange = instance.ScanRange;
val2.minRange = 1;
val2.requiresLineOfSight = true;
val2.headerText = "Spike Trap";
val2.subText = "";
val2.scrapValue = 0;
val2.creatureScanID = -1;
val2.nodeType = 1;
STFixModBase.mls.LogInfo((object)"Added scan node to spike trap.");
}
}
}
}