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 MonstersGordion v1.9.0
MonstersGordion.dll
Decompiled 2 days 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.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using MonstersGordion.Compat; using MonstersGordion.Patches; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("MonstersGordion")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.9.0.0")] [assembly: AssemblyInformationalVersion("1.9.0+6ebcd4f96dbc0f4617f06cdb5691906ae869c699")] [assembly: AssemblyProduct("MonstersGordion")] [assembly: AssemblyTitle("MonstersGordion")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.9.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace MonstersGordion { internal sealed class CompanyMonsterSpawner : MonoBehaviour { private readonly List<GameObject> _aiNodes = new List<GameObject>(); private readonly List<GameObject> _fakeTrees = new List<GameObject>(); private readonly List<EnemyAI> _ownedEnemies = new List<EnemyAI>(); private readonly List<GameObject> _ownedNests = new List<GameObject>(); private readonly Dictionary<int, float> _spawnTimes = new Dictionary<int, float>(); private string[] _originalSurfaceTags; private readonly List<(EnemyAI ai, string name, float time)> _recentSpawns = new List<(EnemyAI, string, float)>(); private readonly Dictionary<string, int> _earlyDeathCounts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); private readonly HashSet<string> _disabledThisLanding = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private const int EarlyDeathsBeforeDisable = 2; private readonly Dictionary<int, (Vector3 pos, float time)> _lastMovement = new Dictionary<int, (Vector3, float)>(); private const float StuckSeconds = 25f; private const float StuckDistance = 1.5f; private NavMeshSampler _sampler; private const float AnnounceDelay = 3f; private Coroutine _loop; private Coroutine _maintenance; private Coroutine _events; private readonly List<GameObject> _ownedHazards = new List<GameObject>(); private bool _shuttingDown; private bool _hasVainShrouds; private readonly HashSet<string> _warnedRequirements = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> HarmlessCreatures = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Manticoil", "Docile Locust Bees" }; private const float EarlyDeathSeconds = 4f; private int _earlyDeathTracesLogged; private static readonly HashSet<string> StationaryByDesign = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Flowerman", "Spring", "Clay Surgeon", "Jester", "Girl", "Cadaver Bloom", "Feiopar" }; private const int TreeCanopyLayer = 25; private static Material _trunkMaterial; private static Mesh _realTreeMesh; private static Material _realTreeMaterial; private static Quaternion _realTreeRotation = Quaternion.identity; private static Vector3 _realTreeScale = Vector3.one; private static bool _realTreeSearched; internal static CompanyMonsterSpawner Instance { get; private set; } internal static bool IsCompanyLevel() { return IsCompanyLevel(((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.currentLevel : null); } internal static bool IsCompanyLevel(SelectableLevel level) { if ((Object)(object)level == (Object)null) { return false; } if (!(level.sceneName == "CompanyBuilding") && level.levelID != 3) { if (level.PlanetName != null) { return level.PlanetName.Contains("Gordion"); } return false; } return true; } internal static void OnShipLanded() { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null)) { SelectableLevel currentLevel = instance.currentLevel; bool isServer = ((NetworkBehaviour)instance).IsServer; bool flag = IsCompanyLevel(); Plugin.Log.LogInfo((object)("Ship landed: planet='" + currentLevel?.PlanetName + "', scene='" + currentLevel?.sceneName + "', " + $"host={isServer}, companyMoon={flag}.")); if (!isServer) { Plugin.Log.LogInfo((object)"This game session is not the host — the spawner only runs on the host's machine. Make sure the lobby host has MonstersGordion installed."); } else if (!flag) { Plugin.DebugLog("Not the Company moon — spawner stays idle."); } else if (!((Object)(object)Instance != (Object)null)) { ToilHeadCompat.Scan(); StarlancerCompat.Scan(); BcmeCompat.Scan(); Instance = new GameObject("MonstersGordion_Spawner").AddComponent<CompanyMonsterSpawner>(); Instance.Begin(); } } } internal static void OnShipLeaving() { if ((Object)(object)Instance != (Object)null) { Instance.Shutdown(); } } private void Begin() { PluginConfig cfg = Plugin.Cfg; EnemyCatalog.Resolve(); if (cfg.RespawnOnLoad.Value) { DespawnAllEnemies(); } _sampler = new NavMeshSampler(cfg.RequireIndoorPoints.Value); if (_sampler.Build() == 0) { Plugin.Log.LogError((object)"No usable navmesh found inside the Company building. Make sure NavMeshInCompanyRedux is installed and working (or set RequireIndoorPoints=false to loosen the filter). Spawning is disabled for this landing."); return; } _hasVainShrouds = CheckVainShrouds(); if (!_hasVainShrouds && BushWolfIsSpawnable()) { int iterations = ((cfg.VainShroudIterations.Value > 0) ? cfg.VainShroudIterations.Value : 12); _hasVainShrouds = GrowVainShrouds(iterations); } PlaceQueuedHazards(); CreateAINodes(cfg.AINodeCount.Value); CreateDeadTreesIfNeeded(); EnableWormFloorEmergeIfNeeded(); _loop = ((MonoBehaviour)this).StartCoroutine(SpawnLoop()); _maintenance = ((MonoBehaviour)this).StartCoroutine(MaintenanceLoop()); _events = ((MonoBehaviour)this).StartCoroutine(EventLoop()); Plugin.Log.LogInfo((object)($"Company spawner active: cap={cfg.GlobalCap.Value}, " + $"interval=[{cfg.MinSpawnInterval.Value:F0}s..{cfg.MaxSpawnInterval.Value:F0}s], " + $"outsideAIMode={cfg.TreatEnemiesAsOutside.Value}, foreignEnemies={cfg.ForeignEnemies.Value}, " + "ToilHead=" + (ToilHeadCompat.Present ? $"Coil {cfg.CoilHeadTurretChance.Value}% / Manti {cfg.ManticoilTurretChance.Value}% / Masked {cfg.MaskedTurretChance.Value}%" : "absent") + ", StarlancerAIFix=" + (StarlancerCompat.Present ? "present" : "absent") + ", BCME=" + (BcmeCompat.Present ? "present" : "absent") + ".")); } private void Shutdown() { if (_shuttingDown) { return; } _shuttingDown = true; if (_loop != null) { ((MonoBehaviour)this).StopCoroutine(_loop); _loop = null; } if (_maintenance != null) { ((MonoBehaviour)this).StopCoroutine(_maintenance); _maintenance = null; } if (_events != null) { ((MonoBehaviour)this).StopCoroutine(_events); _events = null; } GordionEvents.Reset(); try { if (Plugin.Cfg.DespawnOnShipLeave.Value) { DespawnOwnedEnemies(); } DespawnOwnedNests(); RestoreWormFloorEmerge(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Cleanup despawn failed (scene tearing down?): " + ex.Message)); } foreach (GameObject aiNode in _aiNodes) { if ((Object)(object)aiNode != (Object)null) { Object.Destroy((Object)(object)aiNode); } } _aiNodes.Clear(); foreach (GameObject fakeTree in _fakeTrees) { if ((Object)(object)fakeTree != (Object)null) { Object.Destroy((Object)(object)fakeTree); } } _fakeTrees.Clear(); DespawnOwnedHazards(); _ownedEnemies.Clear(); Instance = null; Object.Destroy((Object)(object)((Component)this).gameObject); Plugin.DebugLog("Company spawner shut down."); } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this && !_shuttingDown) { _shuttingDown = true; ((MonoBehaviour)this).StopAllCoroutines(); Instance = null; } } private IEnumerator SpawnLoop() { PluginConfig cfg = Plugin.Cfg; while (true) { float num = Mathf.Max(1f, cfg.MinSpawnInterval.Value); float num2 = Mathf.Max(num, cfg.MaxSpawnInterval.Value); float num3 = Random.Range(num, num2); Plugin.DebugLog($"Next spawn attempt in {num3:F1}s."); yield return (object)new WaitForSeconds(num3); try { TrySpawnCycle(); } catch (Exception arg) { Plugin.Log.LogError((object)$"Spawn cycle failed: {arg}"); } } } private void TrySpawnCycle() { //IL_030d: Unknown result type (might be due to invalid IL or missing references) PluginConfig cfg = Plugin.Cfg; if ((Object)(object)RoundManager.Instance == (Object)null || (Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.shipIsLeaving) { return; } Dictionary<string, int> perType; int num = CountAliveEnemies(out perType); if (num >= cfg.GlobalCap.Value) { Plugin.DebugLog($"Global cap reached ({num}/{cfg.GlobalCap.Value}) — skipping."); return; } ScanForEarlyDeaths(); List<(EnemyType, EnemySpawnSettings, int)> list = new List<(EnemyType, EnemySpawnSettings, int)>(); foreach (EnemyType enemy in EnemyCatalog.Enemies) { EnemySpawnSettings enemySpawnSettings = Plugin.Cfg.For(enemy); if (enemySpawnSettings.Enabled.Value && enemySpawnSettings.SpawnWeight.Value > 0 && enemySpawnSettings.MaxSpawnCount.Value > 0 && (cfg.AllowHarmlessCreatures.Value || !IsHarmlessCreature(enemy)) && !_disabledThisLanding.Contains(enemy.enemyName)) { perType.TryGetValue(enemy.enemyName, out var value); if (value < enemySpawnSettings.MaxSpawnCount.Value && MeetsMapRequirements(enemy)) { list.Add((enemy, enemySpawnSettings, value)); } } } if (list.Count == 0) { Plugin.DebugLog("No eligible enemy types (all disabled or at their max)."); return; } List<(EnemyType, EnemySpawnSettings, int)> list2 = list.Where<(EnemyType, EnemySpawnSettings, int)>(((EnemyType type, EnemySpawnSettings s, int alive) c) => c.alive < c.s.MinSpawnCount.Value).ToList(); List<(EnemyType, EnemySpawnSettings, int)> pool; string arg; if (list2.Count > 0) { pool = list2; arg = "min-deficit pool"; } else { bool wantOutside = Random.Range(0, 100) < cfg.OutsideEnemyShare.Value; List<(EnemyType, EnemySpawnSettings, int)> list3 = list.Where<(EnemyType, EnemySpawnSettings, int)>(((EnemyType type, EnemySpawnSettings s, int alive) c) => c.type.isOutsideEnemy == wantOutside).ToList(); if (list3.Count == 0) { list3 = list; arg = (wantOutside ? "outdoor pool empty → all" : "indoor pool empty → all"); } else { arg = (wantOutside ? "outdoor pool" : "indoor pool"); } pool = list3; } (EnemyType, EnemySpawnSettings, int) tuple = WeightedPick(pool); Plugin.DebugLog($"Picked '{tuple.Item1.enemyName}' (weight {tuple.Item2.SpawnWeight.Value}, " + $"alive {tuple.Item3}/{tuple.Item2.MaxSpawnCount.Value}, {arg}, " + $"total alive {num}/{cfg.GlobalCap.Value})."); Vector3? randomPoint = _sampler.GetRandomPoint(cfg.MinDistanceFromPlayers.Value, 12, UpperShareFor(tuple.Item1)); if (!randomPoint.HasValue) { Plugin.Log.LogWarning((object)("Could not find a valid interior navmesh point for '" + tuple.Item1.enemyName + "' — skipping this cycle.")); } else { SpawnEnemy(tuple.Item1, randomPoint.Value); } } private bool EnsureNestFor(EnemyType type) { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)type.nestSpawnPrefab == (Object)null || !type.requireNestObjectsToSpawn) { return true; } RoundManager instance = RoundManager.Instance; if ((Object)(object)instance == (Object)null) { return true; } instance.enemyNestSpawnObjects.RemoveAll((EnemyAINestSpawnObject n) => (Object)(object)n == (Object)null); if (instance.enemyNestSpawnObjects.Any((EnemyAINestSpawnObject n) => (Object)(object)n != (Object)null && (Object)(object)n.enemyType == (Object)(object)type)) { return true; } Vector3? randomPoint = _sampler.GetRandomPoint(Plugin.Cfg.MinDistanceFromPlayers.Value, 12, Plugin.Cfg.UpperFloorSpawnShare.Value); if (!randomPoint.HasValue) { WarnRequirementOnce(type, "no interior navmesh point was free for its nest object"); return false; } GameObject val = null; try { val = Object.Instantiate<GameObject>(type.nestSpawnPrefab, randomPoint.Value, Quaternion.Euler(0f, Random.Range(-180f, 180f), 0f)); EnemyAINestSpawnObject component = val.GetComponent<EnemyAINestSpawnObject>(); NetworkObject componentInChildren = val.GetComponentInChildren<NetworkObject>(); if ((Object)(object)component == (Object)null || (Object)(object)componentInChildren == (Object)null) { WarnRequirementOnce(type, "its nest prefab '" + ((Object)type.nestSpawnPrefab).name + "' has no " + (((Object)(object)component == (Object)null) ? "EnemyAINestSpawnObject" : "NetworkObject") + " component"); Object.Destroy((Object)(object)val); return false; } componentInChildren.Spawn(true); instance.enemyNestSpawnObjects.Add(component); _ownedNests.Add(val); Plugin.DebugLog($"Placed a nest for '{type.enemyName}' at {randomPoint.Value:F1}."); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not place a nest for '" + type.enemyName + "': " + ex.Message)); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } return false; } } private void WarnRequirementOnce(EnemyType type, string reason) { if (_warnedRequirements.Add(type.enemyName)) { Plugin.Log.LogWarning((object)("Not spawning '" + type.enemyName + "' [" + EnemyCatalog.AIClassName(type) + "]: " + reason + ". Without it the game destroys the enemy inside EnemyAI.Start().")); } } private void DespawnOwnedNests() { foreach (GameObject item in _ownedNests.Where((GameObject n) => (Object)(object)n != (Object)null).ToList()) { try { EnemyAINestSpawnObject component = item.GetComponent<EnemyAINestSpawnObject>(); if ((Object)(object)component != (Object)null && (Object)(object)RoundManager.Instance != (Object)null) { RoundManager.Instance.enemyNestSpawnObjects.Remove(component); } NetworkObject componentInChildren = item.GetComponentInChildren<NetworkObject>(); if ((Object)(object)componentInChildren != (Object)null && componentInChildren.IsSpawned) { componentInChildren.Despawn(true); } else { Object.Destroy((Object)(object)item); } } catch (Exception ex) { Plugin.DebugLog("Nest cleanup failed: " + ex.Message); } } _ownedNests.Clear(); } private bool GrowVainShrouds(int iterations) { //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) try { MoldSpreadManager val = Object.FindObjectOfType<MoldSpreadManager>(); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"Cannot grow vain shrouds: no MoldSpreadManager on this moon."); return false; } try { typeof(MoldSpreadManager).GetField("finishedGeneratingMold", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(val, false); } catch (Exception ex) { Plugin.DebugLog("Could not reset finishedGeneratingMold: " + ex.Message); } FieldInfo field = typeof(MoldSpreadManager).GetField("finishedGeneratingMold", BindingFlags.Instance | BindingFlags.NonPublic); SelectableLevel val2 = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.currentLevel : null); int num = Mathf.Max(1, Plugin.Cfg.VainShroudPatches.Value); List<string> list = new List<string>(); for (int i = 0; i < num; i++) { try { field?.SetValue(val, false); } catch (Exception ex2) { Plugin.DebugLog("Could not reset finishedGeneratingMold: " + ex2.Message); } if ((Object)(object)val2 != (Object)null) { val2.canSpawnMold = true; val2.moldSpreadIterations = iterations; val2.moldStartPosition = -1; } Vector3? val3 = PickVainShroudOrigin(); if (!val3.HasValue) { Plugin.DebugLog("No free interior point left for another weed patch."); break; } val.GenerateMold(val3.Value, iterations); Vector3 value = val3.Value; list.Add(((Vector3)(ref value)).ToString("F1")); } bool weeds = val.GetWeeds(); string text = ((list.Count > 0) ? string.Join(", ", list) : "nowhere"); if (weeds) { Plugin.Log.LogInfo((object)($"Vain shrouds grown on Gordion: {list.Count} patch(es) x {iterations} iterations " + "at " + text + ". Bush Wolf can now hide and will survive.")); } else { Plugin.Log.LogWarning((object)($"Tried to grow vain shrouds ({list.Count} patch(es) x {iterations} iterations at " + text + ") but the game still reports none — Bush Wolf will be skipped. Try a higher [Integration] VainShroudIterations.")); } return weeds; } catch (Exception arg) { Plugin.Log.LogError((object)$"Growing vain shrouds failed: {arg}"); return false; } } private Vector3? PickVainShroudOrigin() { //IL_0074: Unknown result type (might be due to invalid IL or missing references) if (_sampler != null) { Vector3? result = _sampler.GetRandomPoint(Plugin.Cfg.MinDistanceFromPlayers.Value, 12, 50) ?? _sampler.GetRandomPoint(0f, 12, 50); if (result.HasValue) { return result; } } if (!((Object)(object)StartOfRound.Instance != (Object)null)) { return null; } return StartOfRound.Instance.shipLandingPosition.position; } private void EnableWormFloorEmergeIfNeeded() { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Cfg.EarthLeviathanFloorEmerge.Value) { return; } EnemyType val = ((IEnumerable<EnemyType>)EnemyCatalog.Enemies).FirstOrDefault((Func<EnemyType, bool>)((EnemyType e) => string.Equals(e.enemyName, "Earth Leviathan", StringComparison.OrdinalIgnoreCase))); if ((Object)(object)val == (Object)null || !Plugin.Cfg.For(val).Enabled.Value) { return; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || instance.naturalSurfaceTags == null) { return; } try { HashSet<string> hashSet = new HashSet<string>(instance.naturalSurfaceTags, StringComparer.Ordinal); RaycastHit val2 = default(RaycastHit); if (Physics.Raycast((Vector3)(((??)_sampler?.Anchor) ?? instance.shipLandingPosition.position) + Vector3.up * 3f, Vector3.down, ref val2, 12f, instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1)) { string tag = ((Component)((RaycastHit)(ref val2)).collider).tag; if (!string.IsNullOrEmpty(tag)) { hashSet.Add(tag); } Plugin.DebugLog("Worm floor probe hit '" + ((Object)((RaycastHit)(ref val2)).collider).name + "' tag='" + tag + "'."); } string[] array = new string[6] { "Untagged", "Catwalk", "Metal", "Concrete", "Wood", "Tiles" }; foreach (string item in array) { hashSet.Add(item); } _originalSurfaceTags = instance.naturalSurfaceTags; instance.naturalSurfaceTags = hashSet.ToArray(); Plugin.Log.LogInfo((object)($"Earth Leviathan: extended naturalSurfaceTags to {instance.naturalSurfaceTags.Length} entries " + "so the worm can breach up through the Company floor.")); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Worm floor-emerge setup failed: " + ex.Message)); _originalSurfaceTags = null; } } private void RestoreWormFloorEmerge() { if (_originalSurfaceTags == null) { return; } try { if ((Object)(object)StartOfRound.Instance != (Object)null) { StartOfRound.Instance.naturalSurfaceTags = _originalSurfaceTags; } } catch (Exception ex) { Plugin.DebugLog("Restoring naturalSurfaceTags failed: " + ex.Message); } _originalSurfaceTags = null; } private static bool BushWolfIsSpawnable() { EnemyType val = ((IEnumerable<EnemyType>)EnemyCatalog.Enemies).FirstOrDefault((Func<EnemyType, bool>)((EnemyType e) => string.Equals(e.enemyName, "Bush Wolf", StringComparison.OrdinalIgnoreCase))); if ((Object)(object)val != (Object)null) { return Plugin.Cfg.For(val).Enabled.Value; } return false; } private static bool CheckVainShrouds() { try { MoldSpreadManager val = Object.FindObjectOfType<MoldSpreadManager>(); if ((Object)(object)val == (Object)null) { Plugin.DebugLog("No MoldSpreadManager on this moon — no vain shrouds."); return false; } bool weeds = val.GetWeeds(); Plugin.Log.LogInfo((object)$"Vain shrouds present: {weeds}."); return weeds; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Vain shroud check failed: " + ex.Message)); return false; } } private bool MeetsMapRequirements(EnemyType type) { if (string.Equals(type.enemyName, "Bush Wolf", StringComparison.OrdinalIgnoreCase) && !_hasVainShrouds) { if (_warnedRequirements.Add(type.enemyName)) { Plugin.Log.LogWarning((object)("Not spawning '" + type.enemyName + "' [" + EnemyCatalog.AIClassName(type) + "]: it needs vain shrouds to hide in and none grew on this moon. Weeds are grown at level load, so set [Integration] VainShroudIterations > 0 (or keep it at 0 with Bush Wolf enabled) and fly to Gordion again — enabling it mid-round is too late.")); } return false; } if (string.Equals(type.enemyName, "Cadaver Bloom", StringComparison.OrdinalIgnoreCase) && !Plugin.Cfg.CadaverBloomTraps.Value) { if (_warnedRequirements.Add(type.enemyName)) { Plugin.Log.LogWarning((object)"Not spawning 'Cadaver Bloom': it needs its Growth (a dungeon) to activate. Enable [Integration] CadaverBloomTraps to plant standalone burst traps instead."); } return false; } return true; } private void ScanForEarlyDeaths() { float realtimeSinceStartup = Time.realtimeSinceStartup; for (int num = _recentSpawns.Count - 1; num >= 0; num--) { var (val, text, num2) = _recentSpawns[num]; if (!((Object)(object)val == (Object)null) && !val.isEnemyDead) { if (realtimeSinceStartup - num2 > 4f) { _recentSpawns.RemoveAt(num); } } else { _recentSpawns.RemoveAt(num); if (!(realtimeSinceStartup - num2 > 4f)) { _earlyDeathCounts.TryGetValue(text, out var value); value++; _earlyDeathCounts[text] = value; if (value >= 2 && _disabledThisLanding.Add(text)) { Plugin.Log.LogWarning((object)($"'{text}' died within {4f:F0}s of spawning {value} times — it " + "cannot survive on Gordion (it likely needs a dungeon, weeds or other map feature this moon lacks). Disabling it until the next landing.")); } } } } } private static bool IsHarmlessCreature(EnemyType type) { return HarmlessCreatures.Contains(type.enemyName); } private static int UpperShareFor(EnemyType type) { if (Plugin.Cfg.OldBirdUpperFloorOnly.Value && string.Equals(type.enemyName, "RadMech", StringComparison.OrdinalIgnoreCase)) { return 100; } return Plugin.Cfg.UpperFloorSpawnShare.Value; } private static (EnemyType type, EnemySpawnSettings s, int alive) WeightedPick(List<(EnemyType type, EnemySpawnSettings s, int alive)> pool) { int num = 0; foreach (var item in pool) { num += item.s.SpawnWeight.Value; } int num2 = Random.Range(0, num); foreach (var item2 in pool) { num2 -= item2.s.SpawnWeight.Value; if (num2 < 0) { return item2; } } return pool[pool.Count - 1]; } private int CountAliveEnemies(out Dictionary<string, int> perType) { perType = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); int num = 0; _ownedEnemies.RemoveAll((EnemyAI e) => (Object)(object)e == (Object)null || e.isEnemyDead); foreach (EnemyAI item in (IEnumerable<EnemyAI>)(Plugin.Cfg.CountForeignEnemies.Value ? RoundManager.Instance.SpawnedEnemies : _ownedEnemies)) { if (!((Object)(object)item == (Object)null) && !item.isEnemyDead && !((Object)(object)item.enemyType == (Object)null)) { num++; string enemyName = item.enemyType.enemyName; perType.TryGetValue(enemyName, out var value); perType[enemyName] = value + 1; } } return num; } internal static void NotifyEnemyKilled(EnemyAI enemy) { if (!((Object)(object)Instance == (Object)null) && !((Object)(object)enemy == (Object)null) && !((Object)(object)enemy.enemyType == (Object)null)) { Instance.OnEnemyKilled(enemy); } } private void OnEnemyKilled(EnemyAI enemy) { string enemyName = enemy.enemyType.enemyName; int instanceID = ((Object)enemy).GetInstanceID(); if (_spawnTimes.TryGetValue(instanceID, out var value)) { float num = Time.realtimeSinceStartup - value; _spawnTimes.Remove(instanceID); if (num <= 4f) { if (_earlyDeathTracesLogged < 5) { _earlyDeathTracesLogged++; Plugin.Log.LogWarning((object)($"'{enemyName}' [{EnemyCatalog.AIClassName(enemy.enemyType)}] died only {num:F2}s " + "after we spawned it — something is culling it. Caller stack trace:\n" + new StackTrace(fNeedFileInfo: false))); } else { Plugin.Log.LogWarning((object)$"'{enemyName}' died {num:F2}s after spawn (stack trace suppressed after 5)."); } return; } } Plugin.DebugLog("Enemy died: " + enemyName + "."); } internal void SpawnEnemy(EnemyType type, Vector3 point) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) PluginConfig cfg = Plugin.Cfg; try { if (!EnsureNestFor(type)) { return; } Vector3 val = point + Vector3.up * cfg.SpawnYOffset.Value; float num = Random.Range(0f, 360f); NetworkObjectReference val2 = RoundManager.Instance.SpawnEnemyGameObject(val, num, -1, type); NetworkObject val3 = default(NetworkObject); if (!((NetworkObjectReference)(ref val2)).TryGet(ref val3, (NetworkManager)null) || (Object)(object)val3 == (Object)null) { Plugin.Log.LogWarning((object)("Spawn of '" + type.enemyName + "' returned no NetworkObject.")); return; } EnemyAI val4 = ((Component)val3).GetComponent<EnemyAI>(); if ((Object)(object)val4 == (Object)null) { val4 = ((Component)val3).GetComponentInChildren<EnemyAI>(); } if ((Object)(object)val4 == (Object)null) { Plugin.Log.LogWarning((object)("Spawned '" + type.enemyName + "' has no EnemyAI component.")); return; } _ownedEnemies.Add(val4); _spawnTimes[((Object)val4).GetInstanceID()] = Time.realtimeSinceStartup; _recentSpawns.Add((val4, type.enemyName, Time.realtimeSinceStartup)); Plugin.DebugLog($"Spawned '{type.enemyName}' at {val}."); ((MonoBehaviour)this).StartCoroutine(PostSpawnSetup(val4)); } catch (Exception arg) { Plugin.Log.LogError((object)$"Failed to spawn '{type.enemyName}': {arg}"); } } private IEnumerator PostSpawnSetup(EnemyAI ai) { yield return null; yield return null; if ((Object)(object)ai == (Object)null || ai.isEnemyDead) { yield break; } ApplyInteriorAI(ai); string text = (((Object)(object)ai.enemyType != (Object)null) ? ai.enemyType.enemyName : string.Empty); ToilHeadCompat.Kind kind = ToilHeadCompat.KindOf(text); if (ToilHeadCompat.Present && kind != ToilHeadCompat.Kind.None) { int num = Plugin.Cfg.ToilHeadTurretChance(kind); int num2 = Random.Range(0, 100); if (num2 < num) { bool flag = Random.Range(0, 100) < Plugin.Cfg.ToilHeadSlayerChance(kind); bool flag2 = ToilHeadCompat.TryApply(ai, flag); Plugin.DebugLog($"ToilHead: '{text}' rolled {num2} < {num} (slayer={flag}) — " + (flag2 ? "turret attached" : "attach FAILED, see warnings above") + "."); } else { Plugin.DebugLog($"ToilHead: '{text}' rolled {num2} >= {num} — plain spawn."); } } } private void ApplyInteriorAI(EnemyAI ai) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) try { bool value = Plugin.Cfg.TreatEnemiesAsOutside.Value; try { ai.SetEnemyOutside(value); } catch (Exception ex) { Plugin.DebugLog("SetEnemyOutside threw: " + ex.Message); } ai.isOutside = value; AssignNodes(ai); NavMeshHit val = default(NavMeshHit); if ((Object)(object)ai.agent != (Object)null && ((Behaviour)ai.agent).isActiveAndEnabled && NavMesh.SamplePosition(((Component)ai).transform.position, ref val, 4f, -1)) { ai.agent.Warp(((NavMeshHit)(ref val)).position); } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("Interior AI fix-up failed for '" + ai.enemyType?.enemyName + "': " + ex2.Message)); } } private void AssignNodes(EnemyAI ai) { GameObject[] array = _aiNodes.Where((GameObject n) => (Object)(object)n != (Object)null).ToArray(); if (array.Length != 0) { ai.allAINodes = array; } } private void PlaceQueuedHazards() { //IL_0138: Unknown result type (might be due to invalid IL or missing references) List<(GameObject, float)> list = BcmeCompat.DrainQueuedOutdoorObjects(); if (list.Count == 0) { return; } PluginConfig cfg = Plugin.Cfg; float num = ((_sampler != null) ? _sampler.WalkableArea : 0f); if (num <= 0f) { Plugin.Log.LogWarning((object)$"{list.Count} queued outdoor object(s) dropped: no walkable area was measured."); return; } foreach (var (val, num2) in list) { if ((Object)(object)val == (Object)null) { continue; } int num3 = Mathf.Clamp(Mathf.RoundToInt(num2 * num * cfg.HazardDensityMultiplier.Value), 0, cfg.HazardMaxPerEvent.Value); Plugin.Log.LogInfo((object)($"Placing '{((Object)val).name}': density {num2:0.####} over {num:F0} m^2 " + $"x{cfg.HazardDensityMultiplier.Value:0.##} = {num3}.")); int num4 = 0; for (int i = 0; i < num3; i++) { Vector3? randomPoint = _sampler.GetRandomPoint(cfg.MinDistanceFromPlayers.Value, 12, cfg.UpperFloorSpawnShare.Value); if (randomPoint.HasValue && SpawnHazard(val, randomPoint.Value)) { num4++; } } if (num4 < num3) { Plugin.DebugLog($"'{((Object)val).name}': placed {num4} of {num3} (no room for the rest)."); } } } private bool SpawnHazard(GameObject prefab, Vector3 point) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) try { Quaternion val = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f); GameObject val2 = Object.Instantiate<GameObject>(prefab, point, val); NetworkObject component = val2.GetComponent<NetworkObject>(); if ((Object)(object)component != (Object)null) { component.Spawn(true); } _ownedHazards.Add(val2); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to place '" + ((Object)prefab).name + "': " + ex.Message)); return false; } } private void DespawnOwnedHazards() { foreach (GameObject ownedHazard in _ownedHazards) { if ((Object)(object)ownedHazard == (Object)null) { continue; } try { NetworkObject component = ownedHazard.GetComponent<NetworkObject>(); if ((Object)(object)component != (Object)null && component.IsSpawned) { component.Despawn(true); } else { Object.Destroy((Object)(object)ownedHazard); } } catch (Exception ex) { Plugin.DebugLog("Hazard cleanup failed: " + ex.Message); } } _ownedHazards.Clear(); } private IEnumerator EventLoop() { PluginConfig cfg = Plugin.Cfg; yield return (object)new WaitForSeconds(3f); if (!cfg.HordeEnabled.Value) { yield break; } float num = Mathf.Max(1f, (float)cfg.HordeDelaySeconds.Value - 3f); Plugin.DebugLog($"Horde due in {num:F0}s."); yield return (object)new WaitForSeconds(num); while (true) { yield return RunHorde(); int value = cfg.HordeRepeatSeconds.Value; if (value <= 0) { break; } yield return (object)new WaitForSeconds((float)value); } } private IEnumerator RunHorde() { string wanted = Plugin.Cfg.HordeEnemy.Value; EnemyType enemy = ((IEnumerable<EnemyType>)EnemyCatalog.Enemies).FirstOrDefault((Func<EnemyType, bool>)((EnemyType e) => string.Equals(e.enemyName, wanted, StringComparison.OrdinalIgnoreCase))); if ((Object)(object)enemy == (Object)null) { Plugin.Log.LogWarning((object)("Horde: '" + wanted + "' is not in the spawn pool — check the [Horde] Enemy name and that it is not barred by ExcludedEnemies. Skipping.")); yield break; } if (_sampler == null) { Plugin.Log.LogWarning((object)"Horde: no navmesh sample available — skipping."); yield break; } int perSide = Mathf.Max(1, Plugin.Cfg.HordeCountPerSide.Value); float value = Plugin.Cfg.MinDistanceFromPlayers.Value; if (!_sampler.TryGetOppositeEdgePoints(perSide, value, out var sideA, out var sideB) && !_sampler.TryGetOppositeEdgePoints(perSide, 0f, out sideA, out sideB)) { Plugin.Log.LogWarning((object)"Horde: could not find usable points at both edges of the map — skipping. On a heavily reshaped Company moon the walkable area can be too fragmented for this."); yield break; } GordionEvents.AnnounceHorde(enemy.enemyName); Plugin.Log.LogInfo((object)$"Horde: spawning {sideA.Count} + {sideB.Count} '{enemy.enemyName}' at opposite map edges."); int most = Mathf.Max(sideA.Count, sideB.Count); for (int i = 0; i < most; i++) { if (i < sideA.Count) { SpawnEnemy(enemy, sideA[i]); } yield return null; if (i < sideB.Count) { SpawnEnemy(enemy, sideB[i]); } yield return null; } } private IEnumerator MaintenanceLoop() { while (true) { yield return (object)new WaitForSeconds(Mathf.Max(1f, Plugin.Cfg.MaintenanceInterval.Value)); try { MaintenanceTick(); } catch (Exception arg) { Plugin.Log.LogError((object)$"Maintenance tick failed: {arg}"); } } } private void MaintenanceTick() { if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.shipIsLeaving) { return; } ScanForEarlyDeaths(); _ownedEnemies.RemoveAll((EnemyAI e) => (Object)(object)e == (Object)null || e.isEnemyDead); bool value = Plugin.Cfg.TreatEnemiesAsOutside.Value; GameObject[] array = _aiNodes.Where((GameObject n) => (Object)(object)n != (Object)null).ToArray(); foreach (EnemyAI ownedEnemy in _ownedEnemies) { if (!((Object)(object)ownedEnemy == (Object)null) && !ownedEnemy.isEnemyDead && !ownedEnemy.inSpecialAnimation) { if (ownedEnemy.isOutside != value) { ownedEnemy.isOutside = value; Plugin.DebugLog($"Re-applied isOutside={value} to '{ownedEnemy.enemyType?.enemyName}'."); } if (array.Length != 0 && (ownedEnemy.allAINodes == null || ownedEnemy.allAINodes.Length != array.Length || (ownedEnemy.allAINodes.Length != 0 && (Object)(object)ownedEnemy.allAINodes[0] != (Object)(object)array[0]))) { ownedEnemy.allAINodes = array; Plugin.DebugLog("Restored patrol nodes for '" + ownedEnemy.enemyType?.enemyName + "'."); } if (!RescueIfStranded(ownedEnemy)) { UnstickIfIdle(ownedEnemy); } } } _lastMovement.Keys.Where((int id) => _ownedEnemies.All((EnemyAI e) => (Object)(object)e == (Object)null || ((Object)e).GetInstanceID() != id)).ToList().ForEach(delegate(int id) { _lastMovement.Remove(id); }); DriveCadaverBlooms(); EnforceForeignEnemyPolicy(); } private void DriveCadaverBlooms() { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return; } float value = Plugin.Cfg.CadaverBloomTriggerRange.Value; float num = value * value; foreach (EnemyAI ownedEnemy in _ownedEnemies) { CadaverBloomAI val = (CadaverBloomAI)(object)((ownedEnemy is CadaverBloomAI) ? ownedEnemy : null); if (val == null || ((EnemyAI)val).isEnemyDead || val.hasBurst) { continue; } PlayerControllerB val2 = null; float num2 = num; Vector3 position = ((Component)val).transform.position; PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; foreach (PlayerControllerB val3 in allPlayerScripts) { if (!((Object)(object)val3 == (Object)null) && val3.isPlayerControlled && !val3.isPlayerDead) { Vector3 val4 = ((Component)val3).transform.position - position; float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude; if (sqrMagnitude < num2) { num2 = sqrMagnitude; val2 = val3; } } } if (!((Object)(object)val2 == (Object)null)) { try { val.BurstForth(val2, false, position, ((Component)val).transform.eulerAngles); Plugin.Log.LogInfo((object)$"Cadaver Bloom burst on a player within {Mathf.Sqrt(num2):F1} m at {position:F1}."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Cadaver Bloom BurstForth failed: " + ex.Message)); } } } } private void UnstickIfIdle(EnemyAI ai) { //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ai.agent == (Object)null || !((Behaviour)ai.agent).isActiveAndEnabled) { return; } string text = (((Object)(object)ai.enemyType != (Object)null) ? ai.enemyType.enemyName : string.Empty); if (StationaryByDesign.Contains(text)) { return; } int instanceID = ((Object)ai).GetInstanceID(); Vector3 position = ((Component)ai).transform.position; float realtimeSinceStartup = Time.realtimeSinceStartup; if (!_lastMovement.TryGetValue(instanceID, out (Vector3, float) value)) { _lastMovement[instanceID] = (position, realtimeSinceStartup); return; } Vector3 val = position - value.Item1; if (((Vector3)(ref val)).sqrMagnitude > 2.25f) { _lastMovement[instanceID] = (position, realtimeSinceStartup); } else { if (realtimeSinceStartup - value.Item2 < 25f) { return; } Vector3? randomPoint = _sampler.GetRandomPoint(Plugin.Cfg.MinDistanceFromPlayers.Value, 8, Plugin.Cfg.UpperFloorSpawnShare.Value); if (randomPoint.HasValue) { try { ai.SetDestinationToPosition(randomPoint.Value, false); } catch (Exception ex) { Plugin.DebugLog("Unstick SetDestination failed: " + ex.Message); } _lastMovement[instanceID] = (position, realtimeSinceStartup); Plugin.DebugLog($"Nudged idle '{text}' toward a new point (was stuck ~{25f:F0}s)."); } } } private bool RescueIfStranded(EnemyAI ai) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if (_sampler == null || (Object)(object)ai.agent == (Object)null || !((Behaviour)ai.agent).isActiveAndEnabled) { return false; } if (_sampler.IsReachable(((Component)ai).transform.position)) { return false; } Vector3? randomPoint = _sampler.GetRandomPoint(Plugin.Cfg.MinDistanceFromPlayers.Value, 8, Plugin.Cfg.UpperFloorSpawnShare.Value); if (!randomPoint.HasValue) { return false; } ai.agent.Warp(randomPoint.Value + Vector3.up * Plugin.Cfg.SpawnYOffset.Value); _lastMovement[((Object)ai).GetInstanceID()] = (randomPoint.Value, Time.realtimeSinceStartup); Plugin.Log.LogInfo((object)("Rescued stranded '" + ai.enemyType?.enemyName + "' — teleported back onto the interior navmesh.")); return true; } private void EnforceForeignEnemyPolicy() { ForeignEnemyPolicy value = Plugin.Cfg.ForeignEnemies.Value; if (value == ForeignEnemyPolicy.Ignore) { return; } RoundManager instance = RoundManager.Instance; if ((Object)(object)instance == (Object)null) { return; } foreach (EnemyAI item in instance.SpawnedEnemies.Where((EnemyAI e) => (Object)(object)e != (Object)null).ToList()) { if (!item.isEnemyDead && !((Object)(object)item.enemyType == (Object)null) && !_ownedEnemies.Contains(item) && !item.inSpecialAnimation && !((Object)(object)item.inSpecialAnimationWithPlayer != (Object)null)) { string enemyName = item.enemyType.enemyName; bool flag = EnemyCatalog.IsExcluded(enemyName); if (!flag && value == ForeignEnemyPolicy.RemoveNotEnabled) { flag = !Plugin.Cfg.For(item.enemyType).Enabled.Value; } if (flag && TryDespawn(instance, item)) { Plugin.Log.LogInfo((object)$"Removed '{enemyName}' spawned by another mod (ForeignEnemies={value})."); } } } } private void CreateAINodes(int count) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < count; i++) { Vector3? randomPoint = _sampler.GetRandomPoint(0f, 15, 50); if (randomPoint.HasValue) { _aiNodes.Add(CreateNode($"MG_AINode_{i}", randomPoint.Value, "AINode")); _aiNodes.Add(CreateNode($"MG_OutsideAINode_{i}", randomPoint.Value, "OutsideAINode")); } } Plugin.Log.LogInfo((object)$"Created {_aiNodes.Count / 2} interior AI patrol nodes (indoor + outdoor tagged)."); } private GameObject CreateNode(string name, Vector3 position, string tagName) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(((Component)this).transform, false); val.transform.position = position; try { val.tag = tagName; } catch (Exception ex) { Plugin.DebugLog("Could not tag AI node '" + tagName + "': " + ex.Message); } return val; } private void CreateDeadTreesIfNeeded() { //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Expected O, but got Unknown //IL_01b1: 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_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_00e6: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Cfg.FeioparDeadTrees.Value) { return; } EnemyType val = ((IEnumerable<EnemyType>)EnemyCatalog.Enemies).FirstOrDefault((Func<EnemyType, bool>)((EnemyType e) => string.Equals(e.enemyName, "Feiopar", StringComparison.OrdinalIgnoreCase))); if ((Object)(object)val == (Object)null) { return; } if (!Plugin.Cfg.For(val).Enabled.Value) { Plugin.DebugLog("Feiopar disabled — skipping dead-tree generation."); return; } int value = Plugin.Cfg.FeioparTreeCount.Value; int num = 0; List<Vector3> list = new List<Vector3>(); for (int num2 = 0; num2 < value; num2++) { Vector3? val2 = PickOpenTreePoint(list); if (val2.HasValue) { list.Add(val2.Value); GameObject val3 = new GameObject($"MG_DeadTree_{num2}") { layer = 25 }; val3.transform.SetParent(((Component)this).transform, false); val3.transform.position = val2.Value; try { val3.tag = "Tree"; } catch (Exception ex) { Plugin.DebugLog("Could not tag tree: " + ex.Message); } CapsuleCollider obj = val3.AddComponent<CapsuleCollider>(); obj.direction = 1; obj.radius = 0.5f; obj.height = 16f; obj.center = new Vector3(0f, 8f, 0f); ((Collider)obj).isTrigger = false; AttachTreeVisual(val3, 10f); _fakeTrees.Add(val3); GameObject val4 = new GameObject($"MG_DeadTreeCanopy_{num2}") { layer = 25 }; val4.transform.SetParent(((Component)this).transform, false); val4.transform.position = val2.Value + Vector3.up * 16f; SphereCollider obj2 = val4.AddComponent<SphereCollider>(); obj2.radius = 2f; ((Collider)obj2).isTrigger = false; _fakeTrees.Add(val4); num++; } } ResetPumaTreeCache(val); Plugin.Log.LogInfo((object)($"Feiopar: grew {num} dead trees (tagged 'Tree', layer 25, with collider + canopy) " + "for it to stalk from. Set [Integration] FeioparDeadTrees=false to disable.")); } private Vector3? PickOpenTreePoint(List<Vector3> alreadyPlaced) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) int num = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMaskAndDefault : (-5)); Vector3? val = null; for (int i = 0; i < 40; i++) { Vector3? p = _sampler.GetRandomPoint(0f, 10, 0); if (!p.HasValue) { continue; } bool flag = alreadyPlaced.Any(delegate(Vector3 q) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Vector3 val3 = q - p.Value; return ((Vector3)(ref val3)).sqrMagnitude < 25f; }); bool flag2 = Physics.CheckSphere(p.Value + Vector3.up * 1.5f, 1f, num, (QueryTriggerInteraction)1); bool num2 = SameLevelFloorCount(p.Value, 4f) == 8; bool flag3 = SameLevelFloorCount(p.Value, 7f) >= 6; if (num2 && !flag && !flag2) { Vector3? val2 = val; if (!val2.HasValue) { val = p; } } if (num2 && flag3 && !flag && !flag2) { return p; } } return val; } private static int SameLevelFloorCount(Vector3 center, float radius) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) int num = 0; NavMeshHit val = default(NavMeshHit); for (int i = 0; i < 8; i++) { float num2 = (float)i * MathF.PI * 2f / 8f; if (NavMesh.SamplePosition(center + new Vector3(Mathf.Cos(num2), 0f, Mathf.Sin(num2)) * radius, ref val, 1.2f, -1) && Mathf.Abs(((NavMeshHit)(ref val)).position.y - center.y) <= 1.5f) { num++; } } return num; } private static void AttachTreeVisual(GameObject tree, float targetHeight) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Expected O, but got Unknown //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Expected O, but got Unknown //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown try { if (!TryAttachRealTree(tree, targetHeight)) { GameObject val = new GameObject("ProcTree"); val.transform.SetParent(tree.transform, false); val.transform.localPosition = Vector3.zero; val.transform.localScale = Vector3.one * (targetHeight / 4.4f); Material trunkMaterial = GetTrunkMaterial(); AddBranch(val, trunkMaterial, new Vector3(0f, 2.2f, 0f), new Vector3(0.35f, 2.2f, 0.35f), Quaternion.identity); AddBranch(val, trunkMaterial, new Vector3(0.35f, 3.6f, 0.1f), new Vector3(0.12f, 1.1f, 0.12f), Quaternion.Euler(0f, 0f, 55f)); AddBranch(val, trunkMaterial, new Vector3(-0.3f, 3.9f, -0.15f), new Vector3(0.1f, 1f, 0.1f), Quaternion.Euler(0f, 120f, -50f)); AddBranch(val, trunkMaterial, new Vector3(0.05f, 4.3f, 0.3f), new Vector3(0.09f, 0.9f, 0.09f), Quaternion.Euler(35f, 0f, 20f)); } } catch (Exception ex) { Plugin.DebugLog("Could not build tree visual: " + ex.Message); } } private static void AddBranch(GameObject tree, Material mat, Vector3 localPos, Vector3 localScale, Quaternion localRot) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) GameObject obj = GameObject.CreatePrimitive((PrimitiveType)2); ((Object)obj).name = "Branch"; Collider component = obj.GetComponent<Collider>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } obj.transform.SetParent(tree.transform, false); obj.transform.localPosition = localPos; obj.transform.localRotation = localRot; obj.transform.localScale = localScale; Renderer component2 = obj.GetComponent<Renderer>(); if ((Object)(object)component2 != (Object)null && (Object)(object)mat != (Object)null) { component2.sharedMaterial = mat; } } private static bool TryAttachRealTree(GameObject tree, float targetHeight) { //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Expected O, but got Unknown //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) if (!_realTreeSearched) { _realTreeSearched = true; try { MeshFilter[] array = Resources.FindObjectsOfTypeAll<MeshFilter>(); foreach (MeshFilter val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.sharedMesh == (Object)null) { continue; } string text = (((Object)val.sharedMesh).name + " " + ((Object)((Component)val).gameObject).name).ToLowerInvariant(); if (text.Contains("tree") || text.Contains("leafless") || text.Contains("pine") || text.Contains("birch") || text.Contains("trunk")) { MeshRenderer component = ((Component)val).GetComponent<MeshRenderer>(); if (!((Object)(object)component == (Object)null) && !((Object)(object)((Renderer)component).sharedMaterial == (Object)null)) { _realTreeMesh = val.sharedMesh; _realTreeMaterial = ((Renderer)component).sharedMaterial; _realTreeRotation = ((Component)val).transform.rotation; _realTreeScale = ((Component)val).transform.lossyScale; Plugin.Log.LogInfo((object)("Feiopar: using real tree mesh '" + ((Object)val.sharedMesh).name + "' for visuals.")); break; } } } if ((Object)(object)_realTreeMesh == (Object)null) { Plugin.DebugLog("No real tree mesh loaded — using procedural dead trees."); } } catch (Exception ex) { Plugin.DebugLog("Real-tree search failed: " + ex.Message); } } if ((Object)(object)_realTreeMesh == (Object)null || (Object)(object)_realTreeMaterial == (Object)null) { return false; } GameObject val2 = new GameObject("TreeMesh"); val2.transform.SetParent(tree.transform, false); val2.transform.rotation = _realTreeRotation; val2.transform.localScale = _realTreeScale; val2.AddComponent<MeshFilter>().sharedMesh = _realTreeMesh; MeshRenderer obj = val2.AddComponent<MeshRenderer>(); ((Renderer)obj).sharedMaterial = _realTreeMaterial; Bounds bounds = ((Renderer)obj).bounds; float y = ((Bounds)(ref bounds)).size.y; if (y > 0.01f) { float num = Mathf.Clamp(targetHeight / y, 0.05f, 20f); Transform transform = val2.transform; transform.localScale *= num; } bounds = ((Renderer)obj).bounds; float y2 = ((Bounds)(ref bounds)).min.y; Transform transform2 = val2.transform; transform2.position += Vector3.up * (tree.transform.position.y - y2); return true; } private static Material GetTrunkMaterial() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_trunkMaterial != (Object)null) { return _trunkMaterial; } try { MeshRenderer[] array = Object.FindObjectsOfType<MeshRenderer>(); Color val2 = default(Color); for (int i = 0; i < array.Length; i++) { Material sharedMaterial = ((Renderer)array[i]).sharedMaterial; if (!((Object)(object)sharedMaterial == (Object)null) && !((Object)(object)sharedMaterial.shader == (Object)null) && (((Object)sharedMaterial.shader).name.IndexOf("HDRP", StringComparison.OrdinalIgnoreCase) >= 0 || ((Object)sharedMaterial.shader).name.IndexOf("Lit", StringComparison.OrdinalIgnoreCase) >= 0)) { Material val = new Material(sharedMaterial) { name = "MG_DeadTreeBark" }; ((Color)(ref val2))..ctor(0.16f, 0.11f, 0.08f); if (val.HasProperty("_BaseColor")) { val.SetColor("_BaseColor", val2); } if (val.HasProperty("_Color")) { val.SetColor("_Color", val2); } if (val.HasProperty("_BaseColorMap")) { val.SetTexture("_BaseColorMap", (Texture)null); } if (val.HasProperty("_MainTex")) { val.SetTexture("_MainTex", (Texture)null); } _trunkMaterial = val; Plugin.DebugLog("Dead-tree material cloned from shader '" + ((Object)sharedMaterial.shader).name + "'."); return _trunkMaterial; } } Plugin.DebugLog("No HDRP material found in scene for dead-tree trunks (will render default)."); } catch (Exception ex) { Plugin.DebugLog("GetTrunkMaterial failed: " + ex.Message); } return null; } private static void ResetPumaTreeCache(EnemyType feiopar) { try { Type type = ((object)(((Object)(object)feiopar.enemyPrefab != (Object)null) ? feiopar.enemyPrefab.GetComponentInChildren<EnemyAI>(true) : null))?.GetType(); FieldInfo fieldInfo = type?.GetField("AllTreeNodes", BindingFlags.Static | BindingFlags.Public); if (fieldInfo != null) { fieldInfo.SetValue(null, null); Plugin.DebugLog("Reset " + type.Name + ".AllTreeNodes so it rebuilds from our fake trees."); } else { Plugin.DebugLog("Could not find PumaAI.AllTreeNodes to reset (field renamed?)."); } } catch (Exception ex) { Plugin.DebugLog("ResetPumaTreeCache failed: " + ex.Message); } } private void DespawnAllEnemies() { RoundManager instance = RoundManager.Instance; if ((Object)(object)instance == (Object)null) { return; } List<EnemyAI> list = instance.SpawnedEnemies.Where((EnemyAI e) => (Object)(object)e != (Object)null).ToList(); int num = 0; foreach (EnemyAI item in list) { if (TryDespawn(instance, item)) { num++; } } instance.SpawnedEnemies.RemoveAll((EnemyAI e) => (Object)(object)e == (Object)null); if (num > 0) { Plugin.Log.LogInfo((object)$"RespawnOnLoad: despawned {num} pre-existing enemies."); } } private void DespawnOwnedEnemies() { RoundManager instance = RoundManager.Instance; if ((Object)(object)instance == (Object)null) { return; } int num = 0; foreach (EnemyAI item in _ownedEnemies.Where((EnemyAI e) => (Object)(object)e != (Object)null).ToList()) { if (TryDespawn(instance, item)) { num++; } } if (num > 0) { Plugin.DebugLog($"Ship leaving: despawned {num} of our enemies."); } } private static bool TryDespawn(RoundManager rm, EnemyAI enemy) { try { NetworkObject val = enemy.thisNetworkObject; if ((Object)(object)val == (Object)null) { val = ((Component)enemy).GetComponent<NetworkObject>(); } if ((Object)(object)val != (Object)null && val.IsSpawned) { rm.DespawnEnemyOnServer(val); return true; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Failed to despawn '" + enemy.enemyType?.enemyName + "': " + ex.Message)); } return false; } } internal static class EnemyCatalog { private static readonly HashSet<string> HardExcluded = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Lasso", "Red pill" }; private static readonly Dictionary<string, string> Notes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase) { ["Bush Wolf"] = "BushWolfEnemy needs vain shrouds to hide in. The mod now grows them on Gordion at save load (see [Integration] VainShroudIterations), so enable Bush Wolf and reload the save / fly fresh — enabling it mid-round is too late for weed generation", ["Cadaver Growths"] = "Intentionally unsupported: CadaverGrowthAI hard-requires a DunGen dungeon, which the Company building is not (even BrutalCompanyMinus's own Cadaver event can't bypass it). Use Cadaver Bloom traps (CadaverBloomTraps) for a working cadaver on Gordion", ["Cadaver Bloom"] = "Enabled via [Integration] CadaverBloomTraps: planted directly as standalone corpse traps that burst and chase when a player walks close, so no Growth/dungeon is needed", ["Feiopar"] = "PumaAI hunts from 'Tree'-tagged colliders on layer 25. With [Integration] FeioparDeadTrees the mod grows proper dead trees (tag + layer 25 collider + canopy) so it perches ~3 m up on a tree and pounces players who come near", ["Earth Leviathan"] = "SandWormAI only emerges through natural ground, which the Company interior lacks. With [Balance] EarthLeviathanFloorEmerge the mod lets it breach up through the building floor" }; private static readonly HashSet<string> ListedNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static bool _whitelistMode; internal static readonly List<EnemyType> Enemies = new List<EnemyType>(); internal static bool IsExcluded(string enemyName) { if (enemyName == null) { return false; } if (HardExcluded.Contains(enemyName)) { return true; } if (!_whitelistMode) { return ListedNames.Contains(enemyName); } return !ListedNames.Contains(enemyName); } internal static string AIClassName(EnemyType type) { try { EnemyAI val = (((Object)(object)type.enemyPrefab != (Object)null) ? type.enemyPrefab.GetComponentInChildren<EnemyAI>(true) : null); return ((Object)(object)val != (Object)null) ? ((object)val).GetType().Name : "<no EnemyAI component>"; } catch (Exception ex) { return "<unreadable: " + ex.GetType().Name + ">"; } } internal static void Resolve() { Enemies.Clear(); ListedNames.Clear(); _whitelistMode = Plugin.Cfg.ExcludedEnemiesIsWhitelist.Value; foreach (string item in from s in (Plugin.Cfg.ExcludedEnemies.Value ?? string.Empty).Split(',') select s.Trim() into s where s.Length > 0 select s) { ListedNames.Add(item); } Plugin.Log.LogInfo((object)(_whitelistMode ? $"Enemy list is a WHITELIST of {ListedNames.Count} name(s): only these may spawn." : $"Enemy list is a blacklist of {ListedNames.Count} name(s).")); if (_whitelistMode && ListedNames.Count == 0) { Plugin.Log.LogWarning((object)"ExcludedEnemiesIsWhitelist=true but ExcludedEnemies is empty — nothing will spawn."); } HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); List<string> list = new List<string>(); EnemyType[] array = Resources.FindObjectsOfTypeAll<EnemyType>(); foreach (EnemyType val in array) { if (!((Object)(object)val == (Object)null) && !string.IsNullOrWhiteSpace(val.enemyName) && hashSet.Add(val.enemyName)) { if ((Object)(object)val.enemyPrefab == (Object)null) { list.Add("'" + val.enemyName + "' [no prefab] — the EnemyType asset has no enemyPrefab, so it cannot be instantiated"); } else if (IsExcluded(val.enemyName)) { string text = (HardExcluded.Contains(val.enemyName) ? "built-in exclusion" : (_whitelistMode ? "not on the whitelist" : "on the blacklist")); list.Add("'" + val.enemyName + "' [" + AIClassName(val) + "] — excluded (" + text + ")"); } else { Enemies.Add(val); } } } Enemies.Sort((EnemyType a, EnemyType b) => string.CompareOrdinal(a.enemyName, b.enemyName)); foreach (EnemyType enemy in Enemies) { Plugin.Cfg.For(enemy); } Plugin.Cfg.FinishLegacyCapMigration(); Plugin.Log.LogInfo((object)($"Enemy catalog resolved: {Enemies.Count} spawnable types " + $"({list.Count} rejected).")); LogSpawnabilityReport(list); } private static void LogSpawnabilityReport(List<string> rejected) { foreach (string item in rejected) { Plugin.Log.LogInfo((object)(" rejected: " + item)); } List<string> list = new List<string>(); foreach (EnemyType enemy in Enemies) { EnemySpawnSettings enemySpawnSettings = Plugin.Cfg.For(enemy); string text = null; if (!enemySpawnSettings.Enabled.Value) { text = "Enabled = false"; } else if (enemySpawnSettings.SpawnWeight.Value <= 0) { text = "SpawnWeight = 0"; } else if (enemySpawnSettings.MaxSpawnCount.Value <= 0) { text = "MaxSpawnCount = 0"; } Notes.TryGetValue(enemy.enemyName, out var value); if (text != null) { if (Plugin.Cfg.DebugMode.Value) { Plugin.DebugLog(" off: '" + enemy.enemyName + "' [" + AIClassName(enemy) + "] — " + text + ((value != null) ? ("; note: " + value) : string.Empty)); } continue; } list.Add($"{enemy.enemyName} (w{enemySpawnSettings.SpawnWeight.Value}, max {enemySpawnSettings.MaxSpawnCount.Value}" + (enemy.isOutsideEnemy ? ", outdoor" : ", indoor") + ")"); if (value != null) { Plugin.Log.LogWarning((object)("'" + enemy.enemyName + "' [" + AIClassName(enemy) + "] is enabled, note: " + value + ".")); } } Plugin.Log.LogInfo((object)((list.Count > 0) ? ("Spawn pool: " + string.Join(", ", list)) : "Spawn pool is EMPTY — nothing is enabled with a non-zero weight and max count.")); } } internal static class GordionEvents { private const string HordeColor = "#cc0000"; private static readonly string[] HordeLines = new string[3] { "Something has been wearing their faces for a while now.", "They came in from both ends of the dock. All of them.", "You stayed too long." }; internal static void Reset() { } internal static void RollForLanding() { Reset(); if (!Plugin.Cfg.EnableStockEvents.Value) { return; } BcmeCompat.Scan(); List<(string, string, int, object)> list = new List<(string, string, int, object)>(); CollectStockCandidates(list); if (list.Count == 0) { BcmeCompat.PublishCurrentEvent(null); Plugin.Log.LogInfo((object)"No event is eligible on the Company moon this landing — see the reasons above."); return; } (string, string, int, object) tuple = PickWeighted(list); Plugin.Log.LogInfo((object)("Company moon event chosen: '" + tuple.Item2 + "' " + $"(from {list.Count} eligible candidate(s)).")); if (!BcmeCompat.Execute(tuple.Item4, tuple.Item1)) { BcmeCompat.PublishCurrentEvent(null); } else { BcmeCompat.PublishCurrentEvent(tuple.Item4); } } private static void CollectStockCandidates(List<(string name, string display, int weight, object mEvent)> candidates) { if (!BcmeCompat.Present) { Plugin.Log.LogInfo((object)"EnableStockEvents is on but BrutalCompanyMinus is not installed — skipping."); return; } if (!BcmeCompat.EventsUsable) { Plugin.Log.LogWarning((object)"EnableStockEvents is on but BCMER's event API is unavailable — skipping."); return; } foreach (string item in Plugin.Cfg.ResolveStockEventWhitelist()) { string reason; object obj = BcmeCompat.TryResolve(item, out reason); if (obj == null) { Plugin.Log.LogInfo((object)(" stock event '" + item + "' skipped: " + reason + ".")); } else { int num = Mathf.Max(1, BcmeCompat.WeightOf(item)); candidates.Add((item, item, num, obj)); Plugin.DebugLog($" stock event '{item}' eligible ({BcmeCompat.TypeOf(item)}, w{num})."); } } } private static (string name, string display, int weight, object mEvent) PickWeighted(List<(string name, string display, int weight, object mEvent)> candidates) { long num = 0L; foreach (var candidate in candidates) { num += candidate.weight; } if (num <= 0) { return candidates[Random.Range(0, candidates.Count)]; } long num2 = (long)(Random.value * (float)num); foreach (var candidate2 in candidates) { num2 -= candidate2.weight; if (num2 < 0) { return candidate2; } } return candidates[candidates.Count - 1]; } internal static void AnnounceHorde(string enemyName) { Say(Format(string.IsNullOrWhiteSpace(enemyName) ? "Horde" : (enemyName + " Horde"), "#cc0000", HordeLines[Random.Range(0, HordeLines.Length)]), Plugin.Cfg.HordeAnnounce.Value); } private static string Format(string title, string colorHex, string description) { if (string.IsNullOrWhiteSpace(colorHex) || colorHex[0] != '#') { colorHex = "#ffffff"; } string text = "<color=" + colorHex + ">" + title + "</color>"; if (!string.IsNullOrWhiteSpace(description)) { return text + ": " + description; } return text; } private static void Say(string message, bool allowed) { if (!allowed) { return; } try { if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.AddChatMessage(message, "", -1, false); } } catch (Exception ex) { Plugin.DebugLog("Event announcement failed: " + ex.Message); } } } internal sealed class LandingWatcher : MonoBehaviour { private const float PollInterval = 0.5f; private const float FallbackLandedAfter = 20f; private float _nextPoll; private bool _landedHandled; private float _fallbackTimer; internal static void Create() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0010: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("MonstersGordion_LandingWatcher"); Object.DontDestroyOnLoad((Object)val); ((Object)val).hideFlags = (HideFlags)61; val.AddComponent<LandingWatcher>(); } private void Update() { if (Time.unscaledTime < _nextPoll) { return; } _nextPoll = Time.unscaledTime + 0.5f; try { Poll(); } catch (Exception ex) { Plugin.DebugLog("LandingWatcher poll failed: " + ex.Message); } } private void Poll() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { ResetLandingState(); return; } bool flag = instance.shipHasLanded && !instance.shipIsLeaving; if (!flag && !instance.shipIsLeaving && !instance.inShipPhase && IsCompanySceneLoaded()) { _fallbackTimer += 0.5f; if (_fallbackTimer >= 20f && !_landedHandled) { Plugin.Log.LogWarning((object)"shipHasLanded was never set (another mod likely altered the landing sequence) — starting the spawner via fallback detection."); flag = true; } } else { _fallbackTimer = 0f; } if (flag && !_landedHandled) { _landedHandled = true; CompanyMonsterSpawner.OnShipLanded(); } else if (!flag && _landedHandled && (instance.shipIsLeaving || !instance.shipHasLanded)) { ResetLandingState(); CompanyMonsterSpawner.OnShipLeaving(); } } private void ResetLandingState() { _landedHandled = false; _fallbackTimer = 0f; } private static bool IsCompanySceneLoaded() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) if (!CompanyMonsterSpawner.IsCompanyLevel()) { return false; } Scene sceneByName = SceneManager.GetSceneByName("CompanyBuilding"); if (((Scene)(ref sceneByName)).IsValid()) { return ((Scene)(ref sceneByName)).isLoaded; } return false; } } internal sealed class NavMeshSampler { private sealed class Tier { public readonly List<(int a, int b, int c)> Triangles = new List<(int, int, int)>(); public readonly List<float> CumulativeAreas = new List<float>(); public float TotalArea; public void Add((int a, int b, int c) triangle, float area) { Triangles.Add(triangle); TotalArea += area; CumulativeAreas.Add(TotalArea); } } private const float EdgeSliceFraction = 0.2f; private const float EdgeSpacing = 3f; private readonly bool _requireIndoor; private readonly int _roofMask; private readonly NavMeshPath _pathCache = new NavMeshPath(); private Vector3[] _vertices; private readonly Tier _upper = new Tier(); private readonly Tier _lower = new Tier(); private Vector3? _anchor; public Vector3? Anchor => _anchor; public bool HasUpperTier => _upper.Triangles.Count > 0; public bool HasLowerTier => _lower.Triangles.Count > 0; public float WalkableArea => _upper.TotalArea + _lower.TotalArea; public NavMeshSampler(bool requireIndoor) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown _requireIndoor = requireIndoor; _roofMask = BuildRoofMask(); } private static int BuildRoofMask() { int num = 0; string[] array = new string[5] { "Room", "Colliders", "Default", "MapHazards", "Terrain" }; for (int i = 0; i < array.Length; i++) { int num2 = LayerMask.NameToLayer(array[i]); if (num2 >= 0) { num |= 1 << num2; } } if (num != 0) { return num; } return -5; } public int Build() { //IL_006c: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: 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_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: 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_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0302: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_0368: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Unknown result type (might be due to invalid IL or missing references) //IL_0397: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Unknown result type (might be due to invalid IL or missing references) //IL_054d: Unknown result type (might be due to invalid IL or missing references) //IL_0552: Unknown result type (might be due to invalid IL or missing references) _upper.Triangles.Clear(); _upper.CumulativeAreas.Clear(); _upper.TotalArea = 0f; _lower.Triangles.Clear(); _lower.CumulativeAreas.Clear(); _lower.TotalArea = 0f; _anchor = null; NavMeshTriangulation val = NavMesh.CalculateTriangulation(); _vertices = val.vertices; List<(int, int, int, float, Vector3)> list = new List<(int, int, int, float, Vector3)>(); float num = 0f; Vector3 val5; for (int i = 0; i + 2 < val.indices.Length; i += 3) { int num2 = val.indices[i]; int num3 = val.indices[i + 1]; int num4 = val.indices[i + 2]; Vector3 val2 = _vertices[num2]; Vector3 val3 = _vertices[num3]; Vector3 val4 = _vertices[num4]; val5 = Vector3.Cross(val3 - val2, val4 - val2); float num5 = ((Vector3)(ref val5)).magnitude * 0.5f; if (!(num5 < 0.05f)) { Vector3 val6 = (val2 + val3 + val4) / 3f; if (!IsInsideShip(val6) && (!_requireIndoor || HasCeilingAbove(val6))) { list.Add((num2, num3, num4, num5, val6)); num += num5; } } } if (list.Count == 0) { return 0; } int[] array = new int[list.Count]; for (int j = 0; j < array.Length; j++) { array[j] = j; } Dictionary<long, int> dictionary = new Dictionary<long, int>(); for (int k = 0; k < list.Count; k++) { int[] array2 = new int[3] { list[k].Item1, list[k].Item2, list[k].Item3 }; foreach (int num6 in array2) { long key = WeldKey(_vertices[num6]); if (dictionary.TryGetValue(key, out var value)) { Union(array, k, value); } else { dictionary[key] = k; } } } Dictionary<int, float> dictionary2 = new Dictionary<int, float>(); for (int m = 0; m < list.Count; m++) { int key2 = Find(array, m); dictionary2.TryGetValue(key2, out var value2); dictionary2[key2] = value2 + list[m].Item4; } int num7 = -1; float num8 = 0f; foreach (KeyValuePair<int, float> item in dictionary2) { if (item.Value > num8) { num8 = item.Value; num7 = item.Key; } } float num9 = 0f; Vector3 val7 = Vector3.zero; for (int n = 0; n < list.Count; n++) { if (Find(array, n) == num7 && list[n].Item4 > num9) { num9 = list[n].Item4; val7 = list[n].Item5; } } NavMeshHit val8 = default(NavMeshHit); if (NavMesh.SamplePosition(val7, ref val8, 5f, -1)) { _anchor = ((NavMeshHit)(ref val8)).position; } float num10 = DetermineSplitY(list); int num11 = 0; NavMeshHit val9 = default(NavMeshHit); foreach (var item2 in list) { if (!NavMesh.SamplePosition(item2.Item5, ref val9, 3f, -1) || !IsReachable(((NavMeshHit)(ref val9)).position)) { num11++; } else { ((item2.Item5.y >= num10) ? _upper : _lower).Add((a: item2.Item1, b: item2.Item2, c: item2.Item3), item2.Item4); } } int num12 = _upper.Triangles.Count + _lower.Triangles.Count; ManualLogSource log = Plugin.Log; string[] obj = new string[8] { $"NavMeshSampler: kept {num12}/{list.Count} triangles ", $"({_upper.TotalArea + _lower.TotalArea:F0} of {num:F0} m², ", $"{dictionary2.Count} regions, {num11} unreachable). ", $"Tiers @ splitY={num10:F1}: upper {_upper.Triangles.Count} tris / {_upper.TotalArea:F0} m², ", $"lower {_lower.Triangles.Count} tris / {_lower.TotalArea:F0} m². ", "Anchor=", null, null }; object obj2; if (!_anchor.HasValue) { obj2 = "NONE"; } else { val5 = _anchor.Value; obj2 = ((Vector3)(ref val5)).ToString("F1"); } obj[6] = (string)obj2; obj[7] = "."; log.LogInfo((object)string.Concat(obj)); return num12; } private static float DetermineSplitY(List<(int a, int b, int c, float area, Vector3 centroid)> candidates) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) try { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null && (Object)(object)instance.shipBounds != (Object)null) { Bounds bounds = instance.shipBounds.bounds; return ((Bounds)(ref bounds)).min.y - 3f; } } catch { } List<float> list = new List<float>(candidates.Count); foreach (var candidate in candidates) { list.Add(candidate.centroid.y); } list.Sort(); return list[list.Count / 2]; } public Vector3? GetRandomPoint(float minPlayerDistance, int attempts, int upperSharePercent) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (_upper.Triangles.Count == 0 && _lower.Triangles.Count == 0) { return null; } for (int i = 0; i < attempts; i++) { bool flag = Random.Range(0, 100) < upperSharePercent; Tier tier = (flag ? _upper : _lower); if (tier.Triangles.Count == 0) { tier = (flag ? _lower : _upper); } Vector3 candidate = RandomPointInTriangle(tier); if (TryValidate(candidate, minPlayerDistance, out var point)) { return point; } } return null; } public bool TryGetOppositeEdgePoints(int perSide, float minPlayerDistance, out List<Vector3> sideA, out List<Vector3> sideB) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0100: 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_0117: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) sideA = new List<Vector3>(); sideB = new List<Vector3>(); if (perSide <= 0) { return false; } List<((int, int, int), Vector3)> list = new List<((int, int, int), Vector3)>(); float num = float.MaxValue; float num2 = float.MinValue; float num3 = float.MaxValue; float num4 = float.MinValue; Tier[] array = new Tier[2] { _upper, _lower }; for (int i = 0; i < array.Length; i++) { foreach (var triangle in array[i].Triangles) { Vector3 val = (_vertices[triangle.a] + _vertices[triangle.b] + _vertices[triangle.c]) / 3f; if (val.x < num) { num = val.x; } if (val.x > num2) { num2 = val.x; } if (val.z < num3) { num3 = val.z; } if (val.z > num4) { num4 = val.z; } list.Add((triangle, val)); } } if (list.Count < 2) { return false; } bool useX = num2 - num >= num4 - num3; list.Sort((((int a, int b, int c) tri, Vector3 centroid) l, ((int a, int b, int c) tri, Vector3 centroid) r) => (useX ? l.centroid.x : l.centroid.z).CompareTo(useX ? r.centroid.x : r.centroid.z)); int num5 = Mathf.Clamp(Mathf.RoundToInt((float)list.Count * 0.2f), 1, list.Count / 2); FillEdge(list, 0, num5, perSide, minPlayerDistance, sideA); FillEdge(list, list.Count - num5, list.Count, perSide, minPlayerDistance, sideB); Plugin.DebugLog("Edge sampling on " + (useX ? "X" : "Z") + " axis, span " + $"{(useX ? (num2 - num) : (num4 - num3)):F0} m over {list.Count} triangles " + $"(slice {num5}): got {sideA.Count} + {sideB.Count} of {perSide} per side."); if (sideA.Count > 0) { return sideB.Count > 0; } return false; } private void FillEdge(List<((int a, int b, int c) tri, Vector3 centroid)> tris, int from, int to, int wanted, float minPlayerDistance, List<Vector3> into) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) int num = wanted * 12; for (int i = 0; i < num; i++) { if (into.Count >= wanted) { break; } (int, int, int) item = tris[Random.Range(from, to)].tri; if (!TryValidate(RandomPointInTriangle(item), minPlayerDistance, out var point)) { continue; } bool flag = false; foreach (Vector3 item2 in into) { Vector3 val = item2 - point; if (((Vector3)(ref val)).sqrMagnitude < 9f) { flag = true; break; } } if (!flag) { into.Add(point); } } } private bool TryValidate(Vector3 candidate, float minPlayerDistance, out Vector3 point) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) point = candidate; NavMeshHit val = default(NavMeshHit); if (!NavMesh.SamplePosition(candidate, ref val, 3f, -1)) { return false; } point = ((NavMeshHit)(ref val)).position; if (IsInsideShip(point)) { return false; } if (_requireIndoor && !HasCeilingAbove(point)) { return false; } if (minPlayerDistance > 0f && IsTooCloseToAnyPlayer(point, minPlayerDistance)) { return false; } if (!IsReachable(point)) { Plugin.DebugLog($"Rejected unreachable point {point:F1}."); return false; } return true; } public bool IsReachable(Vector3 point) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 if (!_anchor.HasValue) { return true; } _pathCache.ClearCorners(); if (NavMesh.CalculatePath(point, _anchor.Value, -1, _pathCache)) { return (int)_pathCache.status == 0; } return false; } private Vector3 RandomPointInTriangle((int a, int b, int c) triangle) { //IL_001d: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_006b: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Sqrt(Random.value); float value = Random.value; return _vertices[triangle.a] * (1f - num) + _vertices[triangle.b] * (num * (1f - value)) + _vertices[triangle.c] * (num * value); } private Vector3 RandomPointInTriangle(Tier tier) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0090: 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) //IL_009f: Unknown result type (might be due to invalid IL or missing references) float value = Random.value * tier.TotalArea; int index = LowerBound(tier.CumulativeAreas, value); (int a, int b, int c) tuple = tier.Triangles[index]; int item = tuple.a; int item2 = tuple.b; int item3 = tuple.c; float num = Mathf.Sqrt(Random.value); float value2 = Random.value; return _vertices[item] * (1f - num) + _vertices[item2] * (num * (1f - value2)) + _vertices[item3] * (num * value2); } private static long WeldKey(Vector3 v) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) long num = (long)Mathf.Round(v.x * 10f); long num2 = (long)Mathf.Round(v.y * 10f); long num3 = (long)Mathf.Round(v.z * 10f); return (num & 0x1FFFFF) | ((num2 & 0x1FFFFF) << 21) | ((num3 & 0x1FFFFF) << 42); } private static int Find(int[] parent, int i) { while (parent[i] != i) { parent[i] = parent[parent[i]]; i = parent[i]; } return i; } private static void Union(int[] parent, int a, int b) { int num = Find(parent, a); int num2 = Find(parent, b); if (num != num2) { parent[num] = num2; } } private static int LowerBound(List<float> sorted, float value) { int num = 0; int num2 = sorted.Count - 1; while (num < num2) { int num3 = (num + num2) / 2; if (sorted[num3] < value) { num = num3 + 1; } else { num2 = num3; } } return num; } private bool HasCeilingAbove(Vector3 point) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) return Physics.Raycast(point + Vector3.up * 0.3f, Vector3.up, 60f, _roofMask, (QueryTriggerInteraction)1); } private static bool IsInsideShip(Vector3 point) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.shipBounds == (Object)null) { return false; } Bounds bounds = instance.shipBounds.bounds; ((Bounds)(ref bounds)).Expand(4f); return ((Bounds)(ref bounds)).Contains(point); } private static bool IsTooCloseToAnyPlayer(Vector3 point, float minDistance) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null) { return false; } float num = minDistance * minDistance; PlayerControllerB[] allPlayerScripts = instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead) { Vector3 val2 = ((Component)val).transform.position - point; if (((Vector3)(ref val2)).sqrMagnitude < num) { return true; } } } return false; } } [BepInPlugin("Timofey.MonstersGordion", "MonstersGordion", "1.9.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private Harmony _harmony; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static PluginConfig Cfg { get; private set; } private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Cfg = new PluginConfig(((BaseUnityPlugin)this).Config); _harmony = new Harmony("Timofey.MonstersGordion"); _harmony.PatchAll(typeof(StartOfRoundPatches)); _harmony.PatchAll(typeof(EnemyAIPatches)); _harmony.PatchAll(typeof(M