Please disclose if any significant portion of your mod was created 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 LethalBattleTweaks v1.0.4
LethalBattleTweaks.dll
Decompiled 6 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; 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; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; [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.7.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.7.0")] namespace CombatSharedFeatures { public static class PublicFeatureConfig { public static ConfigEntry<bool> RefillEnabled { get; private set; } public static ConfigEntry<float> RefillInterval { get; private set; } public static ConfigEntry<int> RefillTotalCap { get; private set; } public static ConfigEntry<int> RefillDelay { get; private set; } public static ConfigEntry<float> SpawnDelay { get; private set; } public static ConfigEntry<int> RefillBatchSize { get; private set; } public static ConfigEntry<int> RefillFlowermanInit { get; private set; } public static ConfigEntry<int> RefillFlowermanMax { get; private set; } public static ConfigEntry<int> RefillFlowermanWeight { get; private set; } public static ConfigEntry<int> RefillNutcrackerInit { get; private set; } public static ConfigEntry<int> RefillNutcrackerMax { get; private set; } public static ConfigEntry<int> RefillNutcrackerWeight { get; private set; } public static ConfigEntry<int> RefillCrawlerInit { get; private set; } public static ConfigEntry<int> RefillCrawlerMax { get; private set; } public static ConfigEntry<int> RefillCrawlerWeight { get; private set; } public static ConfigEntry<int> RefillCentipedeInit { get; private set; } public static ConfigEntry<int> RefillCentipedeMax { get; private set; } public static ConfigEntry<int> RefillCentipedeWeight { get; private set; } public static ConfigEntry<int> RefillHoarderBugInit { get; private set; } public static ConfigEntry<int> RefillHoarderBugMax { get; private set; } public static ConfigEntry<int> RefillHoarderBugWeight { get; private set; } public static ConfigEntry<int> RefillMaskedInit { get; private set; } public static ConfigEntry<int> RefillMaskedMax { get; private set; } public static ConfigEntry<int> RefillMaskedWeight { get; private set; } public static ConfigEntry<int> RefillSandSpiderInit { get; private set; } public static ConfigEntry<int> RefillSandSpiderMax { get; private set; } public static ConfigEntry<int> RefillSandSpiderWeight { get; private set; } public static ConfigEntry<int> RefillButlerInit { get; private set; } public static ConfigEntry<int> RefillButlerMax { get; private set; } public static ConfigEntry<int> RefillButlerWeight { get; private set; } public static ConfigEntry<int> RefillStingrayInit { get; private set; } public static ConfigEntry<int> RefillStingrayMax { get; private set; } public static ConfigEntry<int> RefillStingrayWeight { get; private set; } public static ConfigEntry<int> StartingCredits { get; private set; } public static ConfigEntry<bool> DisableHazards { get; private set; } public static ConfigEntry<bool> DisableVanillaEnemies { get; private set; } public static ConfigEntry<bool> DisableScrap { get; private set; } public static ConfigEntry<bool> NoLockedDoors { get; private set; } public static ConfigEntry<bool> DisableFriendlyMeleeDamage { get; private set; } public static ConfigEntry<bool> ButlerInstantOpenDoors { get; private set; } public static ConfigEntry<bool> NutcrackerNoFriendlyFire { get; private set; } public static ConfigEntry<bool> NutcrackerDropGunAndShells { get; private set; } public static ConfigEntry<bool> ButlerDropKnifeAndBees { get; private set; } public static ConfigEntry<bool> TZPHealingEnabled { get; private set; } public static ConfigEntry<int> TZPHealAmount { get; private set; } public static ConfigEntry<bool> LootDropsEnabled { get; private set; } public static ConfigEntry<float> TZPDropChance { get; private set; } public static ConfigEntry<float> StunGrenadeDropChance { get; private set; } public static ConfigEntry<bool> CorpseCleanupEnabled { get; private set; } public static ConfigEntry<float> CorpseCleanupDelay { get; private set; } public static ConfigEntry<bool> CurfewEnabled { get; private set; } public static ConfigEntry<float> CurfewDuration { get; private set; } public static ConfigEntry<float> CurfewRepeatInterval { get; private set; } public static ConfigEntry<bool> ScoreboardEnabled { get; private set; } public static ConfigEntry<bool> MvpEnabled { get; private set; } public static ConfigEntry<Key> ScoreboardKey { get; private set; } public static ConfigEntry<int> ScoreWeight { get; private set; } public static ConfigEntry<int> HitWeight { get; private set; } public static ConfigEntry<bool> ScoreboardBroadcastEnabled { get; private set; } public static ConfigEntry<float> ScoreboardBroadcastInterval { get; private set; } public static ConfigEntry<int> ScoreboardBroadcastTopCount { get; private set; } public static ConfigEntry<int> ScoreFlowerman { get; private set; } public static ConfigEntry<int> ScoreNutcracker { get; private set; } public static ConfigEntry<int> ScoreCrawler { get; private set; } public static ConfigEntry<int> ScoreCentipede { get; private set; } public static ConfigEntry<int> ScoreHoarderBug { get; private set; } public static ConfigEntry<int> ScoreMasked { get; private set; } public static ConfigEntry<int> ScoreSandSpider { get; private set; } public static ConfigEntry<int> ScoreButler { get; private set; } public static ConfigEntry<int> ScoreStingray { get; private set; } public static ConfigEntry<bool> RescueEnabled { get; private set; } public static ConfigEntry<float> RescueImmunityDuration { get; private set; } public static ConfigEntry<bool> EnableCrawlerScale { get; private set; } public static ConfigEntry<float> CrawlerScale { get; private set; } public static ConfigEntry<bool> EnableButlerScale { get; private set; } public static ConfigEntry<float> ButlerScale { get; private set; } private static string Bilingual(string en, string zh) { return en + " / " + zh; } public static void Bind(ConfigFile config) { //IL_08d2: Unknown result type (might be due to invalid IL or missing references) //IL_08dc: Expected O, but got Unknown //IL_0909: Unknown result type (might be due to invalid IL or missing references) //IL_0913: Expected O, but got Unknown //IL_0abd: Unknown result type (might be due to invalid IL or missing references) //IL_0ac7: Expected O, but got Unknown //IL_0b24: Unknown result type (might be due to invalid IL or missing references) //IL_0b2e: Expected O, but got Unknown //IL_0b8b: Unknown result type (might be due to invalid IL or missing references) //IL_0b95: Expected O, but got Unknown string text = "Enemy Refill"; RefillEnabled = config.Bind<bool>(text, "Enabled", true, Bilingual("Enable dynamic indoor enemy refill.", "启用设施内动态补怪。")); RefillInterval = config.Bind<float>(text, "Interval", 5f, Bilingual("Seconds between refill checks.", "每次检查是否需要补怪的间隔秒数。")); RefillTotalCap = config.Bind<int>(text, "TotalCap", 18, Bilingual("Total indoor enemies the refill system tries to maintain.", "补怪系统尝试维持的设施内怪物总上限。")); RefillDelay = config.Bind<int>(text, "RefillDelay", 10, Bilingual("Delay after landing before refill starts.", "飞船落地后开始补怪前的延迟秒数。")); SpawnDelay = config.Bind<float>(text, "SpawnDelay", 0.3f, Bilingual("Delay between spawned enemies during a refill batch.", "单次补怪批次中每只怪之间的生成间隔。")); RefillBatchSize = config.Bind<int>(text, "BatchSize", 3, Bilingual("Maximum enemies spawned per refill batch.", "每次补怪批次最多生成的怪物数量。")); RefillFlowermanInit = config.Bind<int>("Enemy Refill - Flowerman", "Init", 0, Bilingual("Initial Flowerman count.", "开局额外生成的小黑数量。")); RefillFlowermanMax = config.Bind<int>("Enemy Refill - Flowerman", "Max", 1, Bilingual("Maximum Flowerman refill count.", "小黑可被补怪到的最大数量。")); RefillFlowermanWeight = config.Bind<int>("Enemy Refill - Flowerman", "Weight", 10, Bilingual("Flowerman refill weight.", "小黑的补怪权重。")); RefillNutcrackerInit = config.Bind<int>("Enemy Refill - Nutcracker", "Init", 0, Bilingual("Initial Nutcracker count.", "开局额外生成的胡桃夹子数量。")); RefillNutcrackerMax = config.Bind<int>("Enemy Refill - Nutcracker", "Max", 1, Bilingual("Maximum Nutcracker refill count.", "胡桃夹子可被补怪到的最大数量。")); RefillNutcrackerWeight = config.Bind<int>("Enemy Refill - Nutcracker", "Weight", 10, Bilingual("Nutcracker refill weight.", "胡桃夹子的补怪权重。")); RefillCrawlerInit = config.Bind<int>("Enemy Refill - Crawler", "Init", 0, Bilingual("Initial Crawler count.", "开局额外生成的半身鱼数量。")); RefillCrawlerMax = config.Bind<int>("Enemy Refill - Crawler", "Max", 1, Bilingual("Maximum Crawler refill count.", "半身鱼可被补怪到的最大数量。")); RefillCrawlerWeight = config.Bind<int>("Enemy Refill - Crawler", "Weight", 10, Bilingual("Crawler refill weight.", "半身鱼的补怪权重。")); RefillCentipedeInit = config.Bind<int>("Enemy Refill - Centipede", "Init", 0, Bilingual("Initial Centipede count.", "开局额外生成的抱脸虫数量。")); RefillCentipedeMax = config.Bind<int>("Enemy Refill - Centipede", "Max", 2, Bilingual("Maximum Centipede refill count.", "抱脸虫可被补怪到的最大数量。")); RefillCentipedeWeight = config.Bind<int>("Enemy Refill - Centipede", "Weight", 10, Bilingual("Centipede refill weight.", "抱脸虫的补怪权重。")); RefillHoarderBugInit = config.Bind<int>("Enemy Refill - HoarderBug", "Init", 0, Bilingual("Initial HoarderBug count.", "开局额外生成的囤积虫数量。")); RefillHoarderBugMax = config.Bind<int>("Enemy Refill - HoarderBug", "Max", 3, Bilingual("Maximum HoarderBug refill count.", "囤积虫可被补怪到的最大数量。")); RefillHoarderBugWeight = config.Bind<int>("Enemy Refill - HoarderBug", "Weight", 10, Bilingual("HoarderBug refill weight.", "囤积虫的补怪权重。")); RefillMaskedInit = config.Bind<int>("Enemy Refill - Masked", "Init", 0, Bilingual("Initial Masked count.", "开局额外生成的假人数量。")); RefillMaskedMax = config.Bind<int>("Enemy Refill - Masked", "Max", 1, Bilingual("Maximum Masked refill count.", "假人可被补怪到的最大数量。")); RefillMaskedWeight = config.Bind<int>("Enemy Refill - Masked", "Weight", 10, Bilingual("Masked refill weight.", "假人的补怪权重。")); RefillSandSpiderInit = config.Bind<int>("Enemy Refill - SandSpider", "Init", 0, Bilingual("Initial SandSpider count.", "开局额外生成的蜘蛛数量。")); RefillSandSpiderMax = config.Bind<int>("Enemy Refill - SandSpider", "Max", 2, Bilingual("Maximum SandSpider refill count.", "蜘蛛可被补怪到的最大数量。")); RefillSandSpiderWeight = config.Bind<int>("Enemy Refill - SandSpider", "Weight", 10, Bilingual("SandSpider refill weight.", "蜘蛛的补怪权重。")); RefillButlerInit = config.Bind<int>("Enemy Refill - Butler", "Init", 0, Bilingual("Initial Butler count.", "开局额外生成的管家数量。")); RefillButlerMax = config.Bind<int>("Enemy Refill - Butler", "Max", 2, Bilingual("Maximum Butler refill count.", "管家可被补怪到的最大数量。")); RefillButlerWeight = config.Bind<int>("Enemy Refill - Butler", "Weight", 10, Bilingual("Butler refill weight.", "管家的补怪权重。")); RefillStingrayInit = config.Bind<int>("Enemy Refill - Stingray", "Init", 0, Bilingual("Initial Stingray count.", "开局额外生成的黏滑鱼数量。")); RefillStingrayMax = config.Bind<int>("Enemy Refill - Stingray", "Max", 1, Bilingual("Maximum Stingray refill count.", "黏滑鱼可被补怪到的最大数量。")); RefillStingrayWeight = config.Bind<int>("Enemy Refill - Stingray", "Weight", 10, Bilingual("Stingray refill weight.", "黏滑鱼的补怪权重。")); StartingCredits = config.Bind<int>("World Rules", "StartingCredits", 60, Bilingual("Starting company credits.", "开局公司金钱。")); DisableHazards = config.Bind<bool>("World Rules", "DisableHazards", false, Bilingual("Disable landmines, turrets, and spikes.", "禁用地雷、炮塔和尖刺。")); DisableVanillaEnemies = config.Bind<bool>("World Rules", "DisableVanillaEnemies", false, Bilingual("Disable vanilla natural enemy spawning.", "禁用原版自然刷怪。")); DisableScrap = config.Bind<bool>("World Rules", "DisableScrap", false, Bilingual("Disable indoor scrap spawning.", "禁用室内废料刷新。")); NoLockedDoors = config.Bind<bool>("World Rules", "NoLockedDoors", true, Bilingual("Automatically unlock locked doors.", "自动解锁上锁的门。")); DisableFriendlyMeleeDamage = config.Bind<bool>("World Rules", "DisableFriendlyMeleeDamage", true, Bilingual("Disable shovel and knife friendly fire.", "禁用铲子和小刀的玩家友伤。")); ButlerInstantOpenDoors = config.Bind<bool>("World Rules", "ButlerInstantOpenDoors", true, Bilingual("Make butlers open doors almost instantly.", "让管家几乎瞬间开门。")); NutcrackerNoFriendlyFire = config.Bind<bool>("World Rules", "NutcrackerNoFriendlyFire", true, Bilingual("Prevent nutcracker shotgun blasts from damaging monsters while still allowing player damage.", "让胡桃夹子不会伤害怪物,但仍然可以伤害玩家。")); NutcrackerDropGunAndShells = config.Bind<bool>("World Rules", "NutcrackerDropGunAndShells", false, Bilingual("Allow nutcrackers to drop their shotgun and shotgun shells on death.", "允许胡桃夹子死亡时掉落枪和子弹。")); ButlerDropKnifeAndBees = config.Bind<bool>("World Rules", "ButlerDropKnifeAndBees", false, Bilingual("Allow butlers to spawn their knife and bees on death.", "允许管家死亡时掉落菜刀并生成蜜蜂。")); TZPHealingEnabled = config.Bind<bool>("TZP Healing", "Enabled", true, Bilingual("Allow TZP inhalant to heal once per canister.", "允许每罐 TZP 提供一次回血。")); TZPHealAmount = config.Bind<int>("TZP Healing", "HealAmount", 20, Bilingual("Healing amount per TZP use.", "每次 TZP 触发时的回血量。")); LootDropsEnabled = config.Bind<bool>("Loot Drops", "Enabled", true, Bilingual("Allow enemies to drop combat supplies.", "允许敌人掉落战斗补给。")); TZPDropChance = config.Bind<float>("Loot Drops", "TZPDropChance", 0.1f, Bilingual("Chance for supported enemies to drop TZP.", "支持的敌人掉落 TZP 的概率。")); StunGrenadeDropChance = config.Bind<float>("Loot Drops", "StunGrenadeDropChance", 0.05f, Bilingual("Chance for supported enemies to drop a stun grenade.", "支持的敌人掉落闪光弹的概率。")); CorpseCleanupEnabled = config.Bind<bool>("Corpse Cleanup", "Enabled", true, Bilingual("Automatically remove enemy corpses after a delay.", "延迟一段时间后自动清理怪物尸体。")); CorpseCleanupDelay = config.Bind<float>("Corpse Cleanup", "Delay", 5f, Bilingual("Delay in seconds before supported enemy corpses are removed.", "支持的怪物尸体在被清理前等待的秒数。")); CurfewEnabled = config.Bind<bool>("Curfew Execution", "Enabled", false, Bilingual("After landing, execute players who remain outside the facility after the timer, including players idling on the ship.", "飞船落地倒计时结束后处决仍在设施外的玩家,飞船内挂机玩家也会被处决。")); CurfewDuration = config.Bind<float>("Curfew Execution", "Duration", 60f, Bilingual("Seconds after landing before players outside the facility are condemned.", "飞船落地后多少秒开始处决设施外玩家。")); CurfewRepeatInterval = config.Bind<float>("Curfew Execution", "RepeatInterval", 5f, Bilingual("Seconds between judge dog retry attempts and outside checks.", "审判犬失败重试和室外检查的间隔秒数。")); ScoreboardEnabled = config.Bind<bool>("Combat Scoreboard", "Enabled", true, Bilingual("Enable the host combat scoreboard.", "启用主机战斗记分面板。")); MvpEnabled = config.Bind<bool>("Combat Scoreboard", "MvpEnabled", true, Bilingual("Broadcast the round MVP in chat at game over.", "游戏结束时在聊天栏播报本局 MVP。")); ScoreboardKey = config.Bind<Key>("Combat Scoreboard", "ToggleKey", (Key)16, Bilingual("Keyboard key used to show or hide the scoreboard.", "显示或隐藏记分面板的按键。")); ScoreWeight = config.Bind<int>("Combat Scoreboard", "ScoreWeight", 7, Bilingual("Weight of kill score in total score.", "击杀得分在总分中的权重。")); HitWeight = config.Bind<int>("Combat Scoreboard", "HitWeight", 3, Bilingual("Weight of hit count in total score.", "命中次数在总分中的权重。")); ScoreboardBroadcastEnabled = config.Bind<bool>("Scoreboard Broadcast", "Enabled", true, Bilingual("Broadcast the top players in chat.", "在聊天栏播报排名。")); ScoreboardBroadcastInterval = config.Bind<float>("Scoreboard Broadcast", "Interval", 10f, new ConfigDescription(Bilingual("Seconds between ranking broadcasts.", "排名播报间隔秒数。"), (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 300f), Array.Empty<object>())); ScoreboardBroadcastTopCount = config.Bind<int>("Scoreboard Broadcast", "TopCount", 3, new ConfigDescription(Bilingual("Number of top players to broadcast.", "聊天栏播报前几名。"), (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>())); ScoreFlowerman = config.Bind<int>("Combat Scoring", "Flowerman", 3, Bilingual("Score for killing a Flowerman.", "击杀小黑的得分。")); ScoreNutcracker = config.Bind<int>("Combat Scoring", "Nutcracker", 2, Bilingual("Score for killing a Nutcracker.", "击杀胡桃夹子的得分。")); ScoreCrawler = config.Bind<int>("Combat Scoring", "Crawler", 2, Bilingual("Score for killing a Crawler.", "击杀半身鱼的得分。")); ScoreCentipede = config.Bind<int>("Combat Scoring", "Centipede", 1, Bilingual("Score for killing a Centipede.", "击杀抱脸虫的得分。")); ScoreHoarderBug = config.Bind<int>("Combat Scoring", "HoarderBug", 1, Bilingual("Score for killing a Hoarder Bug.", "击杀囤积虫的得分。")); ScoreMasked = config.Bind<int>("Combat Scoring", "Masked", 1, Bilingual("Score for killing a Masked enemy.", "击杀假人的得分。")); ScoreSandSpider = config.Bind<int>("Combat Scoring", "SandSpider", 2, Bilingual("Score for killing a Sand Spider.", "击杀蜘蛛的得分。")); ScoreButler = config.Bind<int>("Combat Scoring", "Butler", 2, Bilingual("Score for killing a Butler.", "击杀管家的得分。")); ScoreStingray = config.Bind<int>("Combat Scoring", "Stingray", 2, Bilingual("Score for killing a Stingray.", "击杀黏滑鱼的得分。")); RescueEnabled = config.Bind<bool>("Rescue", "Enabled", true, Bilingual("Allow the host to rescue an outside player with the ship teleporter.", "允许主机使用飞船传送器救援设施外玩家。")); RescueImmunityDuration = config.Bind<float>("Rescue", "ImmunityDuration", 45f, new ConfigDescription(Bilingual("Seconds of curfew immunity after a successful rescue.", "成功救援后免处决的秒数。"), (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 300f), Array.Empty<object>())); EnableCrawlerScale = config.Bind<bool>("Enemy Scale", "EnableCrawlerScale", false, Bilingual("Enable Crawler scaling.", "启用半身鱼体型放大。")); CrawlerScale = config.Bind<float>("Enemy Scale", "CrawlerScale", 1.5f, new ConfigDescription(Bilingual("Crawler local scale multiplier.", "半身鱼体型倍率。"), (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 2f), Array.Empty<object>())); EnableButlerScale = config.Bind<bool>("Enemy Scale", "EnableButlerScale", false, Bilingual("Enable Butler scaling.", "启用管家体型放大。")); ButlerScale = config.Bind<float>("Enemy Scale", "ButlerScale", 1.3f, new ConfigDescription(Bilingual("Butler local scale multiplier.", "管家体型倍率。"), (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 2f), Array.Empty<object>())); } } 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(); } } } [HarmonyPatch(typeof(StartOfRound), "gameOverAnimation")] private static class GameOverAnimationPatch { [HarmonyPostfix] private static void Postfix() { CombatScoreboardSystem.ScheduleMvpAnnouncement(); } } [HarmonyPatch(typeof(RoundManager), "DespawnPropsAtEndOfRound")] private static class RoundEndPatch { [HarmonyPrefix] private static void Prefix() { CombatScoreboardSystem.EndRound(); CurfewExecutionSystem.Stop(); } } private static BaseUnityPlugin _plugin; private static ConfigFile _config; private static Func<EnemyAI, bool> _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; CombatScoreboardSystem.Initialize(); } } public static void OnPluginStart() { if (_isInitialized) { PublicFeatureEnemyCatalog.CacheAllEnemyTypes(); RefillSystem.Restart(); } } public static void Shutdown() { if (_isInitialized) { RefillSystem.Stop(); CurfewExecutionSystem.Stop(); CombatScoreboardSystem.Stop(); EnemyScalePatches.ResetRoundState(); if (_config != null) { _config.SettingChanged -= HandleConfigChanged; } _plugin = null; _config = null; _privateBossPredicate = null; _isInitialized = false; } } public static void HandleRoundStart() { if (_isInitialized) { PublicFeatureEnemyCatalog.CacheAllEnemyTypes(); ItemAndLootPatches.ResetRoundState(); CurfewExecutionSystem.ResetRoundState(); CombatScoreboardSystem.ResetRoundState(); EnemyScalePatches.ResetRoundState(); RefillSystem.Restart(); CurfewExecutionSystem.Restart(); CombatScoreboardSystem.StartBroadcast(); } } public static void HandleConfigChanged(object sender, SettingChangedEventArgs e) { if (_isInitialized && e != null && e.ChangedSetting != null) { string text = e.ChangedSetting.Definition.Section ?? string.Empty; if (text.StartsWith("Enemy Refill", StringComparison.OrdinalIgnoreCase)) { RefillSystem.Restart(); } else if (text.StartsWith("Combat Scoreboard", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Combat Scoring", StringComparison.OrdinalIgnoreCase)) { CombatScoreboardSystem.ResetRoundState(); } else if (text.StartsWith("Curfew Execution", StringComparison.OrdinalIgnoreCase)) { CurfewExecutionSystem.Restart(); } else if (text.StartsWith("Scoreboard Broadcast", StringComparison.OrdinalIgnoreCase)) { CombatScoreboardSystem.RestartBroadcast(); } } } public static void SetPrivateBossPredicate(Func<EnemyAI, bool> 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<string, EnemyType> EnemyTypeCache = new Dictionary<string, EnemyType>(); private static readonly Dictionary<string, string> EnemyNameAliases = new Dictionary<string, string>(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<string, EnemyType> item in EnemyTypeCache) { if (item.Key.Equals(text, StringComparison.OrdinalIgnoreCase) || item.Key.Equals(name, StringComparison.OrdinalIgnoreCase)) { return item.Value; } } foreach (KeyValuePair<string, EnemyType> 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; } } private static readonly List<MonsterConfig> CachedRefillConfigs = new List<MonsterConfig>(); private static readonly List<(MonsterConfig Config, float Score)> RefillCandidatesBuffer = new List<(MonsterConfig, float)>(); private static readonly List<int> ValidSpawnNodesBuffer = new List<int>(); 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; } } private static IEnumerator RefillEnemiesRoutine() { while ((Object)(object)RoundManager.Instance == (Object)null) { yield return (object)new WaitForSeconds(1f); } while (!((Object)(object)StartOfRound.Instance == (Object)null) && !((Object)(object)RoundManager.Instance == (Object)null)) { yield return (object)new WaitUntil((Func<bool>)(() => (Object)(object)StartOfRound.Instance == (Object)null || (StartOfRound.Instance.shipHasLanded && !StartOfRound.Instance.inShipPhase && RoundManager.Instance.allEnemyVents != null && RoundManager.Instance.allEnemyVents.Length != 0))); if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null) { break; } if (!PublicFeatureConfig.RefillEnabled.Value || !((NetworkBehaviour)RoundManager.Instance).IsHost) { yield return (object)new WaitForSeconds(1f); continue; } if (PublicFeatureEnemyCatalog.CachedEnemyCount == 0) { PublicFeatureEnemyCatalog.CacheAllEnemyTypes(); } yield return SpawnInitialEnemiesCoroutine(); for (float elapsed = 0f; elapsed < (float)PublicFeatureConfig.RefillDelay.Value; elapsed += 1f) { if ((Object)(object)StartOfRound.Instance == (Object)null) { break; } if (StartOfRound.Instance.inShipPhase) { break; } yield return (object)new WaitForSeconds(1f); } if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.inShipPhase) { continue; } while ((Object)(object)StartOfRound.Instance != (Object)null && StartOfRound.Instance.shipHasLanded && !StartOfRound.Instance.inShipPhase) { if (PublicFeatureConfig.RefillEnabled.Value) { yield return CheckAndRefillEnemiesCoroutine(); } yield return (object)new WaitForSeconds(PublicFeatureConfig.RefillInterval.Value); if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.inShipPhase) { break; } } } } private static IEnumerator SpawnInitialEnemiesCoroutine() { List<MonsterConfig> monsterConfigs = GetMonsterConfigs(); foreach (MonsterConfig item in monsterConfigs) { if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.inShipPhase) { yield break; } if (item.Init > 0) { EnemyType enemyTypeByName = PublicFeatureEnemyCatalog.GetEnemyTypeByName(item.Name); if ((Object)(object)enemyTypeByName != (Object)null) { yield return SpawnEnemyCoroutine(enemyTypeByName, item.Init); } } } } private static IEnumerator CheckAndRefillEnemiesCoroutine() { if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null) { yield break; } List<MonsterConfig> monsterConfigs = GetMonsterConfigs(); Dictionary<string, EnemyType> configTypeMap = new Dictionary<string, EnemyType>(); foreach (MonsterConfig item2 in monsterConfigs) { configTypeMap[item2.Name] = PublicFeatureEnemyCatalog.GetEnemyTypeByName(item2.Name); } List<EnemyAI> spawnedEnemies = RoundManager.Instance.SpawnedEnemies; int num = 0; Dictionary<string, int> currentCounts = new Dictionary<string, int>(); foreach (MonsterConfig item3 in monsterConfigs) { currentCounts[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 monsterConfigs) { EnemyType val2 = configTypeMap[item4.Name]; if ((Object)(object)val2 != (Object)null && (Object)(object)val.enemyType == (Object)(object)val2) { currentCounts[item4.Name]++; num++; break; } } } int num2 = PublicFeatureConfig.RefillTotalCap.Value - num; if (num2 <= 0) { yield break; } int toSpawn = Mathf.Min(num2, PublicFeatureConfig.RefillBatchSize.Value); for (int j = 0; j < toSpawn; j++) { RefillCandidatesBuffer.Clear(); foreach (MonsterConfig item5 in monsterConfigs) { if (currentCounts[item5.Name] < item5.Max && item5.Weight > 0) { float item = (float)item5.Weight / (float)(currentCounts[item5.Name] + 1); RefillCandidatesBuffer.Add((item5, item)); } } if (RefillCandidatesBuffer.Count == 0) { break; } float num3 = 0f; foreach (var item6 in RefillCandidatesBuffer) { num3 += item6.Score; } float num4 = Random.Range(0f, num3); MonsterConfig selectedConfig = RefillCandidatesBuffer.Last().Config; float num5 = 0f; foreach (var item7 in RefillCandidatesBuffer) { num5 += item7.Score; if (num4 < num5) { (selectedConfig, _) = item7; break; } } EnemyType val3 = configTypeMap[selectedConfig.Name]; if ((Object)(object)val3 != (Object)null) { yield return SpawnEnemyCoroutine(val3, 1); currentCounts[selectedConfig.Name]++; } } } private static List<MonsterConfig> 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; } private static IEnumerator SpawnEnemyCoroutine(EnemyType type, int count) { if ((Object)(object)type == (Object)null || count <= 0 || (Object)(object)RoundManager.Instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null) { yield break; } GameObject[] aiNodes = RoundManager.Instance.insideAINodes; if (aiNodes == null || aiNodes.Length == 0) { yield break; } ValidSpawnNodesBuffer.Clear(); for (int i = 0; i < aiNodes.Length; i++) { ValidSpawnNodesBuffer.Add(i); } for (int j = 0; j < count; j++) { if (ValidSpawnNodesBuffer.Count == 0) { for (int k = 0; k < aiNodes.Length; k++) { ValidSpawnNodesBuffer.Add(k); } } int index = Random.Range(0, ValidSpawnNodesBuffer.Count); int num = ValidSpawnNodesBuffer[index]; GameObject val = aiNodes[num]; if ((Object)(object)val == (Object)null) { ValidSpawnNodesBuffer.RemoveAt(index); j--; continue; } Vector3 position = val.transform.position; Quaternion val2 = Quaternion.Euler(0f, val.transform.eulerAngles.y, 0f); try { GameObject obj = Object.Instantiate<GameObject>(type.enemyPrefab, position, val2); obj.GetComponentInChildren<NetworkObject>().Spawn(true); EnemyAI component = obj.GetComponent<EnemyAI>(); if ((Object)(object)component != (Object)null) { RoundManager.Instance.SpawnedEnemies.Add(component); component.enemyType.isOutsideEnemy = false; component.allAINodes = aiNodes; 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 (j < count - 1) { yield return (object)new WaitForSeconds(PublicFeatureConfig.SpawnDelay.Value); if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null || StartOfRound.Instance.inShipPhase) { break; } } } } } public static class CurfewExecutionSystem { private sealed class ExecutionTaskState { internal string Reason; internal int AttemptCount; internal float StartedAt; internal float NextRetryAt; internal bool PendingAnnounced; } private const string ExecutionReason = "怯战"; private static readonly Vector3 JudgeDogHiddenPosition = new Vector3(0f, -500f, 0f); private static readonly Dictionary<ulong, ExecutionTaskState> CondemnedPlayers = new Dictionary<ulong, ExecutionTaskState>(); private static readonly Dictionary<ulong, float> RescueImmunityExpiry = new Dictionary<ulong, float>(); private static readonly HashSet<ulong> RescuedPlayersThisRound = new HashSet<ulong>(); private static Coroutine _curfewCoroutine; private static Coroutine _executionCoroutine; private static Coroutine _rescueCoroutine; private static MouthDogAI _judgeDog; private static ulong? _activeExecutionTarget; private static int _judgeDogExecutionSerial; public static void Restart() { Stop(); if (PublicFeatureRuntime.IsEnabled && PublicFeatureConfig.CurfewEnabled.Value && !((Object)(object)PublicFeatureRuntime.PluginInstance == (Object)null)) { _curfewCoroutine = ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StartCoroutine(CurfewRoutine()); _executionCoroutine = ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StartCoroutine(ProcessExecutionLoop()); } } public static void Stop() { if ((Object)(object)PublicFeatureRuntime.PluginInstance != (Object)null) { if (_curfewCoroutine != null) { ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StopCoroutine(_curfewCoroutine); } if (_executionCoroutine != null) { ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StopCoroutine(_executionCoroutine); } if (_rescueCoroutine != null) { ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StopCoroutine(_rescueCoroutine); } } _curfewCoroutine = null; _executionCoroutine = null; _rescueCoroutine = null; } public static void ResetRoundState() { CondemnedPlayers.Clear(); RescueImmunityExpiry.Clear(); RescuedPlayersThisRound.Clear(); _activeExecutionTarget = null; _judgeDog = null; _judgeDogExecutionSerial = 0; } private static IEnumerator CurfewRoutine() { while ((Object)(object)TimeOfDay.Instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null) { yield return null; } float duration = Mathf.Max(0f, PublicFeatureConfig.CurfewDuration.Value); HUDManager instance = HUDManager.Instance; if (instance != null) { instance.AddTextToChatOnServer("<color=red>可以怕,但不能怂," + FormatCurfewDuration(duration) + "s后在设施外的玩家将被无情处决</color>", -1); } while (!StartOfRound.Instance.shipHasLanded) { if (!CanWaitForLanding()) { yield break; } yield return (object)new WaitForSeconds(0.5f); } float landingTime = Time.realtimeSinceStartup; while (Time.realtimeSinceStartup - landingTime < duration) { if (!IsRoundValidForCurfew()) { yield break; } yield return (object)new WaitForSeconds(1f); } HUDManager instance2 = HUDManager.Instance; if (instance2 != null) { instance2.AddTextToChatOnServer("<color=red>处决开始!所有设施外人员将被无情处决</color>", -1); } while (IsRoundValidForCurfew()) { CondemnOutsidePlayersOnce(); yield return (object)new WaitForSeconds(Mathf.Max(1f, PublicFeatureConfig.CurfewRepeatInterval.Value)); } } private static IEnumerator ProcessExecutionLoop() { while (PublicFeatureRuntime.IsEnabled && PublicFeatureConfig.CurfewEnabled.Value) { if (!IsRoundValidForCurfew()) { yield return (object)new WaitForSeconds(1f); continue; } float time = Time.time; RemoveStaleCondemnedPlayers(); if (_activeExecutionTarget.HasValue) { ProcessActiveExecutionTarget(time); } if (!_activeExecutionTarget.HasValue) { KeyValuePair<ulong, ExecutionTaskState> keyValuePair = CondemnedPlayers.OrderBy((KeyValuePair<ulong, ExecutionTaskState> kv) => kv.Value.StartedAt).FirstOrDefault(); if (!keyValuePair.Equals(default(KeyValuePair<ulong, ExecutionTaskState>))) { _activeExecutionTarget = keyValuePair.Key; } } yield return (object)new WaitForSeconds(1f); } } private static void CondemnOutsidePlayersOnce() { if (StartOfRound.Instance?.allPlayerScripts == null) { return; } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead && !val.isInsideFactory && !IsRescueImmune(val.playerClientId)) { ExecutePlayer(val, "怯战"); } } } private static void ExecutePlayer(PlayerControllerB player, string reason) { if (!((Object)(object)player == (Object)null) && !player.isPlayerDead) { if (CondemnedPlayers.TryGetValue(player.playerClientId, out var value)) { value.Reason = reason; return; } CondemnedPlayers[player.playerClientId] = new ExecutionTaskState { Reason = reason, AttemptCount = 0, StartedAt = Time.time, NextRetryAt = Time.time, PendingAnnounced = false }; } } private static void ProcessActiveExecutionTarget(float now) { ulong value = _activeExecutionTarget.Value; if (!CondemnedPlayers.TryGetValue(value, out var value2)) { _activeExecutionTarget = null; return; } PlayerControllerB val = FindPlayer(value); if ((Object)(object)val == (Object)null) { CondemnedPlayers.Remove(value); _activeExecutionTarget = null; } else if (val.isPlayerDead) { HUDManager instance = HUDManager.Instance; if (instance != null) { instance.AddTextToChatOnServer("<color=red>" + val.playerUsername + "已被处决</color>", -1); } CondemnedPlayers.Remove(value); _activeExecutionTarget = null; } else if (IsRescueImmune(value) || val.isInsideFactory) { CondemnedPlayers.Remove(value); _activeExecutionTarget = null; } else { if (now < value2.NextRetryAt) { return; } if (!value2.PendingAnnounced) { HUDManager instance2 = HUDManager.Instance; if (instance2 != null) { instance2.AddTextToChatOnServer("<color=red>" + val.playerUsername + "因怯战已被审判犬追猎</color>", -1); } value2.PendingAnnounced = true; } if (!TryExecutePlayerByJudgeDog(val)) { KillPlayerDirectlyForExecution(val); } value2.AttemptCount++; value2.NextRetryAt = now + Mathf.Max(1f, PublicFeatureConfig.CurfewRepeatInterval.Value); } } private static bool TryExecutePlayerByJudgeDog(PlayerControllerB activePlayer) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)activePlayer == (Object)null || activePlayer.isPlayerDead) { return false; } MouthDogAI orCreateJudgeDog = GetOrCreateJudgeDog(); if ((Object)(object)orCreateJudgeDog == (Object)null) { return false; } Vector3 val = ((Component)activePlayer).transform.position + ((Component)activePlayer).transform.forward * -1.5f; ((EnemyAI)orCreateJudgeDog).EnableEnemyMesh(true, false, false); ((Component)orCreateJudgeDog).transform.position = val; ((EnemyAI)orCreateJudgeDog).serverPosition = val; ((Component)orCreateJudgeDog).transform.LookAt(((Component)activePlayer).transform.position); ((EnemyAI)orCreateJudgeDog).SyncPositionToClients(); activePlayer.inAnimationWithEnemy = (EnemyAI)(object)orCreateJudgeDog; orCreateJudgeDog.KillPlayerServerRpc((int)activePlayer.playerClientId); int executionSerial = ++_judgeDogExecutionSerial; BaseUnityPlugin pluginInstance = PublicFeatureRuntime.PluginInstance; if (pluginInstance != null) { ((MonoBehaviour)pluginInstance).StartCoroutine(MoveJudgeDogBackAfterExecution(orCreateJudgeDog, activePlayer, executionSerial)); } return true; } private static MouthDogAI GetOrCreateJudgeDog() { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_judgeDog != (Object)null && !((EnemyAI)_judgeDog).isEnemyDead) { return _judgeDog; } MouthDogAI[] array = Object.FindObjectsOfType<MouthDogAI>(); foreach (MouthDogAI val in array) { if ((Object)(object)val != (Object)null && ((Object)((Component)val).gameObject).name.Contains("PublicJudgeDog") && !((EnemyAI)val).isEnemyDead) { _judgeDog = val; return _judgeDog; } } EnemyType val2 = FindMouthDogType(); if ((Object)(object)val2 == (Object)null || (Object)(object)val2.enemyPrefab == (Object)null) { return null; } GameObject obj = Object.Instantiate<GameObject>(val2.enemyPrefab, JudgeDogHiddenPosition, Quaternion.identity); ((Object)obj).name = "PublicJudgeDog"; NetworkObject componentInChildren = obj.GetComponentInChildren<NetworkObject>(); if ((Object)(object)componentInChildren != (Object)null && !componentInChildren.IsSpawned) { componentInChildren.Spawn(true); } _judgeDog = obj.GetComponent<MouthDogAI>(); if ((Object)(object)_judgeDog != (Object)null) { ((EnemyAI)_judgeDog).isOutside = true; if ((Object)(object)((EnemyAI)_judgeDog).agent != (Object)null) { ((Behaviour)((EnemyAI)_judgeDog).agent).enabled = false; } ((EnemyAI)_judgeDog).EnableEnemyMesh(false, false, false); ((EnemyAI)_judgeDog).SwitchToBehaviourState(0); } return _judgeDog; } private static EnemyType FindMouthDogType() { EnemyType val = PublicFeatureEnemyCatalog.GetEnemyTypeByName("MouthDog"); if ((Object)(object)val != (Object)null) { return val; } RoundManager instance = RoundManager.Instance; if ((Object)(object)instance?.currentLevel == (Object)null) { return null; } if (instance.currentLevel.OutsideEnemies != null) { val = instance.currentLevel.OutsideEnemies.Select((SpawnableEnemyWithRarity e) => e.enemyType).FirstOrDefault((Func<EnemyType, bool>)((EnemyType t) => (Object)(object)t != (Object)null && t.enemyName.IndexOf("Dog", StringComparison.OrdinalIgnoreCase) >= 0)); } if ((Object)(object)val == (Object)null && instance.currentLevel.DaytimeEnemies != null) { val = instance.currentLevel.DaytimeEnemies.Select((SpawnableEnemyWithRarity e) => e.enemyType).FirstOrDefault((Func<EnemyType, bool>)((EnemyType t) => (Object)(object)t != (Object)null && t.enemyName.IndexOf("Dog", StringComparison.OrdinalIgnoreCase) >= 0)); } return val; } private static IEnumerator MoveJudgeDogBackAfterExecution(MouthDogAI dog, PlayerControllerB target, int executionSerial) { float startTime = Time.timeSinceLevelLoad; yield return (object)new WaitUntil((Func<bool>)(() => (Object)(object)target == (Object)null || (Object)(object)target.deadBody != (Object)null || Time.timeSinceLevelLoad - startTime > 2f)); yield return (object)new WaitForSeconds(3f); if (executionSerial == _judgeDogExecutionSerial && (Object)(object)dog != (Object)null && !((EnemyAI)dog).isEnemyDead) { ((EnemyAI)dog).EnableEnemyMesh(false, false, false); ((EnemyAI)dog).SwitchToBehaviourState(0); ((Component)dog).transform.position = JudgeDogHiddenPosition; ((EnemyAI)dog).serverPosition = JudgeDogHiddenPosition; ((EnemyAI)dog).SyncPositionToClients(); } } private static void KillPlayerDirectlyForExecution(PlayerControllerB player) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || player.isPlayerDead) { return; } try { if (((NetworkBehaviour)player).IsOwner) { player.KillPlayer(Vector3.zero, true, (CauseOfDeath)0, 0, default(Vector3), false); } else { player.DamagePlayerFromOtherClientServerRpc(1000, Vector3.zero, (int)player.playerClientId); } } catch (Exception ex) { ManualLogSource logger = PublicFeatureRuntime.Logger; if (logger != null) { logger.LogWarning((object)("[CurfewExecution] Direct fallback failed for " + player.playerUsername + ": " + ex.Message)); } } } private static void RemoveStaleCondemnedPlayers() { List<ulong> list = new List<ulong>(); foreach (KeyValuePair<ulong, ExecutionTaskState> condemnedPlayer in CondemnedPlayers) { if ((Object)(object)FindPlayer(condemnedPlayer.Key) == (Object)null) { list.Add(condemnedPlayer.Key); } } foreach (ulong item in list) { CondemnedPlayers.Remove(item); if (_activeExecutionTarget == item) { _activeExecutionTarget = null; } } } public static bool TryStartRescue(PlayerControllerB targetPlayer) { if (!PublicFeatureConfig.RescueEnabled.Value || (Object)(object)targetPlayer == (Object)null || !IsRoundValidForRescue()) { return false; } if (targetPlayer.isPlayerDead || !targetPlayer.isPlayerControlled || targetPlayer.isInHangarShipRoom) { return false; } ulong playerClientId = targetPlayer.playerClientId; if (RescuedPlayersThisRound.Contains(playerClientId) || IsRescueImmune(playerClientId) || _rescueCoroutine != null) { return false; } if ((Object)(object)PublicFeatureRuntime.PluginInstance == (Object)null) { return false; } _rescueCoroutine = ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StartCoroutine(RescueTeleportCoroutine(targetPlayer)); return true; } private static IEnumerator RescueTeleportCoroutine(PlayerControllerB targetPlayer) { ShipTeleporter teleporter = ((IEnumerable<ShipTeleporter>)Object.FindObjectsOfType<ShipTeleporter>()).FirstOrDefault((Func<ShipTeleporter, bool>)((ShipTeleporter teleporterObject) => (Object)(object)teleporterObject != (Object)null && !teleporterObject.isInverseTeleporter)); if ((Object)(object)teleporter == (Object)null) { ShowRescueFailure("救援失败:飞船上没有普通传送器。"); _rescueCoroutine = null; yield break; } if ((Object)(object)teleporter.buttonTrigger == (Object)null || !teleporter.buttonTrigger.interactable) { ShowRescueFailure("救援失败:传送器正在冷却。"); _rescueCoroutine = null; yield break; } if ((Object)(object)StartOfRound.Instance?.mapScreen == (Object)null || StartOfRound.Instance.mapScreen.radarTargets == null) { ShowRescueFailure("救援失败:雷达尚未准备好。"); _rescueCoroutine = null; yield break; } int num = -1; for (int num2 = 0; num2 < StartOfRound.Instance.mapScreen.radarTargets.Count; num2++) { Transform val = StartOfRound.Instance.mapScreen.radarTargets[num2]?.transform; PlayerControllerB val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponentInChildren<PlayerControllerB>()); if ((Object)(object)val2 != (Object)null && val2.playerClientId == targetPlayer.playerClientId) { num = num2; break; } } if (num < 0) { ShowRescueFailure("救援失败:无法在雷达上定位该玩家。"); _rescueCoroutine = null; yield break; } if (!TrySetRadarTarget(num)) { ShowRescueFailure("救援失败:雷达目标切换失败。"); _rescueCoroutine = null; yield break; } yield return (object)new WaitForSeconds(0.3f); if (!IsRoundValidForRescue() || (Object)(object)targetPlayer == (Object)null || targetPlayer.isPlayerDead || targetPlayer.isInHangarShipRoom) { ShowRescueFailure("救援取消:目标状态已改变。"); _rescueCoroutine = null; yield break; } if (!TryActivateTeleporter(teleporter)) { ShowRescueFailure("救援失败:传送器调用失败。"); _rescueCoroutine = null; yield break; } yield return (object)new WaitForSeconds(1.5f); if (!IsRoundValidForRescue() || (Object)(object)targetPlayer == (Object)null || targetPlayer.isPlayerDead || !targetPlayer.isInHangarShipRoom) { ShowRescueFailure("救援失败:传送器未能将目标送回飞船。"); _rescueCoroutine = null; yield break; } ulong playerClientId = targetPlayer.playerClientId; RescuedPlayersThisRound.Add(playerClientId); RescueImmunityExpiry[playerClientId] = Time.realtimeSinceStartup + Mathf.Max(1f, PublicFeatureConfig.RescueImmunityDuration.Value); CondemnedPlayers.Remove(playerClientId); if (_activeExecutionTarget == playerClientId) { _activeExecutionTarget = null; } HUDManager instance = HUDManager.Instance; if (instance != null) { instance.AddTextToChatOnServer($"<color=cyan>[救援]</color> <color=yellow>{targetPlayer.playerUsername}</color> 已被救回飞船,{PublicFeatureConfig.RescueImmunityDuration.Value:0.#}秒内免处决。", -1); } _rescueCoroutine = null; } private static bool TrySetRadarTarget(int targetIndex) { try { StartOfRound.Instance.mapScreen.SwitchRadarTargetServerRpc(targetIndex); return true; } catch (Exception ex) { ManualLogSource logger = PublicFeatureRuntime.Logger; if (logger != null) { logger.LogWarning((object)("[Rescue] Radar target selection failed: " + ex.Message)); } return false; } } private static bool TryActivateTeleporter(ShipTeleporter teleporter) { try { teleporter.PressTeleportButtonServerRpc(); return true; } catch (Exception ex) { ManualLogSource logger = PublicFeatureRuntime.Logger; if (logger != null) { logger.LogWarning((object)("[Rescue] Teleporter activation failed: " + ex.Message)); } return false; } } private static bool IsRescueImmune(ulong playerId) { if (!RescueImmunityExpiry.TryGetValue(playerId, out var value)) { return false; } if (Time.realtimeSinceStartup < value) { return true; } RescueImmunityExpiry.Remove(playerId); return false; } private static void ShowRescueFailure(string message) { HUDManager instance = HUDManager.Instance; if (instance != null) { instance.AddTextToChatOnServer("<color=red>" + message + "</color>", -1); } } private static PlayerControllerB FindPlayer(ulong playerClientId) { return StartOfRound.Instance?.allPlayerScripts?.FirstOrDefault((Func<PlayerControllerB, bool>)((PlayerControllerB p) => (Object)(object)p != (Object)null && p.playerClientId == playerClientId)); } private static bool IsRoundValidForRescue() { if (PublicFeatureRuntime.IsEnabled && (Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)RoundManager.Instance != (Object)null && (((NetworkBehaviour)RoundManager.Instance).IsServer || ((NetworkBehaviour)RoundManager.Instance).IsHost) && StartOfRound.Instance.shipHasLanded) { return !StartOfRound.Instance.inShipPhase; } return false; } private static string FormatCurfewDuration(float duration) { return duration.ToString("0.##", CultureInfo.InvariantCulture); } private static bool IsRoundValidForCurfew() { if (!PublicFeatureRuntime.IsEnabled || !PublicFeatureConfig.CurfewEnabled.Value) { return false; } if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null) { return false; } if (!((NetworkBehaviour)RoundManager.Instance).IsServer && !((NetworkBehaviour)RoundManager.Instance).IsHost) { return false; } if (StartOfRound.Instance.inShipPhase || !StartOfRound.Instance.shipHasLanded) { return false; } return true; } private static bool CanWaitForLanding() { if (!PublicFeatureRuntime.IsEnabled || !PublicFeatureConfig.CurfewEnabled.Value) { return false; } if ((Object)(object)StartOfRound.Instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null) { return false; } if (!((NetworkBehaviour)RoundManager.Instance).IsServer && !((NetworkBehaviour)RoundManager.Instance).IsHost) { return false; } return true; } } [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<CentipedeAI>((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 { [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); } } } private static IEnumerator DelayedEarsRinging(float effectSeverity) { yield return (object)new WaitForSeconds(0.6f); SoundManager.Instance.earsRingingTimer = 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<HoarderBugAI>((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 { [HarmonyPrefix] private static bool Prefix(ButlerEnemyAI __instance, ref IEnumerator __result) { if (PublicFeatureConfig.ButlerDropKnifeAndBees.Value) { return true; } __result = ButlerBlowUpWithoutDrops(__instance); return false; } private static IEnumerator ButlerBlowUpWithoutDrops(ButlerEnemyAI instance) { ((EnemyAI)instance).creatureAnimator.SetTrigger("Popping"); ((EnemyAI)instance).creatureAnimator.SetLayerWeight(1, 0f); instance.popAudio.PlayOneShot(((EnemyAI)instance).enemyType.audioClips[3]); yield return (object)new WaitForSeconds(1.1f); ((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); } } } [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); } } } } private static readonly HashSet<ulong> UsedTZPIndices = new HashSet<ulong>(); private static readonly Dictionary<ulong, float> UsedTZPOnGroundSince = new Dictionary<ulong, float>(); private static readonly Dictionary<ulong, int> PendingTzpCriticalHealTargets = new Dictionary<ulong, int>(); private static readonly Dictionary<ulong, Coroutine> PendingTzpCriticalHealCoroutines = new Dictionary<ulong, Coroutine>(); 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<Coroutine>("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}"); } } private static IEnumerator CorpseCleanupCoroutine(EnemyAI enemy, float delay) { yield return (object)new WaitForSeconds(delay); if (!((Object)(object)enemy == (Object)null)) { if ((Object)(object)((NetworkBehaviour)enemy).NetworkObject != (Object)null && ((NetworkBehaviour)enemy).NetworkObject.IsSpawned && (((NetworkBehaviour)enemy).IsServer || ((NetworkBehaviour)enemy).IsHost)) { ((NetworkBehaviour)enemy).NetworkObject.Despawn(true); } else if ((Object)(object)((Component)enemy).gameObject != (Object)null) { Object.Destroy((Object)(object)((Component)enemy).gameObject); } } } 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<GameObject>(item.spawnPrefab, val, Random.rotation); GrabbableObject component = val2.GetComponent<GrabbableObject>(); if ((Object)(object)component != (Object)null) { component.startFallingPosition = val; component.FallToGround(true, true, default(Vector3)); NetworkObject component2 = val2.GetComponent<NetworkObject>(); 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)); } } } private static IEnumerator ApplyPendingTzpCriticalHeal(PlayerControllerB player, ulong playerId) { yield return (object)new WaitForSeconds(1.25f); PendingTzpCriticalHealCoroutines.Remove(playerId); if (!PendingTzpCriticalHealTargets.TryGetValue(playerId, out var value)) { yield break; } PendingTzpCriticalHealTargets.Remove(playerId); if ((Object)(object)player == (Object)null || player.isPlayerDead || player.health < 19 || player.health > 20) { yield break; } value = Mathf.Clamp(value, 20, 100); if (value <= player.health) { if (player.health >= 20) { player.DamagePlayerClientRpc(0, player.health); } } else { player.DamagePlayerClientRpc(-(value - player.health), value); } } } public static class CombatScoreboardSystem { private sealed class PlayerScoreState { internal ulong PlayerId; internal string PlayerName; internal int Hits; internal int KillScore; } public sealed class PlayerScoreSnapshot { public PlayerControllerB Player { get; private set; } public string PlayerName { get; private set; } public int Hits { get; private set; } public int KillScore { get; private set; } public float TotalScore { get; private set; } internal PlayerScoreSnapshot(PlayerControllerB player, string playerName, int hits, int killScore, float totalScore) { Player = player; PlayerName = playerName; Hits = hits; KillScore = killScore; TotalScore = totalScore; } } private const int WindowId = 41982; private static readonly Dictionary<ulong, PlayerScoreState> PlayerScores = new Dictionary<ulong, PlayerScoreState>(); private static readonly Dictionary<ulong, ulong> LastHitters = new Dictionary<ulong, ulong>(); private static readonly HashSet<ulong> ProcessedKills = new HashSet<ulong>(); private static Coroutine _broadcastCoroutine; private static Coroutine _mvpAnnouncementCoroutine; private static CombatScoreboardBehaviour _behaviour; private static bool _windowVisible; private static bool _gameOverMvpAnnounced; private static bool _gameOverMvpPending; private static Rect _windowRect = new Rect(12f, 12f, 580f, 160f); public static void Initialize() { if (!((Object)(object)PublicFeatureRuntime.PluginInstance == (Object)null) && (Object)(object)_behaviour == (Object)null) { _behaviour = ((Component)PublicFeatureRuntime.PluginInstance).gameObject.GetComponent<CombatScoreboardBehaviour>(); if ((Object)(object)_behaviour == (Object)null) { _behaviour = ((Component)PublicFeatureRuntime.PluginInstance).gameObject.AddComponent<CombatScoreboardBehaviour>(); } } } public static void ResetRoundState() { PlayerScores.Clear(); LastHitters.Clear(); ProcessedKills.Clear(); _windowVisible = false; _gameOverMvpAnnounced = false; _gameOverMvpPending = false; if (_mvpAnnouncementCoroutine != null && (Object)(object)PublicFeatureRuntime.PluginInstance != (Object)null) { ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StopCoroutine(_mvpAnnouncementCoroutine); } _mvpAnnouncementCoroutine = null; SeedControlledPlayers(); } private static void SeedControlledPlayers() { PlayerControllerB[] array = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.allPlayerScripts : null); if (array == null) { return; } PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if ((Object)(object)val != (Object)null && val.isPlayerControlled) { GetOrCreatePlayerState(val); } } } public static void StartBroadcast() { StopBroadcast(); if ((Object)(object)PublicFeatureRuntime.PluginInstance != (Object)null && PublicFeatureConfig.ScoreboardEnabled.Value && PublicFeatureConfig.ScoreboardBroadcastEnabled.Value) { _broadcastCoroutine = ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StartCoroutine(BroadcastRoutine()); } } public static void Stop() { StopBroadcast(); ResetRoundState(); } public static void RestartBroadcast() { StartBroadcast(); } public static void EndRound() { StopBroadcast(); _windowVisible = false; } public static void ScheduleMvpAnnouncement() { ManualLogSource logger = PublicFeatureRuntime.Logger; if (logger != null) { logger.LogInfo((object)"[MVP] Game-over hook triggered."); } if (_gameOverMvpAnnounced || _gameOverMvpPending) { ManualLogSource logger2 = PublicFeatureRuntime.Logger; if (logger2 != null) { logger2.LogInfo((object)"[MVP] Skipped because an announcement is already pending or sent."); } return; } if (!IsServer() || !PublicFeatureConfig.ScoreboardEnabled.Value || !PublicFeatureConfig.MvpEnabled.Value) { ManualLogSource logger3 = PublicFeatureRuntime.Logger; if (logger3 != null) { logger3.LogInfo((object)"[MVP] Skipped because this instance is not the server or MVP is disabled."); } return; } PlayerScoreSnapshot playerScoreSnapshot = CaptureMvpSnapshot(); if (playerScoreSnapshot == null) { ManualLogSource logger4 = PublicFeatureRuntime.Logger; if (logger4 != null) { logger4.LogWarning((object)$"[MVP] No player score snapshot was available. Stored score entries: {PlayerScores.Count}."); } return; } if ((Object)(object)PublicFeatureRuntime.PluginInstance == (Object)null) { ManualLogSource logger5 = PublicFeatureRuntime.Logger; if (logger5 != null) { logger5.LogWarning((object)"[MVP] Plugin instance is unavailable; cannot start delayed announcement."); } return; } _gameOverMvpPending = true; ManualLogSource logger6 = PublicFeatureRuntime.Logger; if (logger6 != null) { logger6.LogInfo((object)$"[MVP] Snapshot captured for {playerScoreSnapshot.PlayerName}: hits={playerScoreSnapshot.Hits}, score={playerScoreSnapshot.KillScore}, total={playerScoreSnapshot.TotalScore:0.0}."); } _mvpAnnouncementCoroutine = ((MonoBehaviour)PublicFeatureRuntime.PluginInstance).StartCoroutine(DelayedMvpAnnouncement(playerScoreSnapshot)); } private static PlayerScoreSnapshot CaptureMvpSnapshot() { if (PlayerScores.Count == 0) { return null; } PlayerScoreState playerScoreState = (from state in PlayerScores.Values orderby CalculateTotalScore(state.KillScore, state.Hits) descending, state.KillScore descending, state.Hits descending select state).ThenBy<PlayerScoreState, string>((PlayerScoreState state) => state.PlayerName ?? string.Empty, StringComparer.OrdinalIgnoreCase).FirstOrDefault(); if (playerScoreState == null) { return null; } return new PlayerScoreSnapshot(FindPlayer(playerScoreState.PlayerId), string.IsNullOrEmpty(playerScoreState.PlayerName) ? "Unknown" : playerScoreState.PlayerName, playerScoreState.Hits, playerScoreState.KillScore, CalculateTotalScore(playerScoreState.KillScore, playerScoreState.Hits)); } private static IEnumerator DelayedMvpAnnouncement(PlayerScoreSnapshot mvp) { float deadline = Time.realtimeSinceStartup + 12f; while (Time.realtimeSinceStartup < deadline && (!((Object)(object)StartOfRound.Instance != (Object)null) || !StartOfRound.Instance.inShipPhase || !((Object)(object)HUDManager.Instance != (Object)null))) { yield return (object)new WaitForSecondsRealtime(0.5f); } if ((Object)(object)HUDManager.Instance == (Object)null) { ManualLogSource logger = PublicFeatureRuntime.Logger; if (logger != null) { logger.LogWarning((object)"[MVP] Announcement timed out because HUDManager was unavailable."); } _gameOverMvpPending = false; _mvpAnnouncementCoroutine = null; yield break; } yield return (object)new WaitForSecondsRealtime(1f); HUDManager.Instance.AddTextToChatOnServer("<color=#00FFFF>[本局MVP]</color> <color=#FFD700>" + mvp.PlayerName + "</color> " + $"命中:<color=#FF8C00>{mvp.Hits}</color> " + $"得分:<color=#32CD32>{mvp.KillScore}</color> " + $"总分:<color=#DA70D6>{mvp.TotalScore:0.0}</color>", -1); _gameOverMvpAnnounced = true; _gameOverMvpPending = false; _mvpAnnouncementCoroutine = null; ManualLogSource logger2 = PublicFeatureRuntime.Logger; if (logger2 != null) { logger2.LogInfo((object)("[MVP] Announcement sent for " + mvp.PlayerName + ".")); } } public static void RecordHit(EnemyAI enemy, PlayerControllerB player) { if (IsServer() && PublicFeatureConfig.ScoreboardEnabled.Value && !((Object)(object)enemy == (Object)null) && !((Object)(object)player == (Object)null) && !enemy.isEnemyDead && player.isPlayerControlled && !player.isPlayerDead && !((Object)(object)((NetworkBehaviour)enemy).NetworkObject == (Object)null) && ((NetworkBehaviour)enemy).NetworkObject.IsSpawned && GetEnemyScore(enemy) > 0) { GetOrCreatePlayerState(player).Hits++; LastHitters[GetEnemyTrackingId(enemy)] = player.playerClientId; } } public static void RecordKill(EnemyAI enemy) { if (!IsServer() || !PublicFeatureConfig.ScoreboardEnabled.Value || (Object)(object)enemy == (Object)null || (Object)(object)((NetworkBehaviour)enemy).NetworkObject == (Object)null) { return; } ulong enemyTrackingId = GetEnemyTrackingId(enemy); if (!ProcessedKills.Add(enemyTrackingId)) { return; } try { int enemyScore = GetEnemyScore(enemy); if (enemyScore > 0 && LastHitters.TryGetValue(enemyTrackingId, out var value)) { PlayerControllerB val = FindPlayer(value); if (!((Object)(object)val == (Object)null) && val.isPlayerControlled) { GetOrCreatePlayerState(val).KillScore += enemyScore; } } } finally { LastHitters.Remove(enemyTrackingId); } } public static IReadOnlyList<PlayerScoreSnapshot> GetRankedPlayers() { List<PlayerScoreSnapshot> list = new List<PlayerScoreSnapshot>(); PlayerControllerB[] array = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.allPlayerScripts : null); if (array == null) { return list; } PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if (!((Object)(object)val == (Object)null) && val.isPlayerControlled) { PlayerScoreState orCreatePlayerState = GetOrCreatePlayerState(val); list.Add(new PlayerScoreSnapshot(val, string.IsNullOrEmpty(val.playerUsername) ? "Unknown" : val.playerUsername, orCreatePlayerState.Hits, orCreatePlayerState.KillScore, CalculateTotalScore(orCreatePlayerState.KillScore, orCreatePlayerState.Hits))); } } return (from snapshot in list orderby snapshot.TotalScore descending, snapshot.KillScore descending, snapshot.Hits descending select snapshot).ThenBy<PlayerScoreSnapshot, string>((PlayerScoreSnapshot snapshot) => snapshot.PlayerName, StringComparer.OrdinalIgnoreCase).ToList(); } internal static void Update() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (IsServer() && PublicFeatureConfig.ScoreboardEnabled.Value && Keyboard.current != null && ((ButtonControl)Keyboard.current[PublicFeatureConfig.ScoreboardKey.Value]).wasPressedThisFrame) { _windowVisible = !_windowVisible; } } internal static void OnGui() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (IsServer() && PublicFeatureConfig.ScoreboardEnabled.Value && _windowVisible) { _windowRect = GUI.Window(41982, _windowRect, new WindowFunction(DrawWindow), string.Empty); } } private static void DrawWindow(int windowId) { IReadOnlyList<PlayerScoreSnapshot> rankedPlayers = GetRankedPlayers(); float height = Mathf.Clamp(82f + (float)rankedPlayers.Count * 28f, 120f, (float)Screen.height - 20f); ((Rect)(ref _windowRect)).height = height; ((Rect)(ref _windowRect)).x = Mathf.Clamp(((Rect)(ref _windowRect)).x, 10f, Mathf.Max(10f, (float)Screen.width - ((Rect)(ref _windowRect)).width - 10f)); ((Rect)(ref _windowRect)).y = Mathf.Clamp(((Rect)(ref _windowRect)).y, 10f, Mathf.Max(10f, (float)Screen.height - ((Rect)(ref _windowRect)).height - 10f)); GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); GUILayout.Label("战斗记分板", Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("名次", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(45f) }); GUILayout.Label("玩家", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); GUILayout.Label("命中", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUILayout.Label("得分", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUILayout.Label("总分", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUILayout.Label("救援", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); GUILayout.EndHorizontal(); for (int i = 0; i < rankedPlayers.Count; i++) { PlayerScoreSnapshot playerScoreSnapshot = rankedPlayers[i]; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label((i + 1).ToString(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(45f) }); GUILayout.Label(playerScoreSnapshot.PlayerName, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); GUILayout.Label(playerScoreSnapshot.Hits.ToString(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUILayout.Label(playerScoreSnapshot.KillScore.ToString(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUILayout.Label(playerScoreSnapshot.TotalScore.ToString("0.0"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUI.enabled = PublicFeatureConfig.RescueEnabled.Value && (Object)(object)playerScoreSnapshot.Player != (Object)null && !playerScoreSnapshot.Player.isPlayerDead && !playerScoreSnapshot.Player.isInHangarShipRoom; if (GUILayout.Button("救援", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { CurfewExecutionSystem.TryStartRescue(playerScoreSnapshot.Player); } GUI.enabled = true; GUILayout.EndHorizontal(); } GUILayout.EndVertical(); GUI.DragWindow(); } private static IEnumerator BroadcastRoutine() { while (PublicFeatureRuntime.IsEnabled && PublicFeatureConfig.ScoreboardEnabled.Value && PublicFeatureConfig.ScoreboardBro