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.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using CombatSharedFeatures; using GameNetcodeStuff; using HarmonyLib; using Unity.Netcode; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("LethalBattleTweaks")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("lengjing")] [assembly: AssemblyProduct("LethalBattleTweaks")] [assembly: AssemblyCopyright("Copyright © lengjing 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("8e5a2c62-6a18-4f9e-8948-596a50b89631")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.2.0")] namespace CombatSharedFeatures { public static class PublicFeatureConfig { public static ConfigEntry RefillEnabled { get; private set; } public static ConfigEntry RefillInterval { get; private set; } public static ConfigEntry RefillTotalCap { get; private set; } public static ConfigEntry RefillDelay { get; private set; } public static ConfigEntry SpawnDelay { get; private set; } public static ConfigEntry RefillBatchSize { get; private set; } public static ConfigEntry RefillFlowermanInit { get; private set; } public static ConfigEntry RefillFlowermanMax { get; private set; } public static ConfigEntry RefillFlowermanWeight { get; private set; } public static ConfigEntry RefillNutcrackerInit { get; private set; } public static ConfigEntry RefillNutcrackerMax { get; private set; } public static ConfigEntry RefillNutcrackerWeight { get; private set; } public static ConfigEntry RefillCrawlerInit { get; private set; } public static ConfigEntry RefillCrawlerMax { get; private set; } public static ConfigEntry RefillCrawlerWeight { get; private set; } public static ConfigEntry RefillCentipedeInit { get; private set; } public static ConfigEntry RefillCentipedeMax { get; private set; } public static ConfigEntry RefillCentipedeWeight { get; private set; } public static ConfigEntry RefillHoarderBugInit { get; private set; } public static ConfigEntry RefillHoarderBugMax { get; private set; } public static ConfigEntry RefillHoarderBugWeight { get; private set; } public static ConfigEntry RefillMaskedInit { get; private set; } public static ConfigEntry RefillMaskedMax { get; private set; } public static ConfigEntry RefillMaskedWeight { get; private set; } public static ConfigEntry RefillSandSpiderInit { get; private set; } public static ConfigEntry RefillSandSpiderMax { get; private set; } public static ConfigEntry RefillSandSpiderWeight { get; private set; } public static ConfigEntry RefillButlerInit { get; private set; } public static ConfigEntry RefillButlerMax { get; private set; } public static ConfigEntry RefillButlerWeight { get; private set; } public static ConfigEntry RefillStingrayInit { get; private set; } public static ConfigEntry RefillStingrayMax { get; private set; } public static ConfigEntry RefillStingrayWeight { get; private set; } public static ConfigEntry StartingCredits { get; private set; } public static ConfigEntry DisableHazards { get; private set; } public static ConfigEntry DisableVanillaEnemies { get; private set; } public static ConfigEntry DisableScrap { get; private set; } public static ConfigEntry NoLockedDoors { get; private set; } public static ConfigEntry DisableFriendlyMeleeDamage { get; private set; } public static ConfigEntry ButlerInstantOpenDoors { get; private set; } public static ConfigEntry NutcrackerNoFriendlyFire { get; private set; } public static ConfigEntry NutcrackerDropGunAndShells { get; private set; } public static ConfigEntry ButlerDropKnifeAndBees { get; private set; } public static ConfigEntry TZPHealingEnabled { get; private set; } public static ConfigEntry TZPHealAmount { get; private set; } public static ConfigEntry LootDropsEnabled { get; private set; } public static ConfigEntry TZPDropChance { get; private set; } public static ConfigEntry StunGrenadeDropChance { get; private set; } public static ConfigEntry CorpseCleanupEnabled { get; private set; } public static ConfigEntry CorpseCleanupDelay { get; private set; } private static string Bilingual(string en, string zh) { return en + " / " + zh; } public static void Bind(ConfigFile config) { string text = "Enemy Refill"; RefillEnabled = config.Bind(text, "Enabled", true, Bilingual("Enable dynamic indoor enemy refill.", "启用设施内动态补怪。")); RefillInterval = config.Bind(text, "Interval", 5f, Bilingual("Seconds between refill checks.", "每次检查是否需要补怪的间隔秒数。")); RefillTotalCap = config.Bind(text, "TotalCap", 18, Bilingual("Total indoor enemies the refill system tries to maintain.", "补怪系统尝试维持的设施内怪物总上限。")); RefillDelay = config.Bind(text, "RefillDelay", 10, Bilingual("Delay after landing before refill starts.", "飞船落地后开始补怪前的延迟秒数。")); SpawnDelay = config.Bind(text, "SpawnDelay", 0.3f, Bilingual("Delay between spawned enemies during a refill batch.", "单次补怪批次中每只怪之间的生成间隔。")); RefillBatchSize = config.Bind(text, "BatchSize", 3, Bilingual("Maximum enemies spawned per refill batch.", "每次补怪批次最多生成的怪物数量。")); RefillFlowermanInit = config.Bind("Enemy Refill - Flowerman", "Init", 0, Bilingual("Initial Flowerman count.", "开局额外生成的小黑数量。")); RefillFlowermanMax = config.Bind("Enemy Refill - Flowerman", "Max", 1, Bilingual("Maximum Flowerman refill count.", "小黑可被补怪到的最大数量。")); RefillFlowermanWeight = config.Bind("Enemy Refill - Flowerman", "Weight", 10, Bilingual("Flowerman refill weight.", "小黑的补怪权重。")); RefillNutcrackerInit = config.Bind("Enemy Refill - Nutcracker", "Init", 0, Bilingual("Initial Nutcracker count.", "开局额外生成的胡桃夹子数量。")); RefillNutcrackerMax = config.Bind("Enemy Refill - Nutcracker", "Max", 1, Bilingual("Maximum Nutcracker refill count.", "胡桃夹子可被补怪到的最大数量。")); RefillNutcrackerWeight = config.Bind("Enemy Refill - Nutcracker", "Weight", 10, Bilingual("Nutcracker refill weight.", "胡桃夹子的补怪权重。")); RefillCrawlerInit = config.Bind("Enemy Refill - Crawler", "Init", 0, Bilingual("Initial Crawler count.", "开局额外生成的半身鱼数量。")); RefillCrawlerMax = config.Bind("Enemy Refill - Crawler", "Max", 1, Bilingual("Maximum Crawler refill count.", "半身鱼可被补怪到的最大数量。")); RefillCrawlerWeight = config.Bind("Enemy Refill - Crawler", "Weight", 10, Bilingual("Crawler refill weight.", "半身鱼的补怪权重。")); RefillCentipedeInit = config.Bind("Enemy Refill - Centipede", "Init", 0, Bilingual("Initial Centipede count.", "开局额外生成的抱脸虫数量。")); RefillCentipedeMax = config.Bind("Enemy Refill - Centipede", "Max", 2, Bilingual("Maximum Centipede refill count.", "抱脸虫可被补怪到的最大数量。")); RefillCentipedeWeight = config.Bind("Enemy Refill - Centipede", "Weight", 10, Bilingual("Centipede refill weight.", "抱脸虫的补怪权重。")); RefillHoarderBugInit = config.Bind("Enemy Refill - HoarderBug", "Init", 0, Bilingual("Initial HoarderBug count.", "开局额外生成的囤积虫数量。")); RefillHoarderBugMax = config.Bind("Enemy Refill - HoarderBug", "Max", 3, Bilingual("Maximum HoarderBug refill count.", "囤积虫可被补怪到的最大数量。")); RefillHoarderBugWeight = config.Bind("Enemy Refill - HoarderBug", "Weight", 10, Bilingual("HoarderBug refill weight.", "囤积虫的补怪权重。")); RefillMaskedInit = config.Bind("Enemy Refill - Masked", "Init", 0, Bilingual("Initial Masked count.", "开局额外生成的假人数量。")); RefillMaskedMax = config.Bind("Enemy Refill - Masked", "Max", 1, Bilingual("Maximum Masked refill count.", "假人可被补怪到的最大数量。")); RefillMaskedWeight = config.Bind("Enemy Refill - Masked", "Weight", 10, Bilingual("Masked refill weight.", "假人的补怪权重。")); RefillSandSpiderInit = config.Bind("Enemy Refill - SandSpider", "Init", 0, Bilingual("Initial SandSpider count.", "开局额外生成的蜘蛛数量。")); RefillSandSpiderMax = config.Bind("Enemy Refill - SandSpider", "Max", 2, Bilingual("Maximum SandSpider refill count.", "蜘蛛可被补怪到的最大数量。")); RefillSandSpiderWeight = config.Bind("Enemy Refill - SandSpider", "Weight", 10, Bilingual("SandSpider refill weight.", "蜘蛛的补怪权重。")); RefillButlerInit = config.Bind("Enemy Refill - Butler", "Init", 0, Bilingual("Initial Butler count.", "开局额外生成的管家数量。")); RefillButlerMax = config.Bind("Enemy Refill - Butler", "Max", 2, Bilingual("Maximum Butler refill count.", "管家可被补怪到的最大数量。")); RefillButlerWeight = config.Bind("Enemy Refill - Butler", "Weight", 10, Bilingual("Butler refill weight.", "管家的补怪权重。")); RefillStingrayInit = config.Bind("Enemy Refill - Stingray", "Init", 0, Bilingual("Initial Stingray count.", "开局额外生成的粘滑鱼数量。")); RefillStingrayMax = config.Bind("Enemy Refill - Stingray", "Max", 1, Bilingual("Maximum Stingray refill count.", "粘滑鱼可被补怪到的最大数量。")); RefillStingrayWeight = config.Bind("Enemy Refill - Stingray", "Weight", 10, Bilingual("Stingray refill weight.", "粘滑鱼的补怪权重。")); StartingCredits = config.Bind("World Rules", "StartingCredits", 60, Bilingual("Starting company credits.", "开局公司金钱。")); DisableHazards = config.Bind("World Rules", "DisableHazards", false, Bilingual("Disable landmines, turrets, and spikes.", "禁用地雷、炮塔和尖刺。")); DisableVanillaEnemies = config.Bind("World Rules", "DisableVanillaEnemies", false, Bilingual("Disable vanilla natural enemy spawning.", "禁用原版自然刷怪。")); DisableScrap = config.Bind("World Rules", "DisableScrap", false, Bilingual("Disable indoor scrap spawning.", "禁用室内废料刷新。")); NoLockedDoors = config.Bind("World Rules", "NoLockedDoors", true, Bilingual("Automatically unlock locked doors.", "自动解锁上锁的门。")); DisableFriendlyMeleeDamage = config.Bind("World Rules", "DisableFriendlyMeleeDamage", true, Bilingual("Disable shovel and knife friendly fire.", "禁用铲子和小刀的玩家友伤。")); ButlerInstantOpenDoors = config.Bind("World Rules", "ButlerInstantOpenDoors", true, Bilingual("Make butlers open doors almost instantly.", "让管家几乎瞬间开门。")); NutcrackerNoFriendlyFire = config.Bind("World Rules", "NutcrackerNoFriendlyFire", true, Bilingual("Prevent nutcracker shotgun blasts from damaging monsters while still allowing player damage.", "让胡桃夹子不会伤害怪物,但仍然可以伤害玩家。")); NutcrackerDropGunAndShells = config.Bind("World Rules", "NutcrackerDropGunAndShells", false, Bilingual("Allow nutcrackers to drop their shotgun and shotgun shells on death.", "允许胡桃夹子死亡时掉落枪和子弹。")); ButlerDropKnifeAndBees = config.Bind("World Rules", "ButlerDropKnifeAndBees", false, Bilingual("Allow butlers to spawn their knife and bees on death.", "允许管家死亡时掉落菜刀并生成蜜蜂。")); TZPHealingEnabled = config.Bind("TZP Healing", "Enabled", true, Bilingual("Allow TZP inhalant to heal once per canister.", "允许每罐 TZP 提供一次回血。")); TZPHealAmount = config.Bind("TZP Healing", "HealAmount", 20, Bilingual("Healing amount per TZP use.", "每次 TZP 触发时的回血量。")); LootDropsEnabled = config.Bind("Loot Drops", "Enabled", true, Bilingual("Allow enemies to drop combat supplies.", "允许敌人掉落战斗补给。")); TZPDropChance = config.Bind("Loot Drops", "TZPDropChance", 0.1f, Bilingual("Chance for supported enemies to drop TZP.", "支持的敌人掉落 TZP 的概率。")); StunGrenadeDropChance = config.Bind("Loot Drops", "StunGrenadeDropChance", 0.05f, Bilingual("Chance for supported enemies to drop a stun grenade.", "支持的敌人掉落闪光弹的概率。")); CorpseCleanupEnabled = config.Bind("Corpse Cleanup", "Enabled", true, Bilingual("Automatically remove enemy corpses after a delay.", "延迟一段时间后自动清理怪物尸体。")); CorpseCleanupDelay = config.Bind("Corpse Cleanup", "Delay", 5f, Bilingual("Delay in seconds before supported enemy corpses are removed.", "支持的怪物尸体在被清理前等待的秒数。")); } } public enum PublicFeatureMode { PublicStandalone, PrivateExtended } public static class PublicFeatureRuntime { [HarmonyPatch(typeof(StartOfRound), "StartGame")] private static class StartOfRoundStartGamePatch { [HarmonyPrefix] private static void Prefix() { if (!((Object)(object)StartOfRound.Instance == (Object)null) && ((NetworkBehaviour)StartOfRound.Instance).IsServer) { HandleRoundStart(); } } } private static BaseUnityPlugin _plugin; private static ConfigFile _config; private static Func _privateBossPredicate; private static bool _isInitialized; private static ManualLogSource _logger = Logger.CreateLogSource("PublicFeatureRuntime"); public static BaseUnityPlugin PluginInstance => _plugin; public static ManualLogSource Logger => _logger; public static PublicFeatureMode Mode { get; private set; } public static bool IsEnabled => _isInitialized; public static void Initialize(BaseUnityPlugin plugin, PublicFeatureMode mode, ManualLogSource logger) { if (!((Object)(object)_plugin == (Object)(object)plugin) || !_isInitialized) { _plugin = plugin; _config = plugin.Config; Mode = mode; _logger = logger ?? _logger; PublicFeatureConfig.Bind(_config); _config.SettingChanged += HandleConfigChanged; _isInitialized = true; } } public static void OnPluginStart() { if (_isInitialized) { PublicFeatureEnemyCatalog.CacheAllEnemyTypes(); RefillSystem.Restart(); } } public static void Shutdown() { if (_isInitialized) { RefillSystem.Stop(); if (_config != null) { _config.SettingChanged -= HandleConfigChanged; } _plugin = null; _config = null; _privateBossPredicate = null; _isInitialized = false; } } public static void HandleRoundStart() { if (_isInitialized) { PublicFeatureEnemyCatalog.CacheAllEnemyTypes(); ItemAndLootPatches.ResetRoundState(); RefillSystem.Restart(); } } public static void HandleConfigChanged(object sender, SettingChangedEventArgs e) { if (_isInitialized && e != null && e.ChangedSetting != null && (e.ChangedSetting.Definition.Section ?? string.Empty).StartsWith("Enemy Refill", StringComparison.OrdinalIgnoreCase)) { RefillSystem.Restart(); } } public static void SetPrivateBossPredicate(Func predicate) { _privateBossPredicate = predicate; } public static bool IsPrivateBossEnemy(EnemyAI enemy) { if (_privateBossPredicate != null && (Object)(object)enemy != (Object)null) { return _privateBossPredicate(enemy); } return false; } } public static class PublicFeatureEnemyCatalog { private static readonly Dictionary EnemyTypeCache = new Dictionary(); private static readonly Dictionary EnemyNameAliases = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "HoarderBug", "Hoarding bug" }, { "SandSpider", "Bunker Spider" }, { "Masked", "Masked" }, { "Centipede", "Centipede" }, { "Flowerman", "Flowerman" }, { "Nutcracker", "Nutcracker" }, { "Crawler", "Crawler" }, { "Butler", "Butler" }, { "Stingray", "Stingray" } }; public static int CachedEnemyCount => EnemyTypeCache.Count; public static void CacheAllEnemyTypes() { if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.levels == null) { return; } EnemyTypeCache.Clear(); SelectableLevel[] levels = StartOfRound.Instance.levels; foreach (SelectableLevel val in levels) { foreach (SpawnableEnemyWithRarity enemy in val.Enemies) { CacheEnemyType(enemy.enemyType); } foreach (SpawnableEnemyWithRarity daytimeEnemy in val.DaytimeEnemies) { CacheEnemyType(daytimeEnemy.enemyType); } foreach (SpawnableEnemyWithRarity outsideEnemy in val.OutsideEnemies) { CacheEnemyType(outsideEnemy.enemyType); } } } public static EnemyType GetEnemyTypeByName(string name) { if (string.IsNullOrEmpty(name)) { return null; } string text = name; if (EnemyNameAliases.TryGetValue(name, out var value)) { text = value; } if (EnemyTypeCache.TryGetValue(text, out var value2)) { return value2; } if (text != name && EnemyTypeCache.TryGetValue(name, out value2)) { return value2; } foreach (KeyValuePair item in EnemyTypeCache) { if (item.Key.Equals(text, StringComparison.OrdinalIgnoreCase) || item.Key.Equals(name, StringComparison.OrdinalIgnoreCase)) { return item.Value; } } foreach (KeyValuePair item2 in EnemyTypeCache) { if (item2.Key.IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0 || item2.Key.IndexOf(name, StringComparison.OrdinalIgnoreCase) >= 0) { return item2.Value; } } return null; } private static void CacheEnemyType(EnemyType type) { if (!((Object)(object)type == (Object)null) && !EnemyTypeCache.ContainsKey(type.enemyName)) { EnemyTypeCache[type.enemyName] = type; } } } public static class RefillSystem { private struct MonsterConfig { public string Name; public int Init; public int Max; public int Weight; public MonsterConfig(string name, int init, int max, int weight) { Name = name; Init = init; Max = max; Weight = weight; } } [CompilerGenerated] private sealed class d__9 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private List 5__2; private Dictionary 5__3; private Dictionary 5__4; private int 5__5; private int 5__6; private MonsterConfig 5__7; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__9(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__2 = null; 5__3 = null; 5__4 = null; 5__7 = default(MonsterConfig); <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; 5__4[5__7.Name]++; goto IL_03bb; } <>1__state = -1; if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null) { return false; } 5__2 = GetMonsterConfigs(); 5__3 = new Dictionary(); foreach (MonsterConfig item2 in 5__2) { 5__3[item2.Name] = PublicFeatureEnemyCatalog.GetEnemyTypeByName(item2.Name); } List spawnedEnemies = RoundManager.Instance.SpawnedEnemies; int num2 = 0; 5__4 = new Dictionary(); foreach (MonsterConfig item3 in 5__2) { 5__4[item3.Name] = 0; } for (int i = 0; i < spawnedEnemies.Count; i++) { EnemyAI val = spawnedEnemies[i]; if ((Object)(object)val == (Object)null || val.isEnemyDead) { continue; } foreach (MonsterConfig item4 in 5__2) { EnemyType val2 = 5__3[item4.Name]; if ((Object)(object)val2 != (Object)null && (Object)(object)val.enemyType == (Object)(object)val2) { 5__4[item4.Name]++; num2++; break; } } } int num3 = PublicFeatureConfig.RefillTotalCap.Value - num2; if (num3 <= 0) { return false; } 5__5 = Mathf.Min(num3, PublicFeatureConfig.RefillBatchSize.Value); 5__6 = 0; goto IL_03d9; IL_03d9: if (5__6 < 5__5) { RefillCandidatesBuffer.Clear(); foreach (MonsterConfig item5 in 5__2) { if (5__4[item5.Name] < item5.Max && item5.Weight > 0) { float item = (float)item5.Weight / (float)(5__4[item5.Name] + 1); RefillCandidatesBuffer.Add((item5, item)); } } if (RefillCandidatesBuffer.Count != 0) { float num4 = 0f; foreach (var item6 in RefillCandidatesBuffer) { num4 += item6.Score; } float num5 = Random.Range(0f, num4); 5__7 = RefillCandidatesBuffer.Last().Config; float num6 = 0f; foreach (var item7 in RefillCandidatesBuffer) { num6 += item7.Score; if (num5 < num6) { (5__7, _) = item7; break; } } EnemyType val3 = 5__3[5__7.Name]; if ((Object)(object)val3 != (Object)null) { <>2__current = SpawnEnemyCoroutine(val3, 1); <>1__state = 1; return true; } goto IL_03bb; } } return false; IL_03bb: 5__7 = default(MonsterConfig); 5__6++; goto IL_03d9; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__7 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private float 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; goto IL_0058; case 1: <>1__state = -1; goto IL_0058; case 2: <>1__state = -1; if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null) { return false; } if (!PublicFeatureConfig.RefillEnabled.Value || !((NetworkBehaviour)RoundManager.Instance).IsHost) { <>2__current = (object)new WaitForSeconds(1f); <>1__state = 3; return true; } if (PublicFeatureEnemyCatalog.CachedEnemyCount == 0) { PublicFeatureEnemyCatalog.CacheAllEnemyTypes(); } <>2__current = SpawnInitialEnemiesCoroutine(); <>1__state = 4; return true; case 3: <>1__state = -1; goto IL_0065; case 4: <>1__state = -1; 5__2 = 0f; goto IL_0193; case 5: <>1__state = -1; 5__2 += 1f; goto IL_0193; case 6: <>1__state = -1; break; case 7: { <>1__state = -1; if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.inShipPhase) { goto IL_0065; } goto IL_0232; } IL_0193: if (5__2 < (float)PublicFeatureConfig.RefillDelay.Value && !((Object)(object)StartOfRound.Instance == (Object)null) && !StartOfRound.Instance.inShipPhase) { <>2__current = (object)new WaitForSeconds(1f); <>1__state = 5; return true; } if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.inShipPhase) { goto IL_0065; } goto IL_0232; IL_0058: if ((Object)(object)RoundManager.Instance == (Object)null) { <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; } goto IL_0065; IL_0232: if ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.shipHasLanded && !StartOfRound.Instance.inShipPhase) { if (PublicFeatureConfig.RefillEnabled.Value) { <>2__current = CheckAndRefillEnemiesCoroutine(); <>1__state = 6; return true; } break; } goto IL_0065; IL_0065: if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null) { return false; } <>2__current = (object)new WaitUntil((Func)(() => (Object)(object)StartOfRound.Instance == (Object)null || (StartOfRound.Instance.shipHasLanded && !StartOfRound.Instance.inShipPhase && RoundManager.Instance.allEnemyVents != null && RoundManager.Instance.allEnemyVents.Length != 0))); <>1__state = 2; return true; } <>2__current = (object)new WaitForSeconds(PublicFeatureConfig.RefillInterval.Value); <>1__state = 7; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__11 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public EnemyType type; public int count; private GameObject[] 5__2; private int 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__11(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__2 = null; <>1__state = -2; } private bool MoveNext() { //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Expected O, but got Unknown //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null || StartOfRound.Instance.inShipPhase) { return false; } goto IL_0269; } <>1__state = -1; if ((Object)(object)type == (Object)null || count <= 0 || (Object)(object)RoundManager.Instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null) { return false; } 5__2 = RoundManager.Instance.insideAINodes; if (5__2 == null || 5__2.Length == 0) { return false; } ValidSpawnNodesBuffer.Clear(); for (int i = 0; i < 5__2.Length; i++) { ValidSpawnNodesBuffer.Add(i); } 5__3 = 0; goto IL_027b; IL_0269: 5__3++; goto IL_027b; IL_027b: if (5__3 < count) { if (ValidSpawnNodesBuffer.Count == 0) { for (int j = 0; j < 5__2.Length; j++) { ValidSpawnNodesBuffer.Add(j); } } int index = Random.Range(0, ValidSpawnNodesBuffer.Count); int num2 = ValidSpawnNodesBuffer[index]; GameObject val = 5__2[num2]; if ((Object)(object)val == (Object)null) { ValidSpawnNodesBuffer.RemoveAt(index); 5__3--; } else { Vector3 position = val.transform.position; Quaternion val2 = Quaternion.Euler(0f, val.transform.eulerAngles.y, 0f); try { GameObject obj = Object.Instantiate(type.enemyPrefab, position, val2); obj.GetComponentInChildren().Spawn(true); EnemyAI component = obj.GetComponent(); if ((Object)(object)component != (Object)null) { RoundManager.Instance.SpawnedEnemies.Add(component); component.enemyType.isOutsideEnemy = false; component.allAINodes = 5__2; component.SyncPositionToClients(); if (type.enemyName == "Flowerman") { component.EnableEnemyMesh(true, false, false); } } ValidSpawnNodesBuffer.RemoveAt(index); } catch (Exception arg) { PublicFeatureRuntime.Logger.LogError((object)$"[RefillSystem] Failed to spawn {type.enemyName}: {arg}"); } if (5__3 < count - 1) { <>2__current = (object)new WaitForSeconds(PublicFeatureConfig.SpawnDelay.Value); <>1__state = 1; return true; } } goto IL_0269; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__8 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private List.Enumerator <>7__wrap1; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__8(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = default(List.Enumerator); <>1__state = -2; } private bool MoveNext() { bool result; try { switch (<>1__state) { default: result = false; goto end_IL_0000; case 0: { <>1__state = -1; List monsterConfigs = GetMonsterConfigs(); <>7__wrap1 = monsterConfigs.GetEnumerator(); <>1__state = -3; break; } case 1: <>1__state = -3; break; } while (true) { if (<>7__wrap1.MoveNext()) { MonsterConfig current = <>7__wrap1.Current; if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.inShipPhase) { result = false; <>m__Finally1(); break; } if (current.Init > 0) { EnemyType enemyTypeByName = PublicFeatureEnemyCatalog.GetEnemyTypeByName(current.Name); if ((Object)(object)enemyTypeByName != (Object)null) { <>2__current = SpawnEnemyCoroutine(enemyTypeByName, current.Init); <>1__state = 1; result = true; break; } } continue; } <>m__Finally1(); <>7__wrap1 = default(List.Enumerator); result = false; break; } end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>7__wrap1).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly List CachedRefillConfigs = new List(); private static readonly List<(MonsterConfig Config, float Score)> RefillCandidatesBuffer = new List<(MonsterConfig, float)>(); private static readonly List ValidSpawnNodesBuffer = new List(); private static Coroutine _refillCoroutine; public static void Restart() { Stop(); if ((Object)(object)PublicFeatureRuntime.PluginInstance != (Object)null && PublicFeatureConfig.RefillEnabled.Value) { _refillCoroutine = ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StartCoroutine(RefillEnemiesRoutine()); } } public static void Stop() { if (_refillCoroutine != null && (Object)(object)PublicFeatureRuntime.PluginInstance != (Object)null) { ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StopCoroutine(_refillCoroutine); _refillCoroutine = null; } } [IteratorStateMachine(typeof(d__7))] private static IEnumerator RefillEnemiesRoutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__7(0); } [IteratorStateMachine(typeof(d__8))] private static IEnumerator SpawnInitialEnemiesCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__8(0); } [IteratorStateMachine(typeof(d__9))] private static IEnumerator CheckAndRefillEnemiesCoroutine() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__9(0); } private static List GetMonsterConfigs() { CachedRefillConfigs.Clear(); CachedRefillConfigs.Add(new MonsterConfig("Flowerman", PublicFeatureConfig.RefillFlowermanInit.Value, PublicFeatureConfig.RefillFlowermanMax.Value, PublicFeatureConfig.RefillFlowermanWeight.Value)); CachedRefillConfigs.Add(new MonsterConfig("Nutcracker", PublicFeatureConfig.RefillNutcrackerInit.Value, PublicFeatureConfig.RefillNutcrackerMax.Value, PublicFeatureConfig.RefillNutcrackerWeight.Value)); CachedRefillConfigs.Add(new MonsterConfig("Crawler", PublicFeatureConfig.RefillCrawlerInit.Value, PublicFeatureConfig.RefillCrawlerMax.Value, PublicFeatureConfig.RefillCrawlerWeight.Value)); CachedRefillConfigs.Add(new MonsterConfig("Centipede", PublicFeatureConfig.RefillCentipedeInit.Value, PublicFeatureConfig.RefillCentipedeMax.Value, PublicFeatureConfig.RefillCentipedeWeight.Value)); CachedRefillConfigs.Add(new MonsterConfig("HoarderBug", PublicFeatureConfig.RefillHoarderBugInit.Value, PublicFeatureConfig.RefillHoarderBugMax.Value, PublicFeatureConfig.RefillHoarderBugWeight.Value)); CachedRefillConfigs.Add(new MonsterConfig("Masked", PublicFeatureConfig.RefillMaskedInit.Value, PublicFeatureConfig.RefillMaskedMax.Value, PublicFeatureConfig.RefillMaskedWeight.Value)); CachedRefillConfigs.Add(new MonsterConfig("SandSpider", PublicFeatureConfig.RefillSandSpiderInit.Value, PublicFeatureConfig.RefillSandSpiderMax.Value, PublicFeatureConfig.RefillSandSpiderWeight.Value)); CachedRefillConfigs.Add(new MonsterConfig("Butler", PublicFeatureConfig.RefillButlerInit.Value, PublicFeatureConfig.RefillButlerMax.Value, PublicFeatureConfig.RefillButlerWeight.Value)); CachedRefillConfigs.Add(new MonsterConfig("Stingray", PublicFeatureConfig.RefillStingrayInit.Value, PublicFeatureConfig.RefillStingrayMax.Value, PublicFeatureConfig.RefillStingrayWeight.Value)); return CachedRefillConfigs; } [IteratorStateMachine(typeof(d__11))] private static IEnumerator SpawnEnemyCoroutine(EnemyType type, int count) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__11(0) { type = type, count = count }; } } [HarmonyPatch(typeof(RoundManager))] public static class WorldRulePatches { [HarmonyPatch("SpawnMapObjects")] [HarmonyPrefix] private static void SpawnMapObjectsPrefix(RoundManager __instance) { if (!((NetworkBehaviour)__instance).IsHost || !PublicFeatureConfig.DisableHazards.Value || __instance.currentLevel.indoorMapHazards == null) { return; } IndoorMapHazard[] indoorMapHazards = __instance.currentLevel.indoorMapHazards; foreach (IndoorMapHazard val in indoorMapHazards) { if (!((Object)(object)val.hazardType == (Object)null) && !((Object)(object)val.hazardType.prefabToSpawn == (Object)null)) { string name = ((Object)val.hazardType.prefabToSpawn).name; if (name.Contains("Landmine") || name.Contains("Turret") || name.Contains("Spike")) { val.numberToSpawn = AnimationCurve.Constant(0f, 1f, 0f); } } } } [HarmonyPatch("PlotOutEnemiesForNextHour")] [HarmonyPrefix] private static bool PlotOutEnemiesPrefix(RoundManager __instance) { if (((NetworkBehaviour)__instance).IsHost) { return !PublicFeatureConfig.DisableVanillaEnemies.Value; } return true; } [HarmonyPatch("SpawnEnemiesOutside")] [HarmonyPrefix] private static bool SpawnEnemiesOutsidePrefix(RoundManager __instance) { if (((NetworkBehaviour)__instance).IsHost) { return !PublicFeatureConfig.DisableVanillaEnemies.Value; } return true; } [HarmonyPatch("SpawnDaytimeEnemiesOutside")] [HarmonyPrefix] private static bool SpawnDaytimeEnemiesOutsidePrefix(RoundManager __instance) { if (((NetworkBehaviour)__instance).IsHost) { return !PublicFeatureConfig.DisableVanillaEnemies.Value; } return true; } [HarmonyPatch("SpawnScrapInLevel")] [HarmonyPrefix] private static bool SpawnScrapInLevelPrefix(RoundManager __instance) { if (((NetworkBehaviour)__instance).IsHost) { return !PublicFeatureConfig.DisableScrap.Value; } return true; } } [HarmonyPatch(typeof(Terminal), "Awake")] public static class PublicTerminalPatch { [HarmonyPostfix] private static void Postfix(Terminal __instance) { if (__instance.groupCredits == 60) { __instance.groupCredits = PublicFeatureConfig.StartingCredits.Value; } } } [HarmonyPatch(typeof(TimeOfDay), "Awake")] public static class PublicTimeOfDayPatch { [HarmonyPostfix] private static void Postfix(TimeOfDay __instance) { if (__instance.quotaVariables != null) { __instance.quotaVariables.startingCredits = PublicFeatureConfig.StartingCredits.Value; } } } [HarmonyPatch(typeof(DoorLock), "LockDoor")] public static class PublicDoorLockPatch { [HarmonyPostfix] private static void Postfix(DoorLock __instance) { if (PublicFeatureConfig.NoLockedDoors.Value && !((Object)(object)RoundManager.Instance == (Object)null) && (((NetworkBehaviour)RoundManager.Instance).IsServer || ((NetworkBehaviour)RoundManager.Instance).IsHost)) { __instance.UnlockDoorServerRpc(); } } } [HarmonyPatch(typeof(ButlerEnemyAI), "Start")] public static class PublicButlerDoorPatch { [HarmonyPostfix] private static void Postfix(ButlerEnemyAI __instance) { if (PublicFeatureConfig.ButlerInstantOpenDoors.Value) { ((EnemyAI)__instance).openDoorSpeedMultiplier = 100f; } } } [HarmonyPatch(typeof(PlayerControllerB), "DamagePlayerFromOtherClientClientRpc")] public static class PublicFriendlyFirePatch { [HarmonyPrefix] private static bool Prefix(PlayerControllerB __instance, int damageAmount, Vector3 hitDirection, int playerWhoHit, int newHealthAmount) { if (!PublicFeatureConfig.DisableFriendlyMeleeDamage.Value) { return true; } if (damageAmount <= 0 || (Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null) { return true; } CentipedeAI[] array = Object.FindObjectsByType((FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i].clingingToPlayer == (Object)(object)__instance) { return true; } } if (playerWhoHit >= 0 && playerWhoHit < StartOfRound.Instance.allPlayerScripts.Length) { PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[playerWhoHit]; if ((Object)(object)val != (Object)null && (Object)(object)val.currentlyHeldObjectServer != (Object)null && (val.currentlyHeldObjectServer is Shovel || val.currentlyHeldObjectServer is KnifeItem)) { return false; } } return true; } } [HarmonyPatch(typeof(ShotgunItem), "ShootGun")] public static class PublicNutcrackerShotgunPatch { [CompilerGenerated] private sealed class d__2 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public float effectSeverity; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__2(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(0.6f); <>1__state = 1; return true; case 1: <>1__state = -1; SoundManager.Instance.earsRingingTimer = effectSeverity; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [HarmonyPrefix] private static bool Prefix(ShotgunItem __instance, Vector3 shotgunPosition, Vector3 shotgunForward, ref RaycastHit[] ___enemyColliders, EnemyAI ___heldByEnemy) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (!PublicFeatureConfig.NutcrackerNoFriendlyFire.Value) { return true; } if (!(___heldByEnemy is NutcrackerEnemyAI)) { return true; } RunNutcrackerShotgunWithoutMonsterDamage(__instance, shotgunPosition, shotgunForward); return false; } private static void RunNutcrackerShotgunWithoutMonsterDamage(ShotgunItem shotgun, Vector3 shotgunPosition, Vector3 shotgunForward) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: 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_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0112: 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_0115: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: 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_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) shotgun.isReloading = false; bool flag = ((GrabbableObject)shotgun).isHeld && (Object)(object)((GrabbableObject)shotgun).playerHeldBy != (Object)null && (Object)(object)((GrabbableObject)shotgun).playerHeldBy == (Object)(object)GameNetworkManager.Instance.localPlayerController; if (flag) { ((GrabbableObject)shotgun).playerHeldBy.playerBodyAnimator.SetTrigger("ShootShotgun"); } RoundManager.PlayRandomClip(shotgun.gunShootAudio, shotgun.gunShootSFX, true, 1f, 1840, 1000); WalkieTalkie.TransmitOneShotAudio(shotgun.gunShootAudio, shotgun.gunShootSFX[0], 1f); shotgun.gunShootParticle.Play(true); shotgun.shellsLoaded = Mathf.Clamp(shotgun.shellsLoaded - 1, 0, 2); PlayerControllerB localPlayerController = GameNetworkManager.Instance.localPlayerController; if (!((Object)(object)localPlayerController == (Object)null)) { float num = Vector3.Distance(((Component)localPlayerController).transform.position, ((Component)shotgun.shotgunRayPoint).transform.position); bool flag2 = false; int num2 = 0; float num3 = 0f; Vector3 val = localPlayerController.playerCollider.ClosestPoint(shotgunPosition); if (!flag && !Physics.Linecast(shotgunPosition, val, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1) && Vector3.Angle(shotgunForward, val - shotgunPosition) < 30f) { flag2 = true; } if (num < 5f) { num3 = 0.8f; HUDManager.Instance.ShakeCamera((ScreenShakeType)1); num2 = 100; } if (num < 15f) { num3 = 0.5f; HUDManager.Instance.ShakeCamera((ScreenShakeType)1); num2 = 100; } else if (num < 23f) { HUDManager.Instance.ShakeCamera((ScreenShakeType)0); num2 = 40; } else if (num < 30f) { num2 = 20; } if (num3 > 0f && SoundManager.Instance.timeSinceEarsStartedRinging > 16f) { ((MonoBehaviour)shotgun).StartCoroutine(DelayedEarsRinging(num3)); } Ray val2 = default(Ray); ((Ray)(ref val2))..ctor(shotgunPosition, shotgunForward); RaycastHit val3 = default(RaycastHit); if (Physics.Raycast(val2, ref val3, 30f, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1)) { ((Component)shotgun.gunBulletsRicochetAudio).transform.position = ((Ray)(ref val2)).GetPoint(((RaycastHit)(ref val3)).distance - 0.5f); shotgun.gunBulletsRicochetAudio.Play(); } if (flag2) { localPlayerController.DamagePlayer(num2, true, true, (CauseOfDeath)7, 0, false, shotgun.shotgunRayPoint.forward * 30f); } } } [IteratorStateMachine(typeof(d__2))] private static IEnumerator DelayedEarsRinging(float effectSeverity) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__2(0) { effectSeverity = effectSeverity }; } } public static class ItemAndLootPatches { private static class HoarderBugItemCacheCleaner { public static void CleanupHoarderBugItemCaches() { if (HoarderBugAI.HoarderBugItems != null) { HoarderBugAI.HoarderBugItems.RemoveAll((HoarderBugItem item) => item == null || (Object)(object)item.itemGrabbableObject == (Object)null || (Object)(object)((Component)item.itemGrabbableObject).gameObject == (Object)null || item.itemGrabbableObject.deactivated || item.itemGrabbableObject.itemUsedUp); } if (HoarderBugAI.grabbableObjectsInMap != null) { HoarderBugAI.grabbableObjectsInMap.RemoveAll((GameObject obj) => (Object)(object)obj == (Object)null); } CleanupLiveHoarderBugReferences(null); } public static void RemoveHoarderBugItem(GrabbableObject grabbable) { if ((Object)(object)grabbable == (Object)null) { return; } if (HoarderBugAI.HoarderBugItems != null) { HoarderBugAI.HoarderBugItems.RemoveAll((HoarderBugItem item) => item == null || (Object)(object)item.itemGrabbableObject == (Object)null || (Object)(object)item.itemGrabbableObject == (Object)(object)grabbable); } if (HoarderBugAI.grabbableObjectsInMap != null) { GameObject grabbableObject = ((Component)grabbable).gameObject; HoarderBugAI.grabbableObjectsInMap.RemoveAll((GameObject obj) => (Object)(object)obj == (Object)null || (Object)(object)obj == (Object)(object)grabbableObject); } CleanupLiveHoarderBugReferences(grabbable); } private static void CleanupLiveHoarderBugReferences(GrabbableObject grabbable) { HoarderBugAI[] array = Object.FindObjectsByType((FindObjectsSortMode)0); foreach (HoarderBugAI val in array) { if ((Object)(object)val == (Object)null) { continue; } if ((Object)(object)val.targetItem == (Object)null || (Object)(object)val.targetItem == (Object)(object)grabbable) { val.targetItem = null; } if (val.heldItem != null) { GrabbableObject itemGrabbableObject = val.heldItem.itemGrabbableObject; if ((Object)(object)itemGrabbableObject == (Object)null || (Object)(object)itemGrabbableObject == (Object)(object)grabbable) { val.heldItem = null; } } } } } [HarmonyPatch(typeof(HoarderBugAI), "DoAIInterval")] private static class HoarderBugStaleItemCleanupPatch { [HarmonyPrefix] private static void Prefix() { HoarderBugItemCacheCleaner.CleanupHoarderBugItemCaches(); } } [HarmonyPatch(typeof(GrabbableObject), "OnDestroy")] private static class HoarderBugDestroyedItemCleanupPatch { [HarmonyPrefix] private static void Prefix(GrabbableObject __instance) { HoarderBugItemCacheCleaner.RemoveHoarderBugItem(__instance); } } [HarmonyPatch(typeof(NutcrackerEnemyAI), "DropGun")] private static class NutcrackerDropGunPatch { [HarmonyPrefix] private static bool Prefix(NutcrackerEnemyAI __instance) { if (PublicFeatureConfig.NutcrackerDropGunAndShells.Value) { return true; } if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.gun == (Object)null) { return false; } ShotgunItem gun = __instance.gun; __instance.gun = null; if (((NetworkBehaviour)__instance).IsServer || ((NetworkBehaviour)__instance).IsHost) { if ((Object)(object)((NetworkBehaviour)gun).NetworkObject != (Object)null && ((NetworkBehaviour)gun).NetworkObject.IsSpawned) { ((NetworkBehaviour)gun).NetworkObject.Despawn(true); } else if ((Object)(object)((Component)gun).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)gun).gameObject); } } return false; } } [HarmonyPatch(typeof(NutcrackerEnemyAI), "SpawnShotgunShells")] private static class NutcrackerShellDropPatch { [HarmonyPrefix] private static bool Prefix() { return PublicFeatureConfig.NutcrackerDropGunAndShells.Value; } } [HarmonyPatch(typeof(ButlerEnemyAI), "ButlerBlowUpAndPop")] private static class ButlerDeathDropPatch { [CompilerGenerated] private sealed class d__1 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ButlerEnemyAI instance; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__1(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; ((EnemyAI)instance).creatureAnimator.SetTrigger("Popping"); ((EnemyAI)instance).creatureAnimator.SetLayerWeight(1, 0f); instance.popAudio.PlayOneShot(((EnemyAI)instance).enemyType.audioClips[3]); <>2__current = (object)new WaitForSeconds(1.1f); <>1__state = 1; return true; case 1: { <>1__state = -1; ((EnemyAI)instance).creatureAnimator.SetBool("popFinish", true); instance.popAudio.Play(); instance.popAudioFar.Play(); instance.popParticle.Play(true); float num = Vector3.Distance(((Component)GameNetworkManager.Instance.localPlayerController).transform.position, ((Component)instance).transform.position); if (num < 8f) { Landmine.SpawnExplosion(((Component)instance).transform.position + Vector3.up * 0.15f, false, 0f, 2f, 30, 80f, (GameObject)null, false); HUDManager.Instance.ShakeCamera((ScreenShakeType)1); SoundManager.Instance.earsRingingTimer = 0.8f; } else if (num < 27f) { HUDManager.Instance.ShakeCamera((ScreenShakeType)1); } return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [HarmonyPrefix] private static bool Prefix(ButlerEnemyAI __instance, ref IEnumerator __result) { if (PublicFeatureConfig.ButlerDropKnifeAndBees.Value) { return true; } __result = ButlerBlowUpWithoutDrops(__instance); return false; } [IteratorStateMachine(typeof(d__1))] private static IEnumerator ButlerBlowUpWithoutDrops(ButlerEnemyAI instance) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__1(0) { instance = instance }; } } [HarmonyPatch(typeof(SandSpiderAI), "KillEnemy")] private static class SandSpiderWebCleanupPatch { [HarmonyPostfix] private static void Postfix(SandSpiderAI __instance) { if ((Object)(object)__instance == (Object)null || (!((NetworkBehaviour)__instance).IsServer && !((NetworkBehaviour)__instance).IsHost)) { return; } try { for (int num = __instance.webTraps.Count - 1; num >= 0; num--) { __instance.BreakWebServerRpc(num, 1); } } catch (Exception arg) { PublicFeatureRuntime.Logger.LogWarning((object)$"[SandSpider] Failed to clear webs on death: {arg}"); } } } [HarmonyPatch(typeof(EnemyAI), "KillEnemy")] private static class EnemyKillDropPatch { [HarmonyPostfix] private static void Postfix(EnemyAI __instance, bool destroy) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null) { return; } if (!destroy) { TryScheduleCorpseCleanup(__instance); } if (!destroy && PublicFeatureConfig.LootDropsEnabled.Value && !((Object)(object)RoundManager.Instance == (Object)null) && ((NetworkBehaviour)RoundManager.Instance).IsServer && !((Object)(object)__instance.enemyType == (Object)null) && SupportsCombatDrops(__instance) && !PublicFeatureRuntime.IsPrivateBossEnemy(__instance)) { EnsureLootItemsCached(); if ((Object)(object)_tzpItem != (Object)null && Random.value <= PublicFeatureConfig.TZPDropChance.Value) { SpawnLoot(((Component)__instance).transform.position, _tzpItem); } if ((Object)(object)_stunGrenadeItem != (Object)null && Random.value <= PublicFeatureConfig.StunGrenadeDropChance.Value) { SpawnLoot(((Component)__instance).transform.position, _stunGrenadeItem); } } } } [HarmonyPatch(typeof(PlayerControllerB), "DamagePlayerClientRpc")] private static class PlayerDamageSyncPatch { [HarmonyPostfix] private static void Postfix(PlayerControllerB __instance, int newHealthAmount) { if (((NetworkBehaviour)__instance).IsOwner && newHealthAmount >= 20 && __instance.criticallyInjured) { __instance.criticallyInjured = false; if ((Object)(object)__instance.playerBodyAnimator != (Object)null) { __instance.playerBodyAnimator.SetBool("Limp", false); } __instance.bleedingHeavily = false; } } } [HarmonyPatch(typeof(TetraChemicalItem), "Update")] private static class TZPHealingPatch { [HarmonyPostfix] private static void Postfix(TetraChemicalItem __instance) { if ((Object)(object)__instance == (Object)null || (Object)(object)((NetworkBehaviour)__instance).NetworkObject == (Object)null || !PublicFeatureConfig.TZPHealingEnabled.Value || (Object)(object)((NetworkBehaviour)__instance).NetworkManager == (Object)null || !((NetworkBehaviour)__instance).NetworkManager.IsServer) { return; } if (UsedTZPIndices.Contains(((NetworkBehaviour)__instance).NetworkObjectId)) { ulong networkObjectId = ((NetworkBehaviour)__instance).NetworkObjectId; if ((Object)(object)((GrabbableObject)__instance).playerHeldBy == (Object)null) { if (!UsedTZPOnGroundSince.TryGetValue(networkObjectId, out var value)) { UsedTZPOnGroundSince[networkObjectId] = Time.time; } else if (Time.time - value > 2f) { UsedTZPOnGroundSince.Remove(networkObjectId); if (((NetworkBehaviour)__instance).NetworkObject.IsSpawned) { ((NetworkBehaviour)__instance).NetworkObject.Despawn(true); } } } else { UsedTZPOnGroundSince.Remove(networkObjectId); } } else { if (!(bool)AccessTools.Field(typeof(TetraChemicalItem), "emittingGas").GetValue(__instance) || (Object)(object)((GrabbableObject)__instance).playerHeldBy == (Object)null) { return; } PlayerControllerB playerHeldBy = ((GrabbableObject)__instance).playerHeldBy; if (!playerHeldBy.isPlayerDead) { HealPlayerFromTzpNetworked(playerHeldBy, PublicFeatureConfig.TZPHealAmount.Value); if (playerHeldBy.criticallyInjured) { playerHeldBy.MakeCriticallyInjured(false); } UsedTZPIndices.Add(((NetworkBehaviour)__instance).NetworkObjectId); } } } } [CompilerGenerated] private sealed class d__15 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ulong playerId; public PlayerControllerB player; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__15(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(1.25f); <>1__state = 1; return true; case 1: { <>1__state = -1; PendingTzpCriticalHealCoroutines.Remove(playerId); if (!PendingTzpCriticalHealTargets.TryGetValue(playerId, out var value)) { return false; } PendingTzpCriticalHealTargets.Remove(playerId); if ((Object)(object)player == (Object)null || player.isPlayerDead) { return false; } if (player.health < 19 || player.health > 20) { return false; } value = Mathf.Clamp(value, 20, 100); if (value <= player.health) { if (player.health >= 20) { player.DamagePlayerClientRpc(0, player.health); } return false; } player.DamagePlayerClientRpc(-(value - player.health), value); return false; } } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__11 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public float delay; public EnemyAI enemy; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__11(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(delay); <>1__state = 1; return true; case 1: <>1__state = -1; if ((Object)(object)enemy == (Object)null) { return false; } if ((Object)(object)((NetworkBehaviour)enemy).NetworkObject != (Object)null && ((NetworkBehaviour)enemy).NetworkObject.IsSpawned && (((NetworkBehaviour)enemy).IsServer || ((NetworkBehaviour)enemy).IsHost)) { ((NetworkBehaviour)enemy).NetworkObject.Despawn(true); return false; } if ((Object)(object)((Component)enemy).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)enemy).gameObject); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly HashSet UsedTZPIndices = new HashSet(); private static readonly Dictionary UsedTZPOnGroundSince = new Dictionary(); private static readonly Dictionary PendingTzpCriticalHealTargets = new Dictionary(); private static readonly Dictionary PendingTzpCriticalHealCoroutines = new Dictionary(); private static Item _tzpItem; private static Item _stunGrenadeItem; public static void ResetRoundState() { UsedTZPIndices.Clear(); UsedTZPOnGroundSince.Clear(); PendingTzpCriticalHealTargets.Clear(); foreach (Coroutine value in PendingTzpCriticalHealCoroutines.Values) { if (value != null && (Object)(object)PublicFeatureRuntime.PluginInstance != (Object)null) { ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StopCoroutine(value); } } PendingTzpCriticalHealCoroutines.Clear(); _tzpItem = null; _stunGrenadeItem = null; } private static void EnsureLootItemsCached() { if (((Object)(object)_tzpItem != (Object)null && (Object)(object)_stunGrenadeItem != (Object)null) || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)StartOfRound.Instance.allItemsList == (Object)null) { return; } foreach (Item items in StartOfRound.Instance.allItemsList.itemsList) { if ((Object)(object)_tzpItem == (Object)null && items.itemName == "TZP-Inhalant") { _tzpItem = items; } else if ((Object)(object)_stunGrenadeItem == (Object)null && items.itemName == "Stun grenade") { _stunGrenadeItem = items; } } } private static bool SupportsCombatDrops(EnemyAI enemy) { if (!(enemy is FlowermanAI) && !(enemy is ButlerEnemyAI) && !(enemy is NutcrackerEnemyAI) && !(enemy is MaskedPlayerEnemy) && !(enemy is SandSpiderAI) && !(enemy is CentipedeAI) && !(enemy is CrawlerAI) && !(enemy is HoarderBugAI) && !(enemy is BaboonBirdAI) && !(enemy is StingrayAI) && !(enemy is CaveDwellerAI)) { return enemy is GiantKiwiAI; } return true; } private static bool SupportsCorpseCleanup(EnemyAI enemy) { if (!(enemy is FlowermanAI) && !(enemy is ButlerEnemyAI) && !(enemy is NutcrackerEnemyAI) && !(enemy is MaskedPlayerEnemy) && !(enemy is SandSpiderAI) && !(enemy is CentipedeAI) && !(enemy is CrawlerAI) && !(enemy is HoarderBugAI) && !(enemy is BaboonBirdAI) && !(enemy is StingrayAI) && !(enemy is CaveDwellerAI)) { return enemy is GiantKiwiAI; } return true; } private static void TryScheduleCorpseCleanup(EnemyAI enemy) { if (!PublicFeatureConfig.CorpseCleanupEnabled.Value || (Object)(object)enemy == (Object)null || !SupportsCorpseCleanup(enemy) || PublicFeatureRuntime.IsPrivateBossEnemy(enemy) || (!((NetworkBehaviour)enemy).IsServer && !((NetworkBehaviour)enemy).IsHost)) { return; } try { if (Traverse.Create((object)enemy).Field("deathCoroutine").Value == null) { float delay = Mathf.Max(0f, PublicFeatureConfig.CorpseCleanupDelay.Value); Coroutine value = ((MonoBehaviour)enemy).StartCoroutine(CorpseCleanupCoroutine(enemy, delay)); Traverse.Create((object)enemy).Field("deathCoroutine").SetValue((object)value); } } catch (Exception arg) { PublicFeatureRuntime.Logger.LogError((object)$"[CorpseCleanup] Failed to schedule cleanup for {enemy.enemyType?.enemyName ?? ((Object)enemy).name}: {arg}"); } } [IteratorStateMachine(typeof(d__11))] private static IEnumerator CorpseCleanupCoroutine(EnemyAI enemy, float delay) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__11(0) { enemy = enemy, delay = delay }; } private static void SpawnLoot(Vector3 position, Item item) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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 ((Object)(object)item == (Object)null || (Object)(object)item.spawnPrefab == (Object)null) { return; } try { Vector3 val = position + Vector3.up * 0.5f + Random.insideUnitSphere * 0.2f; GameObject val2 = Object.Instantiate(item.spawnPrefab, val, Random.rotation); GrabbableObject component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { component.startFallingPosition = val; component.FallToGround(true, true, default(Vector3)); NetworkObject component2 = val2.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.Spawn(true); } } } catch (Exception arg) { PublicFeatureRuntime.Logger.LogError((object)$"[LootDrops] Failed to spawn {item.itemName}: {arg}"); } } private static void HealPlayerFromTzpNetworked(PlayerControllerB player, int amount) { if ((Object)(object)player == (Object)null || player.isPlayerDead || amount <= 0) { return; } ulong playerClientId = player.playerClientId; if (PendingTzpCriticalHealTargets.TryGetValue(playerClientId, out var value)) { int num = Mathf.Clamp(Mathf.Max(player.health, value) + amount, 20, 100); if (num > value) { PendingTzpCriticalHealTargets[playerClientId] = num; } return; } int health = player.health; int num2 = Mathf.Clamp(health + amount, 0, 100); if (num2 <= health) { return; } if (player.health > 0 && (player.health < 20 || player.criticallyInjured) && num2 >= 20) { if (player.health < 19) { player.DamagePlayerClientRpc(-(19 - player.health), 19); } else if (player.health != 19) { player.DamagePlayerClientRpc(0, 19); } SchedulePendingTzpCriticalHeal(player, num2); } else { player.DamagePlayerClientRpc(-(num2 - player.health), num2); } } private static void SchedulePendingTzpCriticalHeal(PlayerControllerB player, int finalHealth) { if (!((Object)(object)player == (Object)null) && !player.isPlayerDead && !((Object)(object)PublicFeatureRuntime.PluginInstance == (Object)null)) { ulong playerClientId = player.playerClientId; PendingTzpCriticalHealTargets[playerClientId] = finalHealth; if (!PendingTzpCriticalHealCoroutines.TryGetValue(playerClientId, out var value) || value == null) { PendingTzpCriticalHealCoroutines[playerClientId] = ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StartCoroutine(ApplyPendingTzpCriticalHeal(player, playerClientId)); } } } [IteratorStateMachine(typeof(d__15))] private static IEnumerator ApplyPendingTzpCriticalHeal(PlayerControllerB player, ulong playerId) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__15(0) { player = player, playerId = playerId }; } } } namespace PublicCombatMod { [BepInPlugin("com.lengjing.lethalbattletweaks", "LethalBattleTweaks", "1.0.2")] public class Plugin : BaseUnityPlugin { private Harmony _harmony; private void Awake() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown _harmony = new Harmony("com.lengjing.lethalbattletweaks"); _harmony.PatchAll(); PublicFeatureRuntime.Initialize((BaseUnityPlugin)(object)this, PublicFeatureMode.PublicStandalone, ((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Logger.LogInfo((object)"LethalBattleTweaks loaded."); } private void Start() { PublicFeatureRuntime.OnPluginStart(); } private void OnDestroy() { PublicFeatureRuntime.Shutdown(); } } }