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 RadsMoons v1.0.1
BepInEx/plugins/RadsMoons/RadsMoons.dll
Decompiled 14 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("RadsMoons")] [assembly: AssemblyDescription("Asset-free custom moon rules for Lethal Company v81")] [assembly: AssemblyFileVersion("1.0.1.0")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.1.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 RadsMoons { internal static class Atmosphere { private static GameObject s_obj; private static VolumeProfile s_profile; internal static void Apply() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown Clear(); string moon = Content.Moon; if (moon != "March" && moon != "Rend") { return; } s_obj = new GameObject("RadsMoons Fog"); Volume[] array = Object.FindObjectsOfType<Volume>(); Volume[] array2 = array; foreach (Volume val in array2) { if (val.isGlobal) { s_obj.layer = ((Component)val).gameObject.layer; break; } } Volume val2 = s_obj.AddComponent<Volume>(); val2.isGlobal = true; val2.priority = 10000f; val2.weight = 1f; s_profile = ScriptableObject.CreateInstance<VolumeProfile>(); val2.sharedProfile = s_profile; Fog val3 = s_profile.Add<Fog>(true); ((VolumeParameter<bool>)(object)val3.enabled).Override(moon == "March"); if (moon == "March") { ((VolumeParameter<bool>)(object)val3.enableVolumetricFog).Override(true); ((VolumeParameter<float>)(object)val3.meanFreePath).Override(20f); ((VolumeParameter<float>)(object)val3.baseHeight).Override(10000f); ((VolumeParameter<float>)(object)val3.maximumHeight).Override(10001f); } } internal static void Clear() { if ((Object)(object)s_obj != (Object)null) { Object.Destroy((Object)(object)s_obj); } if ((Object)(object)s_profile != (Object)null) { Object.Destroy((Object)(object)s_profile); } s_obj = null; s_profile = null; } } [HarmonyPatch(typeof(RoundManager), "SpawnEnemyFromVent")] internal static class CadaverBloomSpawnPatch { private static void Prefix(RoundManager __instance, out int __state) { __state = __instance.SpawnedEnemies.Count; } private static void Postfix(RoundManager __instance, int __state) { //IL_0053: 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) if (!((NetworkBehaviour)__instance).IsServer || !Levels.s_ready || Content.Moon != "Adamance") { return; } for (int i = __state; i < __instance.SpawnedEnemies.Count; i++) { EnemyAI obj = __instance.SpawnedEnemies[i]; CadaverBloomAI val = (CadaverBloomAI)(object)((obj is CadaverBloomAI) ? obj : null); if (val != null) { ((MonoBehaviour)Plugin.Instance).StartCoroutine(Activate(val, ((Component)val).transform.position, ((Component)val).transform.eulerAngles)); } } } private static IEnumerator Activate(CadaverBloomAI bloom, Vector3 position, Vector3 rotation) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_0016: Unknown result type (might be due to invalid IL or missing references) int generation = Landings.Generation; yield return (object)new WaitForSeconds(0.2f); if (generation != Landings.Generation || (Object)(object)bloom == (Object)null || !((NetworkBehaviour)bloom).IsSpawned || ((EnemyAI)bloom).isEnemyDead || bloom.hasBurst) { yield break; } PlayerControllerB[] players = StartOfRound.Instance.allPlayerScripts; for (int index = 0; index < players.Length; index++) { if (players[index].isPlayerControlled && !players[index].isPlayerDead) { EnemyEvents.Send(1, (EnemyAI)(object)bloom, index, position, rotation); break; } } } } [HarmonyPatch] internal static class AttackScope { [ThreadStatic] internal static EnemyAI t_attacker; private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(PufferAI), "OnCollideWithPlayer", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(HoarderBugAI), "OnCollideWithPlayer", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(BaboonBirdAI), "OnCollideWithPlayer", (Type[])null, (Type[])null); } private static void Prefix(EnemyAI __instance, out EnemyAI __state) { __state = t_attacker; t_attacker = __instance; } private static Exception Finalizer(EnemyAI __state, Exception __exception) { t_attacker = __state; return __exception; } } [HarmonyPatch(typeof(PlayerControllerB), "DamagePlayer")] internal static class DamagePatch { private static bool Prefix(PlayerControllerB __instance, int damageNumber, out int __state) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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) __state = __instance.health; if (damageNumber <= 0 || !((NetworkBehaviour)__instance).IsOwner || __instance.isPlayerDead || !(AttackScope.t_attacker is PufferAI)) { return true; } __instance.KillPlayer(Vector3.zero, true, (CauseOfDeath)6, 0, default(Vector3), false); return false; } private static void Postfix(PlayerControllerB __instance, int __state) { //IL_0060: 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) if (((NetworkBehaviour)__instance).IsOwner && !__instance.isPlayerDead && __instance.health < __state && (AttackScope.t_attacker is HoarderBugAI || AttackScope.t_attacker is BaboonBirdAI) && (Object)(object)__instance.currentlyHeldObjectServer != (Object)null) { __instance.DiscardHeldObject(false, (NetworkObject)null, default(Vector3), true); } } } internal static class Content { internal static readonly Dictionary<Type, EnemyType> s_enemies = new Dictionary<Type, EnemyType>(); internal static readonly Dictionary<Type, int> s_indoorWeights = new Dictionary<Type, int>(); internal static Item s_sheet; internal static Item s_egg; internal static Item s_flashbang; internal static GameObject s_mine; internal static IndoorMapHazardType s_spikeHazard; internal static string Moon => ((Object)(object)RoundManager.Instance == (Object)null || (Object)(object)RoundManager.Instance.currentLevel == (Object)null) ? "" : Rules.MoonName(RoundManager.Instance.currentLevel.PlanetName); internal static void Load() { s_enemies.Clear(); s_indoorWeights.Clear(); EnemyType[] array = Resources.FindObjectsOfTypeAll<EnemyType>(); foreach (EnemyType val in array) { if (!((Object)(object)val.enemyPrefab == (Object)null)) { EnemyAI component = val.enemyPrefab.GetComponent<EnemyAI>(); if ((Object)(object)component != (Object)null && !s_enemies.ContainsKey(((object)component).GetType())) { s_enemies.Add(((object)component).GetType(), val); } } } SelectableLevel[] levels = StartOfRound.Instance.levels; foreach (SelectableLevel val2 in levels) { foreach (SpawnableEnemyWithRarity enemy in val2.Enemies) { Type type = ((object)enemy.enemyType.enemyPrefab.GetComponent<EnemyAI>()).GetType(); if (!s_indoorWeights.ContainsKey(type) || s_indoorWeights[type] < enemy.rarity) { s_indoorWeights[type] = enemy.rarity; } } IndoorMapHazard[] indoorMapHazards = val2.indoorMapHazards; foreach (IndoorMapHazard val3 in indoorMapHazards) { if ((Object)(object)val3.hazardType.prefabToSpawn.GetComponentInChildren<Landmine>(true) != (Object)null) { s_mine = val3.hazardType.prefabToSpawn; } if ((Object)(object)val3.hazardType.prefabToSpawn.GetComponentInChildren<SpikeRoofTrap>(true) != (Object)null) { s_spikeHazard = val3.hazardType; } } SpawnableMapObject[] spawnableMapObjects = val2.spawnableMapObjects; foreach (SpawnableMapObject val4 in spawnableMapObjects) { if ((Object)(object)val4.prefabToSpawn.GetComponentInChildren<Landmine>(true) != (Object)null) { s_mine = val4.prefabToSpawn; } } } List<Item> itemsList = StartOfRound.Instance.allItemsList.itemsList; s_sheet = itemsList.Single((Item x) => x.itemName.Equals("Metal sheet", StringComparison.OrdinalIgnoreCase) || x.itemName.Equals("Tattered metal sheet", StringComparison.OrdinalIgnoreCase)); s_egg = itemsList.Single((Item x) => x.itemName.Equals("Easter egg", StringComparison.OrdinalIgnoreCase)); s_flashbang = itemsList.Single((Item x) => x.itemName.Equals("Homemade flashbang", StringComparison.OrdinalIgnoreCase)); } internal static EnemyType Enemy<T>() where T : EnemyAI { return s_enemies[typeof(T)]; } internal static SpawnableEnemyWithRarity Entry<T>(int weight) where T : EnemyAI { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown return new SpawnableEnemyWithRarity(Enemy<T>(), weight); } } internal static class EnemyEvents { [CompilerGenerated] private static class <>O { public static HandleNamedMessageDelegate <0>__Receive; } internal const byte Bloom = 1; internal const byte Ghost = 2; private const string Message = "juanradical.moonoverhaul.enemy.v3"; private static NetworkManager s_manager; internal static void Bind() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown Unbind(); s_manager = NetworkManager.Singleton; CustomMessagingManager customMessagingManager = s_manager.CustomMessagingManager; object obj = <>O.<0>__Receive; if (obj == null) { HandleNamedMessageDelegate val = Receive; <>O.<0>__Receive = val; obj = (object)val; } customMessagingManager.RegisterNamedMessageHandler("juanradical.moonoverhaul.enemy.v3", (HandleNamedMessageDelegate)obj); } internal static void Unbind() { if ((Object)(object)s_manager != (Object)null && s_manager.CustomMessagingManager != null) { s_manager.CustomMessagingManager.UnregisterNamedMessageHandler("juanradical.moonoverhaul.enemy.v3"); } s_manager = null; } internal unsafe static void Send(byte action, EnemyAI enemy, int player, Vector3 position, Vector3 rotation) { //IL_0046: 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_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)s_manager == (Object)null || !s_manager.IsServer) { throw new InvalidOperationException("Enemy events must originate on the host."); } ulong networkObjectId = ((NetworkBehaviour)enemy).NetworkObjectId; FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(64, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteValueSafe<byte>(ref action, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<ulong>(ref networkObjectId, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe<int>(ref player, default(ForPrimitives)); ((FastBufferWriter)(ref val)).WriteValueSafe(ref position); ((FastBufferWriter)(ref val)).WriteValueSafe(ref rotation); foreach (ulong connectedClientsId in s_manager.ConnectedClientsIds) { if (connectedClientsId != 0) { s_manager.CustomMessagingManager.SendNamedMessage("juanradical.moonoverhaul.enemy.v3", connectedClientsId, val, (NetworkDelivery)3); } } } finally { ((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose(); } ((MonoBehaviour)Plugin.Instance).StartCoroutine(Apply(action, networkObjectId, player, position, rotation)); } private static void Receive(ulong sender, FastBufferReader reader) { //IL_001f: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if (sender == 0L && !s_manager.IsServer) { byte action = default(byte); ((FastBufferReader)(ref reader)).ReadValueSafe<byte>(ref action, default(ForPrimitives)); ulong id = default(ulong); ((FastBufferReader)(ref reader)).ReadValueSafe<ulong>(ref id, default(ForPrimitives)); int playerIndex = default(int); ((FastBufferReader)(ref reader)).ReadValueSafe<int>(ref playerIndex, default(ForPrimitives)); Vector3 position = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref position); Vector3 rotation = default(Vector3); ((FastBufferReader)(ref reader)).ReadValueSafe(ref rotation); ((MonoBehaviour)Plugin.Instance).StartCoroutine(Apply(action, id, playerIndex, position, rotation)); } } private static IEnumerator Apply(byte action, ulong id, int playerIndex, Vector3 position, Vector3 rotation) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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) int generation = Landings.Generation; float deadline = Time.realtimeSinceStartup + 8f; NetworkObject obj = null; while ((Object)(object)s_manager != (Object)null && generation == Landings.Generation && !s_manager.SpawnManager.SpawnedObjects.TryGetValue(id, out obj)) { if (Time.realtimeSinceStartup >= deadline) { throw new InvalidOperationException($"Enemy event {action} could not resolve object {id}."); } yield return null; } if ((Object)(object)s_manager == (Object)null || generation != Landings.Generation) { yield break; } PlayerControllerB player = StartOfRound.Instance.allPlayerScripts[playerIndex]; switch (action) { case 1: { CadaverBloomAI bloom = ((Component)obj).GetComponent<CadaverBloomAI>(); if ((Object)(object)bloom == (Object)null) { throw new InvalidOperationException("Bloom event resolved a different enemy type."); } if (!((EnemyAI)bloom).isEnemyDead && !bloom.hasBurst) { bloom.BurstForth(player, false, position, rotation); } break; } default: throw new InvalidOperationException("Unknown enemy event."); case 2: { DressGirlAI ghost = ((Component)obj).GetComponent<DressGirlAI>(); if ((Object)(object)ghost == (Object)null) { throw new InvalidOperationException("Ghost event resolved a different enemy type."); } ShowerGhosts.ChangeTarget(ghost, player); if ((Object)(object)GameNetworkManager.Instance.localPlayerController != (Object)(object)player) { break; } while ((Object)(object)ghost != (Object)null && !((NetworkBehaviour)ghost).IsOwner) { if (generation != Landings.Generation || (Object)(object)ghost.hauntingPlayer != (Object)(object)player || player.isPlayerDead || !player.isPlayerControlled) { yield break; } if (Time.realtimeSinceStartup >= deadline) { throw new InvalidOperationException("Ghost ownership transfer timed out."); } yield return null; } if (generation == Landings.Generation && (Object)(object)ghost != (Object)null && (Object)(object)ghost.hauntingPlayer == (Object)(object)player && !((EnemyAI)ghost).isEnemyDead && !player.isPlayerDead && player.isPlayerControlled) { ShowerGhosts.Attack(ghost, position); } break; } } } } [HarmonyPatch(typeof(StartOfRound), "Start")] internal static class BindEnemyEventsPatch { private static void Postfix() { EnemyEvents.Bind(); } } [HarmonyPatch(typeof(EnemyAI), "Start")] internal static class EnemyLocationPatch { private static bool Applies(EnemyAI enemy) { return Content.Moon switch { "Experimentation" => enemy is FlowerSnakeEnemy, "Assurance" => enemy is ButlerBeesEnemyAI, "Vow" => enemy is SpringManAI, "Offense" => enemy is CrawlerAI, "Adamance" => enemy is HoarderBugAI, "Dine" => enemy is ClaySurgeonAI, "Embrion" => (enemy is PufferAI || enemy is JesterAI) ? true : false, _ => false, }; } private static void Postfix(EnemyAI __instance) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (Levels.s_ready && Applies(__instance)) { __instance.SetEnemyOutside(((Component)__instance).transform.position.y > -80f); } } } [HarmonyPatch(typeof(JesterAI), "Update")] internal static class JesterLocationPatch { private static bool SameArea(PlayerControllerB player, JesterAI jester) { return player.isInsideFactory != ((EnemyAI)jester).isOutside; } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { FieldInfo field = AccessTools.Field(typeof(PlayerControllerB), "isInsideFactory"); foreach (CodeInstruction instruction in instructions) { if (!CodeInstructionExtensions.LoadsField(instruction, field, false)) { yield return instruction; continue; } CodeInstruction load = new CodeInstruction(OpCodes.Ldarg_0, (object)null); load.labels.AddRange(instruction.labels); instruction.labels.Clear(); load.blocks.AddRange(instruction.blocks); instruction.blocks.Clear(); yield return load; instruction.opcode = OpCodes.Call; instruction.operand = AccessTools.Method(typeof(JesterLocationPatch), "SameArea", (Type[])null, (Type[])null); yield return instruction; } } } internal static class Landings { internal static readonly LandingEvents s_events = new LandingEvents(); private static readonly List<NetworkObject> s_mines = new List<NetworkObject>(); private static GameObject[] s_outside = Array.Empty<GameObject>(); private static GameObject[] s_inside = Array.Empty<GameObject>(); private static Random s_random; private static int s_generation; internal static int Generation => s_generation; internal static void Reset() { s_generation++; s_events.Reset(); s_mines.Clear(); s_outside = Array.Empty<GameObject>(); s_inside = Array.Empty<GameObject>(); Atmosphere.Clear(); ShowerGhosts.Reset(); } internal static void CacheNodes() { s_outside = GameObject.FindGameObjectsWithTag("OutsideAINode"); s_inside = GameObject.FindGameObjectsWithTag("AINode"); s_random = new Random(StartOfRound.Instance.randomMapSeed ^ 0x2D430); } internal static bool Position(bool outdoors, out Vector3 result) { //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_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_0084: Unknown result type (might be due to invalid IL or missing references) //IL_00af: 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) //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_00c5: 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_00e7: 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_0125: 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) GameObject[] array = (outdoors ? s_outside : s_inside); NavMeshHit val3 = default(NavMeshHit); RaycastHit val5 = default(RaycastHit); for (int i = 0; i < 160 && array.Length != 0; i++) { GameObject val = array[s_random.Next(array.Length)]; if ((Object)(object)val == (Object)null) { continue; } Vector3 val2 = val.transform.position + new Vector3((float)s_random.NextDouble() * 12f - 6f, 0f, (float)s_random.NextDouble() * 12f - 6f); if (!NavMesh.SamplePosition(val2, ref val3, 6f, -1)) { continue; } if (outdoors) { Vector3 val4 = StartOfRound.Instance.shipBounds.ClosestPoint(((NavMeshHit)(ref val3)).position) - ((NavMeshHit)(ref val3)).position; if (((Vector3)(ref val4)).sqrMagnitude < 64f) { continue; } } if (!Physics.Raycast(((NavMeshHit)(ref val3)).position + Vector3.up * 2f, Vector3.down, ref val5, 5f, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1)) { continue; } result = ((RaycastHit)(ref val5)).point; return true; } result = default(Vector3); return false; } internal static EnemyAI Spawn<T>(bool outdoors) where T : EnemyAI { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) if (!Position(outdoors, out var result)) { throw new InvalidOperationException("No valid " + (outdoors ? "outdoor" : "indoor") + " spawn point for " + typeof(T).Name); } NetworkObjectReference val = RoundManager.Instance.SpawnEnemyGameObject(result, (float)s_random.Next(360), -1, Content.Enemy<T>()); NetworkObject val2 = default(NetworkObject); if (!((NetworkObjectReference)(ref val)).TryGet(ref val2, (NetworkManager)null)) { throw new InvalidOperationException("Spawn failed for " + typeof(T).Name); } return ((Component)val2).GetComponent<EnemyAI>(); } internal static IEnumerator Land() { int thisGeneration = s_generation; yield return null; if (thisGeneration != s_generation || !Levels.s_ready) { yield break; } CacheNodes(); Atmosphere.Apply(); if (!((NetworkBehaviour)RoundManager.Instance).IsServer || !s_events.Pending("landing")) { yield break; } s_events.Done("landing"); string moon = Content.Moon; if (HasGuaranteedSapsucker(moon) && !RoundManager.Instance.SpawnedEnemies.Any((EnemyAI x) => x is GiantKiwiAI && !x.isEnemyDead)) { Landings.Spawn<GiantKiwiAI>(outdoors: true); Plugin.Log.LogInfo((object)("Spawned guaranteed sapsucker on " + moon + ".")); } if (moon == "Experimentation") { if ((Object)(object)Content.s_mine == (Object)null) { throw new InvalidOperationException("Couldn't find landmine prefab!!"); } for (int i = 0; i < 40; i++) { if (!Position(outdoors: true, out var p)) { throw new InvalidOperationException($"Only placed {i}/40 outdoor mines: no valid ground."); } GameObject obj = Object.Instantiate<GameObject>(Content.s_mine, p, Quaternion.Euler(0f, (float)s_random.Next(360), 0f)); NetworkObject net = obj.GetComponentInChildren<NetworkObject>(); if ((Object)(object)net == (Object)null) { throw new InvalidOperationException("Landmine prefab has no NetworkObject!!"); } net.Spawn(true); s_mines.Add(net); if (i % 5 == 4) { yield return null; if (thisGeneration != s_generation) { yield break; } } p = default(Vector3); } Plugin.Log.LogInfo((object)"Spawned experimentation mines"); ExtraSpikes(); } if (moon == "Titan") { int wanted = s_random.Next(2, 6); for (int i2 = 0; i2 < wanted; i2++) { Landings.Spawn<RedLocustBees>(outdoors: true); yield return null; if (thisGeneration != s_generation) { yield break; } } Plugin.Log.LogInfo((object)$"Trying to spawn {wanted} bees on titan"); } if (moon == "Embrion") { if (!RoundManager.Instance.SpawnedEnemies.Any((EnemyAI x) => x is DressGirlAI && !x.isEnemyDead)) { Landings.Spawn<DressGirlAI>(outdoors: false); } s_events.Done("ghost"); } } private static bool HasGuaranteedSapsucker(string moon) { switch (moon) { case "Titan": case "Artifice": case "Experimentation": case "Offense": return true; default: return false; } } private static void ExtraSpikes() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown if ((Object)(object)Content.s_spikeHazard == (Object)null) { throw new InvalidOperationException("Couldnt find spike traps!!"); } RoundManager instance = RoundManager.Instance; SelectableLevel currentLevel = instance.currentLevel; IndoorMapHazard[] indoorMapHazards = currentLevel.indoorMapHazards; SpawnableMapObject[] spawnableMapObjects = currentLevel.spawnableMapObjects; int increasedMapHazardSpawnRateIndex = instance.increasedMapHazardSpawnRateIndex; try { currentLevel.indoorMapHazards = (IndoorMapHazard[])(object)new IndoorMapHazard[1] { new IndoorMapHazard { hazardType = Content.s_spikeHazard, numberToSpawn = AnimationCurve.Constant(0f, 1f, 8f) } }; currentLevel.spawnableMapObjects = Array.Empty<SpawnableMapObject>(); instance.increasedMapHazardSpawnRateIndex = -1; instance.SpawnMapObjects(); } finally { currentLevel.indoorMapHazards = indoorMapHazards; currentLevel.spawnableMapObjects = spawnableMapObjects; instance.increasedMapHazardSpawnRateIndex = increasedMapHazardSpawnRateIndex; } Plugin.Log.LogInfo((object)"Requested 8 indoor spike traps using vanilla placement."); } internal static void Clock() { if (Levels.s_ready && !(Content.Moon != "Embrion") && StartOfRound.Instance.shipHasLanded && !StartOfRound.Instance.shipIsLeaving && ((NetworkBehaviour)RoundManager.Instance).IsServer && s_events.Pending("jester") && Rules.IsTenPm(TimeOfDay.Instance.normalizedTimeOfDay, TimeOfDay.Instance.numberOfHours)) { Landings.Spawn<JesterAI>(outdoors: true); s_events.Done("jester"); Plugin.Log.LogInfo((object)"22:00: Jester has landed..."); } } internal static void Cleanup() { if ((Object)(object)RoundManager.Instance != (Object)null && ((NetworkBehaviour)RoundManager.Instance).IsServer) { foreach (NetworkObject s_mine in s_mines) { if ((Object)(object)s_mine != (Object)null && s_mine.IsSpawned) { s_mine.Despawn(true); } } } Reset(); } } [HarmonyPatch(typeof(StartOfRound), "OnShipLandedMiscEvents")] internal static class LandPatch { private static void Postfix() { ((MonoBehaviour)Plugin.Instance).StartCoroutine(Landings.Land()); } } [HarmonyPatch(typeof(RoundManager), "Update")] internal static class ClockPatch { private static void Postfix() { Landings.Clock(); } } [HarmonyPatch(typeof(StartOfRound), "ShipHasLeft")] internal static class LeavePatch { private static void Prefix() { Landings.Cleanup(); } } internal static class Levels { internal sealed class Baseline { internal List<SpawnableEnemyWithRarity> _inside; internal List<SpawnableEnemyWithRarity> _outside; internal List<SpawnableItemWithRarity> _scrap; internal int _minScrap; internal int _maxScrap; internal int _power; internal int _diversity; internal float _size; internal AnimationCurve _spawnCurve; internal AnimationCurve[] _traps; internal AnimationCurve[] _hazards; internal Baseline(SelectableLevel l) { _inside = Copy(l.Enemies); _outside = Copy(l.OutsideEnemies); _scrap = CopyScrap(l.spawnableScrap); _minScrap = l.minScrap; _maxScrap = l.maxScrap; _power = l.maxEnemyPowerCount; _diversity = l.maxInsideDiversityPowerCount; _size = l.factorySizeMultiplier; _spawnCurve = l.enemySpawnChanceThroughoutDay; _traps = l.spawnableMapObjects.Select((SpawnableMapObject x) => x.numberToSpawn).ToArray(); _hazards = l.indoorMapHazards.Select((IndoorMapHazard x) => x.numberToSpawn).ToArray(); } internal void Restore(SelectableLevel l) { l.Enemies = Copy(_inside); l.OutsideEnemies = Copy(_outside); l.spawnableScrap = CopyScrap(_scrap); l.minScrap = _minScrap; l.maxScrap = _maxScrap; l.maxEnemyPowerCount = _power; l.maxInsideDiversityPowerCount = _diversity; l.factorySizeMultiplier = _size; l.enemySpawnChanceThroughoutDay = _spawnCurve; for (int i = 0; i < _traps.Length; i++) { l.spawnableMapObjects[i].numberToSpawn = _traps[i]; } for (int j = 0; j < _hazards.Length; j++) { l.indoorMapHazards[j].numberToSpawn = _hazards[j]; } } } private static readonly Dictionary<SelectableLevel, Baseline> s_originals = new Dictionary<SelectableLevel, Baseline>(); private static readonly Dictionary<EnemyType, (int max, bool falloff)> s_caps = new Dictionary<EnemyType, (int, bool)>(); private static List<SpawnableEnemyWithRarity> s_originalWeeds; internal static float s_scrapValue = 1f; internal static bool s_ready; private static List<SpawnableEnemyWithRarity> Copy(List<SpawnableEnemyWithRarity> list) { return ((IEnumerable<SpawnableEnemyWithRarity>)list).Select((Func<SpawnableEnemyWithRarity, SpawnableEnemyWithRarity>)((SpawnableEnemyWithRarity x) => new SpawnableEnemyWithRarity(x.enemyType, x.rarity))).ToList(); } private static List<SpawnableItemWithRarity> CopyScrap(List<SpawnableItemWithRarity> list) { return ((IEnumerable<SpawnableItemWithRarity>)list).Select((Func<SpawnableItemWithRarity, SpawnableItemWithRarity>)((SpawnableItemWithRarity x) => new SpawnableItemWithRarity(x.spawnableItem, x.rarity))).ToList(); } private static AnimationCurve Scale(AnimationCurve original, float multiplier) { //IL_0091: 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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Expected O, but got Unknown Keyframe[] keys = original.keys; for (int i = 0; i < keys.Length; i++) { ref Keyframe reference = ref keys[i]; ((Keyframe)(ref reference)).value = ((Keyframe)(ref reference)).value * multiplier; if (!float.IsInfinity(((Keyframe)(ref keys[i])).inTangent)) { ref Keyframe reference2 = ref keys[i]; ((Keyframe)(ref reference2)).inTangent = ((Keyframe)(ref reference2)).inTangent * multiplier; } if (!float.IsInfinity(((Keyframe)(ref keys[i])).outTangent)) { ref Keyframe reference3 = ref keys[i]; ((Keyframe)(ref reference3)).outTangent = ((Keyframe)(ref reference3)).outTangent * multiplier; } } return new AnimationCurve(keys) { preWrapMode = original.preWrapMode, postWrapMode = original.postWrapMode }; } private static void Multiply<T>(List<SpawnableEnemyWithRarity> pool, float amount) where T : EnemyAI { SpawnableEnemyWithRarity val = pool.Find((SpawnableEnemyWithRarity x) => (Object)(object)x.enemyType == (Object)(object)Content.Enemy<T>()); if (val != null) { val.rarity = Mathf.RoundToInt((float)val.rarity * amount); } } private static void Common<T>(List<SpawnableEnemyWithRarity> pool) where T : EnemyAI { pool.RemoveAll((SpawnableEnemyWithRarity x) => (Object)(object)x.enemyType == (Object)(object)Content.Enemy<T>()); pool.Add(Content.Entry<T>(Math.Max(1, Rules.ShareWeight(pool.Select((SpawnableEnemyWithRarity x) => x.rarity).ToArray(), 0.5)))); } private static void Unlimited<T>() where T : EnemyAI { EnemyType val = Content.Enemy<T>(); if (!s_caps.ContainsKey(val)) { s_caps.Add(val, (val.MaxCount, val.useNumberSpawnedFalloff)); } val.MaxCount = int.MaxValue; val.useNumberSpawnedFalloff = false; } private static void MultiplyCap<T>(int multiplier) where T : EnemyAI { EnemyType val = Content.Enemy<T>(); if (!s_caps.ContainsKey(val)) { s_caps.Add(val, (val.MaxCount, val.useNumberSpawnedFalloff)); } val.MaxCount = checked(s_caps[val].max * multiplier); } private static void Add<T>(List<SpawnableEnemyWithRarity> pool, int weight) where T : EnemyAI { if (!pool.Any((SpawnableEnemyWithRarity x) => (Object)(object)x.enemyType == (Object)(object)Content.Enemy<T>())) { pool.Add(Content.Entry<T>(weight)); } } internal static void Apply(SelectableLevel l) { //IL_03a2: Unknown result type (might be due to invalid IL or missing references) //IL_03ac: Expected O, but got Unknown //IL_03b5: Unknown result type (might be due to invalid IL or missing references) //IL_03bf: Expected O, but got Unknown //IL_0695: Unknown result type (might be due to invalid IL or missing references) //IL_069f: Expected O, but got Unknown //IL_054e: Unknown result type (might be due to invalid IL or missing references) //IL_0558: Expected O, but got Unknown s_ready = false; if (s_originals.Count == 0) { Content.Load(); } if (s_originalWeeds == null) { s_originalWeeds = Copy(RoundManager.Instance.WeedEnemies); } RoundManager.Instance.WeedEnemies = Copy(s_originalWeeds); if (!s_originals.TryGetValue(l, out var value)) { s_originals.Add(l, value = new Baseline(l)); } value.Restore(l); foreach (KeyValuePair<EnemyType, (int, bool)> s_cap in s_caps) { s_cap.Key.MaxCount = s_cap.Value.Item1; s_cap.Key.useNumberSpawnedFalloff = s_cap.Value.Item2; } Unlimited<PufferAI>(); Unlimited<ClaySurgeonAI>(); Unlimited<CaveDwellerAI>(); Unlimited<PumaAI>(); float num = 1f; float num2 = 1f; float num3 = 1f; s_scrapValue = 1f; switch (Rules.MoonName(l.PlanetName)) { case "Experimentation": { num = 1.5f; num2 = 4f; MultiplyCap<FlowerSnakeEnemy>(5); SpawnableItemWithRarity val = l.spawnableScrap.Find((SpawnableItemWithRarity x) => (Object)(object)x.spawnableItem == (Object)(object)Content.s_sheet); if (val != null) { val.rarity *= 5; } Common<FlowerSnakeEnemy>(l.Enemies); break; } case "Assurance": num = 1.5f; num2 = 1.5f; l.spawnableScrap = new List<SpawnableItemWithRarity> { new SpawnableItemWithRarity(Content.s_egg, 90), new SpawnableItemWithRarity(Content.s_flashbang, 10) }; Common<HoarderBugAI>(l.Enemies); Add<ButlerBeesEnemyAI>(l.OutsideEnemies, 50); break; case "Vow": l.OutsideEnemies = new List<SpawnableEnemyWithRarity> { Content.Entry<PumaAI>(50), Content.Entry<SpringManAI>(20) }; Multiply<CaveDwellerAI>(l.Enemies, 3f); break; case "Offense": { num3 = 5f; s_scrapValue = 1.8f; Type[] allowed = new Type[8] { typeof(SandSpiderAI), typeof(BlobAI), typeof(CentipedeAI), typeof(SpringManAI), typeof(StingrayAI), typeof(CaveDwellerAI), typeof(FlowermanAI), typeof(NutcrackerEnemyAI) }; l.Enemies.RemoveAll((SpawnableEnemyWithRarity x) => !allowed.Contains(((object)x.enemyType.enemyPrefab.GetComponent<EnemyAI>()).GetType())); Type[] array = allowed; foreach (Type t in array) { if (!l.Enemies.Any((SpawnableEnemyWithRarity x) => (Object)(object)x.enemyType == (Object)(object)Content.s_enemies[t])) { l.Enemies.Add(new SpawnableEnemyWithRarity(Content.s_enemies[t], Content.s_indoorWeights[t])); } } l.OutsideEnemies = new List<SpawnableEnemyWithRarity> { Content.Entry<CrawlerAI>(100) }; break; } case "March": num = 0.8f; s_scrapValue = 3f; l.factorySizeMultiplier = 2.1f; break; case "Adamance": s_scrapValue = 2f; Unlimited<CadaverGrowthAI>(); l.Enemies = new List<SpawnableEnemyWithRarity> { Content.Entry<CadaverGrowthAI>(50), Content.Entry<CadaverBloomAI>(10), Content.Entry<ButlerEnemyAI>(15), Content.Entry<JesterAI>(15), Content.Entry<DressGirlAI>(10), Content.Entry<MaskedPlayerEnemy>(5) }; l.OutsideEnemies = new List<SpawnableEnemyWithRarity> { Content.Entry<HoarderBugAI>(100) }; break; case "Rend": num2 = 1.8f; s_scrapValue = 1.2f; Multiply<NutcrackerEnemyAI>(l.Enemies, 2.5f); break; case "Dine": num = 1.1f; s_scrapValue = 0.9f; l.spawnableScrap.Add(new SpawnableItemWithRarity(Content.s_sheet, 750)); l.OutsideEnemies = new List<SpawnableEnemyWithRarity> { Content.Entry<ClaySurgeonAI>(100) }; break; case "Titan": num2 = 1.4f; num3 = 1.5f; break; case "Artifice": num2 = 2f; num = 1.5f; num3 = 1.5f; break; case "Embrion": num = 2f; num2 = 1.5f; num3 = 1.6f; l.OutsideEnemies = new List<SpawnableEnemyWithRarity> { Content.Entry<PufferAI>(100) }; break; } string text = Rules.MoonName(l.PlanetName); switch (text) { default: if (!(text == "Embrion")) { break; } goto case "Vow"; case "Vow": case "Offense": case "Adamance": case "Dine": RoundManager.Instance.WeedEnemies.RemoveAll((SpawnableEnemyWithRarity x) => !l.OutsideEnemies.Any((SpawnableEnemyWithRarity e) => (Object)(object)e.enemyType == (Object)(object)x.enemyType)); break; } l.minScrap = Mathf.RoundToInt((float)value._minScrap * num); l.maxScrap = Mathf.RoundToInt((float)value._maxScrap * num); l.maxEnemyPowerCount = Mathf.CeilToInt((float)value._power * num2); l.maxInsideDiversityPowerCount = Mathf.CeilToInt((float)value._diversity * num2); l.enemySpawnChanceThroughoutDay = Scale(value._spawnCurve, num2); for (int num5 = 0; num5 < l.spawnableMapObjects.Length; num5++) { GameObject prefabToSpawn = l.spawnableMapObjects[num5].prefabToSpawn; if ((Object)(object)prefabToSpawn.GetComponentInChildren<Landmine>(true) != (Object)null || (Object)(object)prefabToSpawn.GetComponentInChildren<Turret>(true) != (Object)null || (Object)(object)prefabToSpawn.GetComponentInChildren<SpikeRoofTrap>(true) != (Object)null) { l.spawnableMapObjects[num5].numberToSpawn = Scale(value._traps[num5], num3); } } for (int num6 = 0; num6 < l.indoorMapHazards.Length; num6++) { GameObject prefabToSpawn2 = l.indoorMapHazards[num6].hazardType.prefabToSpawn; if ((Object)(object)prefabToSpawn2.GetComponentInChildren<Landmine>(true) != (Object)null || (Object)(object)prefabToSpawn2.GetComponentInChildren<Turret>(true) != (Object)null || (Object)(object)prefabToSpawn2.GetComponentInChildren<SpikeRoofTrap>(true) != (Object)null) { l.indoorMapHazards[num6].numberToSpawn = Scale(value._hazards[num6], num3); } } s_ready = true; Plugin.Log.LogInfo((object)$"APPLIED MODIFIERS!! 1-{l.PlanetName}|2-{num}|3-{s_scrapValue}|4-{num2}|5-{num3}"); } internal static void RestoreAll() { foreach (KeyValuePair<SelectableLevel, Baseline> s_original in s_originals) { if ((Object)(object)s_original.Key != (Object)null) { s_original.Value.Restore(s_original.Key); } } foreach (KeyValuePair<EnemyType, (int, bool)> s_cap in s_caps) { if ((Object)(object)s_cap.Key != (Object)null) { s_cap.Key.MaxCount = s_cap.Value.Item1; s_cap.Key.useNumberSpawnedFalloff = s_cap.Value.Item2; } } if (s_originalWeeds != null && (Object)(object)RoundManager.Instance != (Object)null) { RoundManager.Instance.WeedEnemies = Copy(s_originalWeeds); } s_originalWeeds = null; s_originals.Clear(); s_caps.Clear(); s_ready = false; } } [HarmonyPatch(typeof(RoundManager), "SpawnRandomWeedEnemy")] internal static class EmptyWeedPoolPatch { private static bool Prefix(RoundManager __instance, ref bool __result) { if (!Levels.s_ready || __instance.WeedEnemies.Count > 0) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(RoundManager), "GenerateNewFloor")] internal static class GeneratePatch { [HarmonyPrefix] [HarmonyPriority(0)] private static void Prefix(RoundManager __instance) { Levels.Apply(__instance.currentLevel); Landings.Reset(); } } [HarmonyPatch(typeof(StartOfRound), "OnDestroy")] internal static class RoundEndPatch { private static void Prefix() { EnemyEvents.Unbind(); Levels.RestoreAll(); Landings.Reset(); } } [BepInPlugin("juanradical.moonoverhaul", "RadsMoons", "1.0.1")] public sealed class Plugin : BaseUnityPlugin { internal const string PluginGuid = "juanradical.moonoverhaul"; internal const string PluginName = "RadsMoons"; internal const string PluginVersion = "1.0.1"; private Harmony _harmony; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } private void Awake() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("juanradical.moonoverhaul"); _harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)"RadsMoons 1.0.1 loaded. Install on every player."); } private void OnDestroy() { EnemyEvents.Unbind(); Levels.RestoreAll(); if (_harmony != null) { _harmony.UnpatchSelf(); } } } internal static class Rules { internal static int ShareWeight(int[] otherWeights, double share) { if (share <= 0.0 || share >= 1.0) { throw new ArgumentOutOfRangeException("share"); } return checked((int)Math.Round((double)((IEnumerable<int>)otherWeights).Sum((Func<int, long>)((int x) => x)) * share / (1.0 - share))); } internal static string MoonName(string value) { value = value.Trim(); int num = value.IndexOf(' '); int result; return (num > 0 && int.TryParse(value.Substring(0, num), out result)) ? value.Substring(num + 1).Trim() : value; } internal static bool IsTenPm(float normalizedTime, float numberOfHours = 18f) { return normalizedTime >= 16f / numberOfHours; } internal static bool EggExplodes(int roll) { return roll < 25; } internal static int OtherPlayer(int[] livingPlayers, int current, Random random) { int[] array = livingPlayers.Where((int id) => id != current).ToArray(); return (array.Length == 0) ? (-1) : array[random.Next(array.Length)]; } } internal sealed class ShowerVisit { private readonly HashSet<int> _used = new HashSet<int>(); internal bool Available(int player) { return !_used.Contains(player); } internal void Used(int player) { _used.Add(player); } internal void Leave(int player) { _used.Remove(player); } } internal sealed class LandingEvents { private readonly HashSet<string> _completed = new HashSet<string>(); internal bool Pending(string name) { return !_completed.Contains(name); } internal void Done(string name) { _completed.Add(name); } internal void Reset() { _completed.Clear(); } } [HarmonyPatch(typeof(RoundManager), "SpawnScrapInLevel")] internal static class ScrapPatch { private static Random s_random; private static List<SpawnableItemWithRarity> s_pool; private static int s_totalWeight; private static void Prefix(RoundManager __instance) { s_pool = __instance.currentLevel.spawnableScrap; s_totalWeight = s_pool.Sum((SpawnableItemWithRarity x) => x.rarity); s_random = new Random(StartOfRound.Instance.randomMapSeed ^ 0x193E); } private static Exception Finalizer(Exception __exception) { s_pool = null; return __exception; } private static void AddScrap(List<Item> selected, Item original) { if (Content.Moon != "Assurance" && Content.Moon != "Dine") { selected.Add(original); return; } int num = s_random.Next(s_totalWeight); foreach (SpawnableItemWithRarity item in s_pool) { num -= item.rarity; if (num >= 0) { continue; } selected.Add(item.spawnableItem); return; } throw new InvalidOperationException("Scrap spawn weights are invalid"); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo original = AccessTools.Method(typeof(List<Item>), "Add", (Type[])null, (Type[])null); MethodInfo replacement = AccessTools.Method(typeof(ScrapPatch), "AddScrap", (Type[])null, (Type[])null); int changed = 0; foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, original)) { instruction.opcode = OpCodes.Call; instruction.operand = replacement; changed++; } yield return instruction; } if (changed == 0) { throw new InvalidOperationException("Unsupported game version: scrap list insertion was not found."); } } } [HarmonyPatch(typeof(RoundManager), "waitForScrapToSpawnToSync")] internal static class ScrapValuePatch { private static void Prefix(NetworkObjectReference[] spawnedScrap, int[] scrapValues) { if (!((NetworkBehaviour)RoundManager.Instance).IsServer || Levels.s_scrapValue == 1f) { return; } NetworkObject val = default(NetworkObject); for (int i = 0; i < scrapValues.Length; i++) { scrapValues[i] = Mathf.RoundToInt((float)scrapValues[i] * Levels.s_scrapValue); if (((NetworkObjectReference)(ref spawnedScrap[i])).TryGet(ref val, (NetworkManager)null)) { ((Component)val).GetComponent<GrabbableObject>().SetScrapValue(scrapValues[i]); } } } } [HarmonyPatch(typeof(StunGrenadeItem), "SetExplodeOnThrowServerRpc")] internal static class EggPatch { internal static bool IsEgg(StunGrenadeItem item) { return ((GrabbableObject)item).itemProperties.itemName.Equals("Easter egg", StringComparison.OrdinalIgnoreCase); } private static void Prefix(StunGrenadeItem __instance) { if (IsEgg(__instance)) { __instance.chanceToExplode = 25f; } } private static int Roll(Random random, int min, int max, StunGrenadeItem item) { int num = random.Next(min, max); return (!IsEgg(item) || min != 0 || max != 100) ? num : ((!Rules.EggExplodes(num)) ? 100 : 0); } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo next = AccessTools.Method(typeof(Random), "Next", new Type[2] { typeof(int), typeof(int) }, (Type[])null); int changed = 0; foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, next)) { CodeInstruction load = new CodeInstruction(OpCodes.Ldarg_0, (object)null); load.labels.AddRange(instruction.labels); instruction.labels.Clear(); load.blocks.AddRange(instruction.blocks); instruction.blocks.Clear(); yield return load; instruction.opcode = OpCodes.Call; instruction.operand = AccessTools.Method(typeof(EggPatch), "Roll", (Type[])null, (Type[])null); changed++; } yield return instruction; } if (changed == 0) { throw new InvalidOperationException("Couldn't change egg explode chance!!"); } } } [HarmonyPatch(typeof(ShipBuildModeManager), "Update")] internal static class ShipPlacementPatch { private static void Prefix(ShipBuildModeManager __instance) { __instance.ghostObjectRed = __instance.ghostObjectGreen; } private static void Postfix(ref bool ___CanConfirmPosition) { ___CanConfirmPosition = true; } } [HarmonyPatch(typeof(ShipBuildModeManager), "ConfirmBuildMode_performed")] internal static class ShipPlacementConfirmPatch { private static void Prefix(ref bool ___CanConfirmPosition) { ___CanConfirmPosition = true; } } [HarmonyPatch(typeof(ShotgunItem), "Update")] internal static class ShotgunMisfirePatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown List<CodeInstruction> list = instructions.ToList(); FieldInfo fieldInfo = AccessTools.Field(typeof(ShotgunItem), "misfireTimer"); MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(Time), "deltaTime"); int num = 0; for (int i = 0; i < list.Count - 3; i++) { if (CodeInstructionExtensions.LoadsField(list[i], fieldInfo, false) && CodeInstructionExtensions.Calls(list[i + 1], methodInfo) && !(list[i + 2].opcode != OpCodes.Sub) && CodeInstructionExtensions.StoresField(list[i + 3], fieldInfo)) { list.InsertRange(i + 2, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[2] { new CodeInstruction(OpCodes.Ldc_R4, (object)5f), new CodeInstruction(OpCodes.Mul, (object)null) }); num++; i += 5; } } if (num != 1) { throw new InvalidOperationException($"Expected one shotgun misfire countdown, found {num}."); } return list; } } [HarmonyPatch(typeof(StartOfRound), "LoadUnlockables")] internal static class ShowerUnlockPatch { internal static void Postfix(StartOfRound __instance) { if (((NetworkBehaviour)__instance).IsServer) { UnlockableItem val = __instance.unlockablesList.unlockables.Single((UnlockableItem x) => x.unlockableName.Equals("Shower", StringComparison.OrdinalIgnoreCase)); if (!val.hasBeenUnlockedByPlayer && !val.alreadyUnlocked) { val.hasBeenUnlockedByPlayer = true; val.inStorage = true; Plugin.Log.LogInfo((object)"Shower unlocked automatically"); } } } } [HarmonyPatch(typeof(StartOfRound), "ResetShip")] internal static class ShowerResetPatch { private static void Postfix(StartOfRound __instance) { ShowerUnlockPatch.Postfix(__instance); } } [HarmonyPatch(typeof(ShowerTrigger), "Update")] internal static class ShowerCurePatch { private static void Postfix(ShowerTrigger __instance, bool ___showerOn) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) ShowerGhosts.Update(__instance, ___showerOn); if (!___showerOn || (Object)(object)RoundManager.Instance == (Object)null) { return; } PlayerControllerB localPlayerController = GameNetworkManager.Instance.localPlayerController; if ((Object)(object)localPlayerController == (Object)null || localPlayerController.isPlayerDead) { return; } Bounds bounds = __instance.showerCollider.bounds; if (!((Bounds)(ref bounds)).Contains(((Component)localPlayerController.gameplayCamera).transform.position)) { return; } int num = (int)localPlayerController.playerClientId; foreach (EnemyAI spawnedEnemy in RoundManager.Instance.SpawnedEnemies) { CadaverGrowthAI val = (CadaverGrowthAI)(object)((spawnedEnemy is CadaverGrowthAI) ? spawnedEnemy : null); if (val != null && !((EnemyAI)val).isEnemyDead && val.playerInfections != null && num < val.playerInfections.Length) { PlayerInfection val2 = val.playerInfections[num]; if (val2 != null && (val2.infected || val2.infectionMeter > 0f || val2.burstMeter > 0f)) { val.CurePlayer(num); val.CurePlayerRpc(num); } } } } } internal static class ShowerGhosts { private static readonly Dictionary<ShowerTrigger, ShowerVisit> s_visits = new Dictionary<ShowerTrigger, ShowerVisit>(); private static readonly MethodInfo s_stop = AccessTools.Method(typeof(DressGirlAI), "StopChasing", (Type[])null, (Type[])null); private static readonly MethodInfo s_stare = AccessTools.Method(typeof(DressGirlAI), "SetHauntStarePosition", (Type[])null, (Type[])null); private static readonly MethodInfo s_chase = AccessTools.Method(typeof(DressGirlAI), "BeginChasing", (Type[])null, (Type[])null); private static readonly FieldInfo s_disappear = AccessTools.Field(typeof(DressGirlAI), "disappearOnDelayCoroutine"); private static readonly FieldInfo s_switching = AccessTools.Field(typeof(DressGirlAI), "switchHauntedPlayerCoroutine"); private static Random s_random; internal static void Reset() { s_visits.Clear(); s_random = null; } internal static void Update(ShowerTrigger shower, bool running) { //IL_00cd: 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_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) if (!Levels.s_ready || (Object)(object)RoundManager.Instance == (Object)null || !((NetworkBehaviour)RoundManager.Instance).IsServer || !StartOfRound.Instance.shipHasLanded) { return; } if (!s_visits.TryGetValue(shower, out var value)) { s_visits.Add(shower, value = new ShowerVisit()); } if (s_random == null) { s_random = new Random(StartOfRound.Instance.randomMapSeed ^ 0x2C17); } PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; PlayerControllerB[] array = allPlayerScripts; NavMeshHit val6 = default(NavMeshHit); foreach (PlayerControllerB val in array) { int num = (int)val.playerClientId; if (running && val.isPlayerControlled && !val.isPlayerDead) { Bounds bounds = shower.showerCollider.bounds; if (((Bounds)(ref bounds)).Contains(((Component)val.gameplayCamera).transform.position)) { if (!value.Available(num)) { continue; } foreach (EnemyAI spawnedEnemy in RoundManager.Instance.SpawnedEnemies) { DressGirlAI val2 = (DressGirlAI)(object)((spawnedEnemy is DressGirlAI) ? spawnedEnemy : null); if (val2 == null || ((EnemyAI)val2).isEnemyDead || (Object)(object)val2.hauntingPlayer != (Object)(object)val) { continue; } int num2 = Rules.OtherPlayer((from p in allPlayerScripts where p.isPlayerControlled && !p.isPlayerDead select (int)p.playerClientId).ToArray(), num, s_random); if (num2 < 0) { continue; } PlayerControllerB val3 = allPlayerScripts[num2]; GameObject[] array2 = (val3.isInsideFactory ? GameObject.FindGameObjectsWithTag("AINode") : GameObject.FindGameObjectsWithTag("OutsideAINode")); float num3 = float.PositiveInfinity; Vector3 position = default(Vector3); GameObject[] array3 = array2; foreach (GameObject val4 in array3) { Vector3 val5 = val4.transform.position - ((Component)val3).transform.position; float sqrMagnitude = ((Vector3)(ref val5)).sqrMagnitude; if (!(sqrMagnitude < 36f) && !(sqrMagnitude >= num3) && !(sqrMagnitude > 1600f) && NavMesh.SamplePosition(val4.transform.position, ref val6, 2f, -1)) { num3 = sqrMagnitude; position = ((NavMeshHit)(ref val6)).position; } } if (float.IsPositiveInfinity(num3)) { throw new InvalidOperationException("No valid nearby ghost spawn point for shower transfer."); } ChangeTarget(val2, val3); EnemyEvents.Send(2, (EnemyAI)(object)val2, num2, position, Vector3.zero); ((EnemyAI)val2).ChangeOwnershipOfEnemy(val3.actualClientId); value.Used(num); Plugin.Log.LogInfo((object)$"Transferred ghost girl from player {num} to {num2}"); } continue; } } value.Leave(num); } } internal static void ChangeTarget(DressGirlAI ghost, PlayerControllerB player) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown Coroutine val = (Coroutine)s_disappear.GetValue(ghost); if (val != null) { ((MonoBehaviour)ghost).StopCoroutine(val); s_disappear.SetValue(ghost, null); } val = (Coroutine)s_switching.GetValue(ghost); if (val != null) { ((MonoBehaviour)ghost).StopCoroutine(val); s_switching.SetValue(ghost, null); } s_stop.Invoke(ghost, null); ghost.heartbeatMusic.volume = 0f; ((EnemyAI)ghost).creatureSFX.Stop(); ghost.hauntingPlayer = player; ghost.hauntingLocalPlayer = (Object)(object)GameNetworkManager.Instance.localPlayerController == (Object)(object)player; ghost.staringInHaunt = false; ghost.disappearingFromStare = false; AccessTools.Field(typeof(DressGirlAI), "switchedHauntingPlayer").SetValue(ghost, false); } internal static void Attack(DressGirlAI ghost, Vector3 position) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) ((EnemyAI)ghost).SetEnemyOutside(!ghost.hauntingPlayer.isInsideFactory); s_stare.Invoke(ghost, new object[2] { position, 0f }); s_chase.Invoke(ghost, null); } } [HarmonyPatch(typeof(TimeOfDay), "DecideRandomDayEvents")] internal static class MeteorChancePatch { private static int Roll(Random random, int min, int max) { int num = random.Next(min, max); return (min == 0 && max == 1000) ? (num / 10) : num; } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo next = AccessTools.Method(typeof(Random), "Next", new Type[2] { typeof(int), typeof(int) }, (Type[])null); MethodInfo replacement = AccessTools.Method(typeof(MeteorChancePatch), "Roll", (Type[])null, (Type[])null); int changed = 0; foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, next)) { instruction.opcode = OpCodes.Call; instruction.operand = replacement; changed++; } yield return instruction; } if (changed == 0) { throw new InvalidOperationException("Meteor event random roll was not found."); } } } }