Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LCFairCompany v2.1.0
LCFairCompany.dll
Decompiled 2 years agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters.Binary; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LCFairCompany.Configs; 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: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: AssemblyCompany("LCFairCompany")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Mod to tweak some monsters in Lethal Company to make them more balanced, now fully configurable and synced with the Host!")] [assembly: AssemblyFileVersion("2.1.0.0")] [assembly: AssemblyInformationalVersion("2.1.0+a6432e10395b947dccd01f14c65e0154edf3f185")] [assembly: AssemblyProduct("LCFairCompany")] [assembly: AssemblyTitle("LCFairCompany")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.1.0.0")] [module: UnverifiableCode] namespace LCFairCompany { [StructLayout(LayoutKind.Sequential, Size = 1)] internal struct EntitiesName { public const string SnareFlea = "Centipede"; public const string BunkerSpider = "Bunker Spider"; public const string HoardingBug = "Hoarding Bug"; public const string Bracken = "Flowerman"; public const string Thumper = "Crawler"; public const string Hydrogene = "Blob"; public const string GhostGirl = "Girl"; public const string SporeLizard = "Puffer"; public const string Nutcracker = "Nutcracker"; public const string Coilhead = "Spring"; public const string Jester = "Jester"; public const string Masked = "Masked"; public const string EyelessDog = "MouthDog"; public const string ForestKeeper = "ForestGiant"; public const string EarthLeviathan = "Earth Leviathan"; public const string BaboonHawk = "Baboon hawk"; public const string CircuitBees = "Red Locust Bees"; public const string Manticoil = "Manticoil"; public const string RoamingLocusts = "Docile Locust Bees"; public const string LassoMan = "Lasso"; } internal static class EnemyHelpers { public static bool IsMatchingName(this EnemyType enemyType, string enemyName) { return enemyType.enemyName.Equals(enemyName, StringComparison.OrdinalIgnoreCase); } public static void SetEnemyPowerLevel(this EnemyType enemyType, float powerLevel) { if (Mathf.Approximately(enemyType.PowerLevel, powerLevel)) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)$"\"{enemyType.enemyName}\" PowerLevel is already {powerLevel}, nothing to change"); } return; } ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogInfo((object)$"Changing \"{enemyType.enemyName}\" PowerLevel ({enemyType.PowerLevel} => {powerLevel})"); } enemyType.PowerLevel = powerLevel; } public static void SetEnemyMaxCount(this EnemyType enemyType, int maxCount) { if (enemyType.MaxCount == maxCount) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)$"\"{enemyType.enemyName}\" MaxCount is already {maxCount}, nothing to change"); } return; } ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogInfo((object)$"Changing \"{enemyType.enemyName}\" MaxCount ({enemyType.MaxCount} => {maxCount})"); } enemyType.MaxCount = maxCount; } public static void SetEnemyHP(this EnemyAI enemyAI, int enemyHP) { if (enemyAI.enemyHP == enemyHP) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)$"\"{enemyAI.enemyType.enemyName}\" EnemyHP is already {enemyHP}, nothing to change"); } return; } ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogInfo((object)$"Changing \"{enemyAI.enemyType.enemyName}\" EnemyHP ({enemyAI.enemyHP} => {enemyHP})"); } enemyAI.enemyHP = enemyHP; } } internal static class AudioHelpers { public static int PlayRandomOneShot(this AudioSource audioSource, AudioClip[] clipsArray, float volume = 1f, float? minDistance = null, float? maxDistance = null, bool bTransmitToWalkieTalkie = true, int timesPlayedInSameSpot = 0) { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)audioSource == (Object)null || clipsArray == null || clipsArray.Length == 0) { return -1; } float pitch = audioSource.pitch; float minDistance2 = audioSource.minDistance; float maxDistance2 = audioSource.maxDistance; audioSource.pitch = Random.Range(0.94f, 1.06f); if (minDistance.HasValue) { audioSource.minDistance = minDistance.Value; } if (maxDistance.HasValue) { audioSource.maxDistance = maxDistance.Value; } int num = Random.Range(0, clipsArray.Length); audioSource.PlayOneShot(clipsArray[num], volume); if (bTransmitToWalkieTalkie) { WalkieTalkie.TransmitOneShotAudio(audioSource, clipsArray[num], 0.85f * volume); } if (audioSource.spatialBlend > 0f) { RoundManager.Instance.PlayAudibleNoise(((Component)audioSource).transform.position, 4f * volume, volume / 2f, timesPlayedInSameSpot, false, 0); } audioSource.pitch = pitch; audioSource.minDistance = minDistance2; audioSource.maxDistance = maxDistance2; return num; } } [BepInPlugin("LCFairCompany", "LCFairCompany", "2.1.0")] internal class Plugin : BaseUnityPlugin { private readonly Harmony _harmony = null; private readonly ConfigManager _configManager = null; internal static ManualLogSource Logger { get; private set; } protected Plugin() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown Logger = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("LCFairCompany"); _configManager = new ConfigManager(((BaseUnityPlugin)this).Config); } protected void Awake() { ManualLogSource logger = Logger; if (logger != null) { logger.LogInfo((object)"Plugin LCFairCompany is loaded!"); } ManualLogSource logger2 = Logger; if (logger2 != null) { logger2.LogDebug((object)"Patching harmony..."); } _harmony.PatchAll(Assembly.GetExecutingAssembly()); } } public static class PluginInfo { public const string PLUGIN_GUID = "LCFairCompany"; public const string PLUGIN_NAME = "LCFairCompany"; public const string PLUGIN_VERSION = "2.1.0"; } } namespace LCFairCompany.Patches { [HarmonyPatch(typeof(CentipedeAI))] internal class CentipedeAIPatch : MonoBehaviour { private CentipedeAI _centipedeAI = null; private int _damageSinceClingingToPlayer = 0; private int _timesPlayedInSameSpot = 0; private float? _delayUntilNextAudioCue = null; private bool LimitDamagePerClinging => SyncedInstance<ConfigManager>.Instance.Centipede.LimitDamagePerClinging.Value; private int MaxDamagePerClinging => SyncedInstance<ConfigManager>.Instance.Centipede.MaxDamagePerClinging.Value; private bool PlayAudioShriekOnCeiling => SyncedInstance<ConfigManager>.Default.Centipede.PlayAudioShriekOnCeiling.Value; private int AudioShriekMinFrequency => SyncedInstance<ConfigManager>.Default.Centipede.AudioShriekMinFrequency.Value; private int AudioShriekMaxFrequency => SyncedInstance<ConfigManager>.Default.Centipede.AudioShriekMaxFrequency.Value; protected void Start() { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)"\"Centipede\" applies \"CentipedeAIPatch\""); } _centipedeAI = ((Component)this).gameObject.GetComponent<CentipedeAI>(); } protected void LateUpdate() { if (PlayAudioShriekOnCeiling) { UpdateDelayUntilNextAudioCue(Time.deltaTime); } else { _delayUntilNextAudioCue = null; } } public void OnPlayerDamaged(int damage) { if (_centipedeAI.inDroppingOffPlayerAnim) { return; } ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)string.Format("Updating \"{0}\" DamageSinceClingingToPlayer ({1} => {2})", "Centipede", _damageSinceClingingToPlayer, _damageSinceClingingToPlayer + damage)); } _damageSinceClingingToPlayer += damage; bool flag = false; if (StartOfRound.Instance.connectedPlayersAmount > 0 && StartOfRound.Instance.livingPlayers == 1 && _centipedeAI.clingingToPlayer.health <= 15 && !StartOfRoundPatch.LastSurvivorSecondChanceGiven) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogInfo((object)"\"Centipede\" is attempting to kill the last survivor"); } StartOfRoundPatch.LastSurvivorSecondChanceGiven = true; flag = true; } else if (LimitDamagePerClinging && _damageSinceClingingToPlayer >= MaxDamagePerClinging) { ManualLogSource logger3 = Plugin.Logger; if (logger3 != null) { logger3.LogInfo((object)string.Format("\"{0}\" reached MaxDamagePerClinging ({1}/{2})", "Centipede", _damageSinceClingingToPlayer, MaxDamagePerClinging)); } flag = true; } if (flag) { _centipedeAI.inDroppingOffPlayerAnim = true; _centipedeAI.StopClingingServerRpc(false); } } public void OnStopClingingToPlayer() { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)string.Format("Resetting \"{0}\" DamageSinceClingingToPlayer ({1} => 0)", "Centipede", _damageSinceClingingToPlayer)); } _damageSinceClingingToPlayer = 0; } private void PlayRandomShriekSFX() { if (!((Object)(object)_centipedeAI == (Object)null)) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)string.Format("Playing \"{0}\" RandomShriekSFX ({1}={2})", "Centipede", "_timesPlayedInSameSpot", _timesPlayedInSameSpot)); } ((EnemyAI)_centipedeAI).creatureSFX.PlayRandomOneShot(_centipedeAI.shriekClips, 0.1f, 10f, 35f, bTransmitToWalkieTalkie: false, _timesPlayedInSameSpot); } } private void UpdateDelayUntilNextAudioCue(float deltaTime) { if ((Object)(object)_centipedeAI == (Object)null || !((NetworkBehaviour)_centipedeAI).IsClient || !_centipedeAI.clingingToCeiling || _centipedeAI.ceilingAnimationCoroutine != null) { _delayUntilNextAudioCue = null; _timesPlayedInSameSpot = 0; return; } if (!_delayUntilNextAudioCue.HasValue) { float num = 60f / (float)Mathf.Max(AudioShriekMaxFrequency, 1); float num2 = 60f / (float)Mathf.Max(AudioShriekMinFrequency, 1); if (num <= num2) { _delayUntilNextAudioCue = Random.Range(num, num2); } else { _delayUntilNextAudioCue = Random.Range(num2, num); } } _delayUntilNextAudioCue -= deltaTime; if (_delayUntilNextAudioCue <= 0f) { PlayRandomShriekSFX(); _delayUntilNextAudioCue = null; _timesPlayedInSameSpot++; } } [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartPostfix(CentipedeAI __instance) { ((Component)__instance).gameObject.AddComponent<CentipedeAIPatch>(); } [HarmonyPatch("DamagePlayerOnIntervals")] [HarmonyPrefix] private static void DamagePlayerOnIntervalsPrefix(CentipedeAI __instance, out int __state) { __state = __instance.clingingToPlayer?.health ?? 0; } [HarmonyPatch("DamagePlayerOnIntervals")] [HarmonyPostfix] private static void DamagePlayerOnIntervalsPostfix(CentipedeAI __instance, int __state) { int valueOrDefault = (__state - __instance.clingingToPlayer?.health).GetValueOrDefault(); CentipedeAIPatch centipedeAIPatch = default(CentipedeAIPatch); if (valueOrDefault > 0 && ((Component)__instance).gameObject.TryGetComponent<CentipedeAIPatch>(ref centipedeAIPatch)) { centipedeAIPatch.OnPlayerDamaged(valueOrDefault); } } [HarmonyPatch("ClingToPlayer")] [HarmonyPostfix] private static void ClingToPlayerPostfix(CentipedeAI __instance, PlayerControllerB playerScript) { PlayerControllerBPatch playerControllerBPatch = default(PlayerControllerBPatch); if (((Component)playerScript).TryGetComponent<PlayerControllerBPatch>(ref playerControllerBPatch)) { playerControllerBPatch.OnClingToPlayer(__instance); } } [HarmonyPatch("StopClingingToPlayer")] [HarmonyPrefix] private static void StopClingingToPlayerPrefix(CentipedeAI __instance) { PlayerControllerBPatch playerControllerBPatch = default(PlayerControllerBPatch); if (((Component)__instance.clingingToPlayer).TryGetComponent<PlayerControllerBPatch>(ref playerControllerBPatch)) { playerControllerBPatch.OnStopClingingToPlayer(); } } [HarmonyPatch("StopClingingToPlayer")] [HarmonyPostfix] private static void StopClingingToPlayerPostfix(CentipedeAI __instance) { CentipedeAIPatch centipedeAIPatch = default(CentipedeAIPatch); if (((Component)__instance).gameObject.TryGetComponent<CentipedeAIPatch>(ref centipedeAIPatch)) { centipedeAIPatch.OnStopClingingToPlayer(); } } } [HarmonyPatch(typeof(EnemyAI))] internal static class EnemyAIPatch { [HarmonyPatch("PlayerIsTargetable")] [HarmonyPostfix] private static void PlayerIsTargetablePostfix(ref bool __result, EnemyAI __instance, PlayerControllerB playerScript) { PlayerControllerBPatch playerControllerBPatch = default(PlayerControllerBPatch); if (__result && __instance is CentipedeAI && ((Component)playerScript).TryGetComponent<PlayerControllerBPatch>(ref playerControllerBPatch) && playerControllerBPatch.IsClingingToPlayer()) { __result = false; } } } [HarmonyPatch(typeof(GameNetworkManager))] internal class GameNetworkManagerPatch { [HarmonyPatch("SteamMatchmaking_OnLobbyMemberJoined")] [HarmonyPostfix] private static void SteamMatchmaking_OnLobbyMemberJoinedPostfix() { //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 (SyncedInstance<ConfigManager>.IsHost) { SyncedInstance<ConfigManager>.MessageManager.RegisterNamedMessageHandler("LCFairCompany_OnRequestConfigSync", new HandleNamedMessageDelegate(ConfigManager.OnRequestSync)); SyncedInstance<ConfigManager>.Synced = true; } else { SyncedInstance<ConfigManager>.Synced = false; SyncedInstance<ConfigManager>.MessageManager.RegisterNamedMessageHandler("LCFairCompany_OnReceiveConfigSync", new HandleNamedMessageDelegate(ConfigManager.OnReceiveSync)); ConfigManager.RequestSync(); } } [HarmonyPatch("StartDisconnect")] [HarmonyPostfix] public static void StartDisconnectPostfix() { SyncedInstance<ConfigManager>.RevertSync(); } } [HarmonyPatch(typeof(PlayerControllerB))] internal class PlayerControllerBPatch : MonoBehaviour { private PlayerControllerB _playerController = null; private CentipedeAI _clingingToPlayer = null; protected void Start() { _playerController = ((Component)this).gameObject.GetComponent<PlayerControllerB>(); ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("\"" + _playerController.playerUsername + "\" applies \"PlayerControllerBPatch\"")); } } public void OnClingToPlayer(CentipedeAI centipedeAI) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("\"Centipede\" has latched on player \"" + _playerController?.playerUsername + "\"")); } _clingingToPlayer = centipedeAI; } public void OnStopClingingToPlayer() { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("\"Centipede\" has dropped from player \"" + _playerController?.playerUsername + "\"")); } _clingingToPlayer = null; } public bool IsClingingToPlayer() { return (Object)(object)_clingingToPlayer != (Object)null && !((EnemyAI)_clingingToPlayer).isEnemyDead; } [HarmonyPatch("Awake")] [HarmonyPostfix] private static void PlayerControllerBAwakePostfix(PlayerControllerB __instance) { ((Component)__instance).gameObject.AddComponent<PlayerControllerBPatch>(); } } [HarmonyPatch(typeof(SandSpiderAI))] internal static class SandSpiderAIPatch { private static int Health => SyncedInstance<ConfigManager>.Instance.Spider.Health.Value; private static int Damage => SyncedInstance<ConfigManager>.Instance.Spider.Damage.Value; private static int ScaleDPS => SyncedInstance<ConfigManager>.Instance.Spider.ScaleDPS.Value; [HarmonyPatch("Start")] [HarmonyPostfix] private static void StartPostfix(SandSpiderAI __instance) { ((EnemyAI)(object)__instance).SetEnemyHP(Health); } [HarmonyPatch("OnCollideWithPlayer")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> OnCollideWithPlayerTranspiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = instructions.ToList(); bool flag = false; float num = (float)Damage / 1f; float num2 = num * (float)ScaleDPS / 100f; float num3 = (float)Damage / num2; FieldInfo fieldInfo = AccessTools.Field(typeof(SandSpiderAI), "timeSinceHittingPlayer"); bool flag2 = false; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)string.Format("Enumerating code instructions in \"{0}\" until we find the hardcoded damage ({1}) and cooldown ({2})...", "OnCollideWithPlayer", 90, 1f)); } for (int i = 0; i < list.Count; i++) { if (CodeInstructionExtensions.LoadsConstant(list[i], 90L)) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogInfo((object)string.Format("Changing \"{0}\" Damage ({1} => {2})", "Bunker Spider", 90, Damage)); } list[i].operand = Damage; flag = true; } if (CodeInstructionExtensions.LoadsField(list[i], fieldInfo, false) && i + 1 < list.Count && CodeInstructionExtensions.LoadsConstant(list[i + 1], 1.0)) { ManualLogSource logger3 = Plugin.Logger; if (logger3 != null) { logger3.LogInfo((object)string.Format("Changing \"{0}\" HitCooldown ({1} => {2})", "Bunker Spider", 1f, num3)); } list[i + 1].operand = num3; flag2 = true; } if (flag && flag2) { break; } } if (!flag) { ManualLogSource logger4 = Plugin.Logger; if (logger4 != null) { logger4.LogError((object)string.Format("[{0}] Cannot find hardcoded damage ({1})", "OnCollideWithPlayer", 90)); } } if (!flag2) { ManualLogSource logger5 = Plugin.Logger; if (logger5 != null) { logger5.LogError((object)string.Format("[{0}] Cannot find \"{1}\" or hardcoded cooldown ({2})", "OnCollideWithPlayer", fieldInfo.Name, 1f)); } } return list; } } [HarmonyPatch(typeof(StartOfRound))] internal static class StartOfRoundPatch { public static bool LastSurvivorSecondChanceGiven; private static bool BunkerSpiderChangeMaxCount => SyncedInstance<ConfigManager>.Instance.Enemy.BunkerSpiderChangeMaxCount.Value; private static bool CoilheadChangePowerLevel => SyncedInstance<ConfigManager>.Instance.Enemy.CoilheadChangePowerLevel.Value; private static bool CoilheadChangeMaxCount => SyncedInstance<ConfigManager>.Instance.Enemy.CoilheadChangeMaxCount.Value; private static bool GhostGirlChangePowerLevel => SyncedInstance<ConfigManager>.Instance.Enemy.GhostGirlChangePowerLevel.Value; private static bool JesterChangePowerLevel => SyncedInstance<ConfigManager>.Instance.Enemy.JesterChangePowerLevel.Value; [HarmonyPatch("StartGame")] [HarmonyPostfix] private static void StartGamePostfix() { LastSurvivorSecondChanceGiven = false; if (StartOfRound.Instance == null || !((NetworkBehaviour)StartOfRound.Instance).IsServer) { return; } if (StartOfRound.Instance.currentLevel == null) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogError((object)"currentLevel is null!"); } return; } ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogDebug((object)"Enumerating all spawnable enemies..."); } foreach (SpawnableEnemyWithRarity enemy in StartOfRound.Instance.currentLevel.Enemies) { if (enemy == null || enemy.enemyType == null) { continue; } EnemyType enemyType = enemy.enemyType; ManualLogSource logger3 = Plugin.Logger; if (logger3 != null) { logger3.LogDebug((object)("Found spawnable enemy: \"" + enemyType.enemyName + "\"")); } if (enemyType.IsMatchingName("Bunker Spider")) { if (BunkerSpiderChangeMaxCount) { enemyType.SetEnemyMaxCount(2); } } else if (enemyType.IsMatchingName("Spring")) { if (CoilheadChangePowerLevel) { enemyType.SetEnemyPowerLevel(1.5f); } if (CoilheadChangeMaxCount) { enemyType.SetEnemyMaxCount(4); } } else if (enemyType.IsMatchingName("Girl")) { if (GhostGirlChangePowerLevel) { enemyType.SetEnemyPowerLevel(3f); } } else if (enemyType.IsMatchingName("Jester") && JesterChangePowerLevel) { enemyType.SetEnemyPowerLevel(2f); } } } } } namespace LCFairCompany.Configs { internal class CentipedeConfig { public ConfigEntry<bool> LimitDamagePerClinging { get; private set; } = null; public ConfigEntry<int> MaxDamagePerClinging { get; private set; } = null; public ConfigEntry<bool> PlayAudioShriekOnCeiling { get; private set; } = null; public ConfigEntry<int> AudioShriekMinFrequency { get; private set; } = null; public ConfigEntry<int> AudioShriekMaxFrequency { get; private set; } = null; public CentipedeConfig(ConfigFile config) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Expected O, but got Unknown //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown if (config == null) { throw new ArgumentNullException("config"); } LimitDamagePerClinging = config.Bind<bool>("Snare Flea Settings", "Limit Damage Per Clinging", true, new ConfigDescription("Limit how much damage Snare Fleas can inflict per clinging to any player (i.e. prevents getting oneshot if a player doesn't find a shovel or the exit in time).", (AcceptableValueBase)null, Array.Empty<object>())); MaxDamagePerClinging = config.Bind<int>("Snare Flea Settings", "Max Damage Per Clinging", 60, new ConfigDescription("If limit is applied, Snare Fleas stop clinging from any player after inflicting that much damage.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(50, 100), Array.Empty<object>())); PlayAudioShriekOnCeiling = config.Bind<bool>("Snare Flea Settings (client only)", "Play Audio Shriek On Ceiling", true, new ConfigDescription("Add a quiet audio shriek (triggering 3 to 4 times per minute by default) when a Snare Flea is on a ceiling.", (AcceptableValueBase)null, Array.Empty<object>())); AudioShriekMinFrequency = config.Bind<int>("Snare Flea Settings (client only)", "Audio Shriek Min Frequency", 3, new ConfigDescription("Snare Fleas will play the audio shriek at least these many times per minute.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 5), Array.Empty<object>())); AudioShriekMaxFrequency = config.Bind<int>("Snare Flea Settings (client only)", "Audio Shriek Max Frequency", 4, new ConfigDescription("Snare Fleas will play the audio shriek at most these many times per minute.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 5), Array.Empty<object>())); } ~CentipedeConfig() { if (LimitDamagePerClinging != null) { ((ConfigEntryBase)LimitDamagePerClinging).ConfigFile.Remove(((ConfigEntryBase)LimitDamagePerClinging).Definition); LimitDamagePerClinging = null; } if (MaxDamagePerClinging != null) { ((ConfigEntryBase)MaxDamagePerClinging).ConfigFile.Remove(((ConfigEntryBase)MaxDamagePerClinging).Definition); MaxDamagePerClinging = null; } if (PlayAudioShriekOnCeiling != null) { ((ConfigEntryBase)PlayAudioShriekOnCeiling).ConfigFile.Remove(((ConfigEntryBase)PlayAudioShriekOnCeiling).Definition); PlayAudioShriekOnCeiling = null; } if (AudioShriekMinFrequency != null) { ((ConfigEntryBase)AudioShriekMinFrequency).ConfigFile.Remove(((ConfigEntryBase)AudioShriekMinFrequency).Definition); AudioShriekMinFrequency = null; } if (AudioShriekMaxFrequency != null) { ((ConfigEntryBase)AudioShriekMaxFrequency).ConfigFile.Remove(((ConfigEntryBase)AudioShriekMaxFrequency).Definition); AudioShriekMaxFrequency = null; } } } [Serializable] internal class ConfigManager : SyncedInstance<ConfigManager> { private ConfigFile _config = null; public EnemyConfig Enemy { get; private set; } = null; public SandSpiderConfig Spider { get; private set; } = null; public CentipedeConfig Centipede { get; private set; } = null; public ConfigFile Config { get { return _config; } set { if (_config != null || value == null) { Enemy = null; Spider = null; Centipede = null; } _config = value; if (_config != null) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)"Binding config..."); } _config.SaveOnConfigSet = false; Enemy = new EnemyConfig(_config); Spider = new SandSpiderConfig(_config); Centipede = new CentipedeConfig(_config); ClearOrphanedEntries(_config); _config.Save(); _config.SaveOnConfigSet = true; } } } public ConfigManager(ConfigFile config) { InitInstance(this); Config = config; } public static void RequestSync() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!SyncedInstance<ConfigManager>.IsClient) { return; } FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(SyncedInstance<ConfigManager>.IntSize, (Allocator)2, -1); try { SyncedInstance<ConfigManager>.MessageManager.SendNamedMessage("LCFairCompany_OnRequestConfigSync", 0uL, val, (NetworkDelivery)3); } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } public static void OnRequestSync(ulong clientId, FastBufferReader _) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (!SyncedInstance<ConfigManager>.IsHost) { return; } ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)$"Config sync request received from client: {clientId}"); } byte[] array = SyncedInstance<ConfigManager>.SerializeToBytes(SyncedInstance<ConfigManager>.Instance); int num = array.Length; FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(num + SyncedInstance<ConfigManager>.IntSize, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref num, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteBytesSafe(array, -1, 0); SyncedInstance<ConfigManager>.MessageManager.SendNamedMessage("LCFairCompany_OnReceiveConfigSync", clientId, val, (NetworkDelivery)3); } catch (Exception arg) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogError((object)$"Error occurred syncing config with client: {clientId}\n{arg}"); } } finally { ((IDisposable)(FastBufferWriter)(ref val)).Dispose(); } } public static void OnReceiveSync(ulong _, FastBufferReader reader) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (!((FastBufferReader)(ref reader)).TryBeginRead(SyncedInstance<ConfigManager>.IntSize)) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.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)) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogError((object)"Config sync error: Host could not sync."); } return; } byte[] data = new byte[num]; ((FastBufferReader)(ref reader)).ReadBytesSafe(ref data, num, 0); SyncedInstance<ConfigManager>.SyncInstance(data); ManualLogSource logger3 = Plugin.Logger; if (logger3 != null) { logger3.LogInfo((object)"Successfully synced config with host."); } } private static void ClearOrphanedEntries(ConfigFile config) { PropertyInfo propertyInfo = AccessTools.Property(typeof(ConfigFile), "OrphanedEntries"); Dictionary<ConfigDefinition, string> dictionary = (Dictionary<ConfigDefinition, string>)propertyInfo.GetValue(config); dictionary.Clear(); } } internal class EnemyConfig { public ConfigEntry<bool> BunkerSpiderChangeMaxCount { get; private set; } = null; public ConfigEntry<bool> CoilheadChangePowerLevel { get; private set; } = null; public ConfigEntry<bool> CoilheadChangeMaxCount { get; private set; } = null; public ConfigEntry<bool> GhostGirlChangePowerLevel { get; private set; } = null; public ConfigEntry<bool> JesterChangePowerLevel { get; private set; } = null; public EnemyConfig(ConfigFile config) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown if (config == null) { throw new ArgumentNullException("config"); } BunkerSpiderChangeMaxCount = config.Bind<bool>("Bunker Spider Settings", "Change Max Count", true, new ConfigDescription("If enabled, there can be at most 2 spiders spawned on the map (instead of 1). Do you prefer potentially having 2 spiders or other monsters? That's up to you.", (AcceptableValueBase)null, Array.Empty<object>())); CoilheadChangePowerLevel = config.Bind<bool>("Coilhead Settings", "Change Power Level", true, new ConfigDescription("If enabled, makes it less likely to spawn (e.g. more than the Bracken but less than Hoarding Bugs).", (AcceptableValueBase)null, Array.Empty<object>())); CoilheadChangeMaxCount = config.Bind<bool>("Coilhead Settings", "Change Max Count", true, new ConfigDescription("If enabled, there can be at most 4 coilheads spawned on the map (instead of 5).", (AcceptableValueBase)null, Array.Empty<object>())); GhostGirlChangePowerLevel = config.Bind<bool>("Ghost Girl Settings", "Change Power Level", true, new ConfigDescription("If enabled, makes it less likely to spawn (e.g. same as the Bracken).", (AcceptableValueBase)null, Array.Empty<object>())); JesterChangePowerLevel = config.Bind<bool>("Jester Settings", "Change Power Level", true, new ConfigDescription("If enabled, makes it more likely to spawn (e.g. more than the Bracken but less than Hoarding Bugs).", (AcceptableValueBase)null, Array.Empty<object>())); } ~EnemyConfig() { if (BunkerSpiderChangeMaxCount != null) { ((ConfigEntryBase)BunkerSpiderChangeMaxCount).ConfigFile.Remove(((ConfigEntryBase)BunkerSpiderChangeMaxCount).Definition); BunkerSpiderChangeMaxCount = null; } if (CoilheadChangePowerLevel != null) { ((ConfigEntryBase)CoilheadChangePowerLevel).ConfigFile.Remove(((ConfigEntryBase)CoilheadChangePowerLevel).Definition); CoilheadChangePowerLevel = null; } if (CoilheadChangeMaxCount != null) { ((ConfigEntryBase)CoilheadChangeMaxCount).ConfigFile.Remove(((ConfigEntryBase)CoilheadChangeMaxCount).Definition); CoilheadChangeMaxCount = null; } if (GhostGirlChangePowerLevel != null) { ((ConfigEntryBase)GhostGirlChangePowerLevel).ConfigFile.Remove(((ConfigEntryBase)GhostGirlChangePowerLevel).Definition); GhostGirlChangePowerLevel = null; } if (JesterChangePowerLevel != null) { ((ConfigEntryBase)JesterChangePowerLevel).ConfigFile.Remove(((ConfigEntryBase)JesterChangePowerLevel).Definition); JesterChangePowerLevel = null; } } } internal class SandSpiderConfig { public ConfigEntry<int> Health { get; private set; } = null; public ConfigEntry<int> Damage { get; private set; } = null; public ConfigEntry<int> ScaleDPS { get; private set; } = null; public SandSpiderConfig(ConfigFile config) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown if (config == null) { throw new ArgumentNullException("config"); } Health = config.Bind<int>("Bunker Spider Settings", "Health", 4, new ConfigDescription("Changes how much health the spider has. Game default: 5", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 5), Array.Empty<object>())); Damage = config.Bind<int>("Bunker Spider Settings", "Damage", 60, new ConfigDescription("Changes how much damage the spider does per hit. Game default: 90", (AcceptableValueBase)(object)new AcceptableValueRange<int>(45, 90), Array.Empty<object>())); ScaleDPS = config.Bind<int>("Bunker Spider Settings", "Scale DPS (%)", 75, new ConfigDescription("Allows to reduce the damage per second (i.e. how fast the spider can chain hits). Game default: 100%", (AcceptableValueBase)(object)new AcceptableValueRange<int>(50, 100), Array.Empty<object>())); } ~SandSpiderConfig() { if (Health != null) { ((ConfigEntryBase)Health).ConfigFile.Remove(((ConfigEntryBase)Health).Definition); Health = null; } if (Damage != null) { ((ConfigEntryBase)Damage).ConfigFile.Remove(((ConfigEntryBase)Damage).Definition); Damage = null; } if (ScaleDPS != null) { ((ConfigEntryBase)ScaleDPS).ConfigFile.Remove(((ConfigEntryBase)ScaleDPS).Definition); ScaleDPS = null; } } } [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(); using MemoryStream memoryStream = new MemoryStream(); try { binaryFormatter.Serialize(memoryStream, val); return memoryStream.ToArray(); } catch (Exception arg) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogError((object)$"Error serializing instance: {arg}"); } return null; } } public static T DeserializeFromBytes(byte[] data) { BinaryFormatter binaryFormatter = new BinaryFormatter(); using MemoryStream serializationStream = new MemoryStream(data); try { return (T)binaryFormatter.Deserialize(serializationStream); } catch (Exception arg) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogError((object)$"Error deserializing instance: {arg}"); } return default(T); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }