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 Schizo v1.1.0
Schizo.dll
Decompiled 2 days agousing System; using System.Collections; using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using TMPro; using Unity.Collections; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("0.0.0.0")] namespace Schizo; internal static class Affliction { private const string AssignMessage = "Schizo.Assign"; private const string SlotsMessage = "Schizo.Slots"; private const ulong Nobody = ulong.MaxValue; internal static ulong AfflictedClientId = ulong.MaxValue; private static readonly Dictionary<ulong, int> phantomSlotMasks = new Dictionary<ulong, int>(); private static CustomMessagingManager registeredWith; internal static bool LocalPlayerIsAfflicted { get { PlayerControllerB localPlayer = LocalPlayer; if ((Object)(object)localPlayer != (Object)null && AfflictedClientId != ulong.MaxValue) { return localPlayer.actualClientId == AfflictedClientId; } return false; } } internal static PlayerControllerB LocalPlayer { get { if (!((Object)(object)GameNetworkManager.Instance != (Object)null)) { return null; } return GameNetworkManager.Instance.localPlayerController; } } internal static void OnLobbyStarted() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown AfflictedClientId = ulong.MaxValue; phantomSlotMasks.Clear(); NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.CustomMessagingManager != null && singleton.CustomMessagingManager != registeredWith) { registeredWith = singleton.CustomMessagingManager; registeredWith.RegisterNamedMessageHandler("Schizo.Assign", new HandleNamedMessageDelegate(OnAssignReceived)); registeredWith.RegisterNamedMessageHandler("Schizo.Slots", new HandleNamedMessageDelegate(OnSlotsReceived)); } } internal static void OnLobbyEnded() { if (registeredWith != null) { try { registeredWith.UnregisterNamedMessageHandler("Schizo.Assign"); registeredWith.UnregisterNamedMessageHandler("Schizo.Slots"); } catch (Exception) { } registeredWith = null; } AfflictedClientId = ulong.MaxValue; phantomSlotMasks.Clear(); } internal static void OnRoundEnded() { phantomSlotMasks.Clear(); } internal static void HostAssignForRound() { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || !singleton.IsServer) { return; } if (AfflictedClientId == ulong.MaxValue || Plugin.RerollEachRound.Value || (Object)(object)FindPlayer(AfflictedClientId) == (Object)null) { List<PlayerControllerB> list = new List<PlayerControllerB>(); PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val != (Object)null && val.isPlayerControlled && !val.isPlayerDead) { list.Add(val); } } if (list.Count == 0) { return; } AfflictedClientId = list[Random.Range(0, list.Count)].actualClientId; } byte[] bytes = BitConverter.GetBytes(AfflictedClientId); foreach (ulong connectedClientsId in singleton.ConnectedClientsIds) { if (connectedClientsId != singleton.LocalClientId) { Send("Schizo.Assign", connectedClientsId, bytes); } } ApplyAssignment(AfflictedClientId); } private static void OnAssignReceived(ulong senderClientId, FastBufferReader reader) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (senderClientId == 0) { byte[] array = Read(reader, 8); if (array != null) { ApplyAssignment(BitConverter.ToUInt64(array, 0)); } } } private static void ApplyAssignment(ulong clientId) { AfflictedClientId = clientId; if (LocalPlayerIsAfflicted) { Phantoms.SpawnForRound(); if (Plugin.TellAfflictedPlayer.Value && (Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.DisplayTip("Something is wrong", "Not everything you see in there is real.", true, false, "LC_Tip1"); } } } internal static void BroadcastLocalPhantomSlots(int mask) { NetworkManager singleton = NetworkManager.Singleton; if (!((Object)(object)singleton == (Object)null) && singleton.IsListening) { if (singleton.IsServer) { Relay(singleton, singleton.LocalClientId, mask, singleton.LocalClientId); } else { Send("Schizo.Slots", 0uL, BitConverter.GetBytes(mask)); } } } private static void OnSlotsReceived(ulong senderClientId, FastBufferReader reader) { //IL_0018: 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) NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null) { return; } if (singleton.IsServer) { byte[] array = Read(reader, 4); if (array != null) { int num = BitConverter.ToInt32(array, 0); phantomSlotMasks[senderClientId] = num; Relay(singleton, senderClientId, num, senderClientId); } } else if (senderClientId == 0) { byte[] array2 = Read(reader, 12); if (array2 != null) { phantomSlotMasks[BitConverter.ToUInt64(array2, 0)] = BitConverter.ToInt32(array2, 8); } } } private static void Relay(NetworkManager network, ulong owner, int mask, ulong skip) { byte[] array = new byte[12]; Buffer.BlockCopy(BitConverter.GetBytes(owner), 0, array, 0, 8); Buffer.BlockCopy(BitConverter.GetBytes(mask), 0, array, 8, 4); foreach (ulong connectedClientsId in network.ConnectedClientsIds) { if (connectedClientsId != network.LocalClientId && connectedClientsId != skip) { Send("Schizo.Slots", connectedClientsId, array); } } } internal static int PhantomSlotMaskOf(PlayerControllerB player) { if ((Object)(object)player == (Object)(object)LocalPlayer || !phantomSlotMasks.TryGetValue(player.actualClientId, out var value)) { return 0; } return value; } private static PlayerControllerB FindPlayer(ulong clientId) { PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts; foreach (PlayerControllerB val in allPlayerScripts) { if ((Object)(object)val != (Object)null && val.isPlayerControlled && val.actualClientId == clientId) { return val; } } return null; } private static void Send(string message, ulong clientId, byte[] payload) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) FastBufferWriter val = default(FastBufferWriter); ((FastBufferWriter)(ref val))..ctor(payload.Length, (Allocator)2, -1); try { ((FastBufferWriter)(ref val)).WriteBytesSafe(payload, payload.Length, 0); NetworkManager.Singleton.CustomMessagingManager.SendNamedMessage(message, clientId, val, (NetworkDelivery)3); } finally { ((FastBufferWriter)(ref val)).Dispose(); } } private static byte[] Read(FastBufferReader reader, int size) { if (((FastBufferReader)(ref reader)).Length - ((FastBufferReader)(ref reader)).Position < size) { return null; } byte[] result = new byte[size]; ((FastBufferReader)(ref reader)).ReadBytesSafe(ref result, size, 0); return result; } } internal static class Monsters { private static readonly HashSet<string> allowedAITypes = new HashSet<string> { "CrawlerAI", "FlowermanAI", "HoarderBugAI", "SandSpiderAI", "SpringManAI", "NutcrackerEnemyAI", "ButlerEnemyAI", "PufferAI", "JesterAI" }; private static readonly List<PhantomMonster> alive = new List<PhantomMonster>(); private static float nextSpawnTime = -1f; internal static void Tick(PlayerControllerB player) { StartOfRound instance = StartOfRound.Instance; if (!Plugin.MonsterHallucinations.Value || (Object)(object)instance == (Object)null || instance.inShipPhase || !instance.shipHasLanded || !player.isInsideFactory || player.isPlayerDead) { nextSpawnTime = -1f; } else if (nextSpawnTime < 0f) { nextSpawnTime = Time.time + NextInterval(); } else if (!(Time.time < nextSpawnTime)) { alive.RemoveAll((PhantomMonster monster) => (Object)(object)monster == (Object)null); if (alive.Count < Plugin.MaxMonstersAtOnce.Value) { nextSpawnTime = Time.time + (TrySpawn(player) ? NextInterval() : 5f); } } } private static float NextInterval() { float num = Mathf.Max(5f, Plugin.MonsterIntervalMin.Value); return Random.Range(num, Mathf.Max(num, Plugin.MonsterIntervalMax.Value)); } private static bool TrySpawn(PlayerControllerB player) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (!FindHiddenSpawn(player, out var position)) { return false; } List<EnemyType> list = EnemyPool(); if (list.Count == 0) { return false; } EnemyType enemy = list[Random.Range(0, list.Count)]; PhantomMonster phantomMonster = Build(enemy, position); if ((Object)(object)phantomMonster == (Object)null) { return false; } phantomMonster.Begin(player); alive.Add(phantomMonster); return true; } private static bool FindHiddenSpawn(PlayerControllerB player, out Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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_0077: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: 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) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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) position = Vector3.zero; GameObject[] array = (((Object)(object)RoundManager.Instance != (Object)null) ? RoundManager.Instance.insideAINodes : null); if (array == null || array.Length == 0) { return false; } Vector3 position2 = ((Component)player.gameplayCamera).transform.position; int collidersAndRoomMaskAndDefault = StartOfRound.Instance.collidersAndRoomMaskAndDefault; List<Vector3> list = new List<Vector3>(); GameObject[] array2 = array; NavMeshHit val2 = default(NavMeshHit); foreach (GameObject val in array2) { if (!((Object)(object)val == (Object)null)) { Vector3 position3 = val.transform.position; float num = Vector3.Distance(position3, ((Component)player).transform.position); if (!(num < 14f) && !(num > 40f) && Physics.Linecast(position2, position3 + Vector3.up, collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1) && NavMesh.SamplePosition(position3, ref val2, 2f, -1)) { list.Add(((NavMeshHit)(ref val2)).position); } } } if (list.Count == 0) { return false; } position = list[Random.Range(0, list.Count)]; return true; } private static List<EnemyType> EnemyPool() { List<EnemyType> list = new List<EnemyType>(); StartOfRound instance = StartOfRound.Instance; AddAllowed(instance.currentLevel, list); if (list.Count == 0 && instance.levels != null) { SelectableLevel[] levels = instance.levels; foreach (SelectableLevel level in levels) { AddAllowed(level, list); } } return list; } private static void AddAllowed(SelectableLevel level, List<EnemyType> pool) { if ((Object)(object)level == (Object)null || level.Enemies == null) { return; } foreach (SpawnableEnemyWithRarity enemy in level.Enemies) { if (enemy != null && !((Object)(object)enemy.enemyType == (Object)null) && !((Object)(object)enemy.enemyType.enemyPrefab == (Object)null) && !pool.Contains(enemy.enemyType)) { EnemyAI componentInChildren = enemy.enemyType.enemyPrefab.GetComponentInChildren<EnemyAI>(true); if ((Object)(object)componentInChildren != (Object)null && allowedAITypes.Contains(((object)componentInChildren).GetType().Name)) { pool.Add(enemy.enemyType); } } } } private static PhantomMonster Build(EnemyType enemy, Vector3 position) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("SchizoMonsterHolder"); val.SetActive(false); try { GameObject val2 = Object.Instantiate<GameObject>(enemy.enemyPrefab, val.transform); ((Object)val2).name = "Hallucination (" + enemy.enemyName + ")"; EnemyAI componentInChildren = val2.GetComponentInChildren<EnemyAI>(true); bool freezes = componentInChildren is SpringManAI; List<AudioClip> harvested = HarvestSounds(componentInChildren); Strip(val2); val2.transform.position = position; val2.transform.rotation = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f); PhantomMonster phantomMonster = val2.AddComponent<PhantomMonster>(); phantomMonster.Configure(harvested, freezes); val2.transform.SetParent((Transform)null, true); return phantomMonster; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Couldn't build a hallucination from " + enemy.enemyName + ": " + ex.Message)); return null; } finally { Object.Destroy((Object)(object)val); } } private static List<AudioClip> HarvestSounds(EnemyAI ai) { List<AudioClip> list = new List<AudioClip>(); if ((Object)(object)ai == (Object)null) { return list; } Type type = ((object)ai).GetType(); while (type != null && type != typeof(MonoBehaviour)) { FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(AudioClip)) { object? value = fieldInfo.GetValue(ai); AudioClip val = (AudioClip)((value is AudioClip) ? value : null); if ((Object)(object)val != (Object)null && !list.Contains(val)) { list.Add(val); } } else { if (!(fieldInfo.FieldType == typeof(AudioClip[])) || !(fieldInfo.GetValue(ai) is AudioClip[] array)) { continue; } AudioClip[] array2 = array; foreach (AudioClip val2 in array2) { if ((Object)(object)val2 != (Object)null && !list.Contains(val2)) { list.Add(val2); } } } } type = type.BaseType; } return list; } private static void Strip(GameObject body) { int num = int.MaxValue; for (int i = 0; i < 5; i++) { List<MonoBehaviour> list = new List<MonoBehaviour>(); MonoBehaviour[] componentsInChildren = body.GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour val in componentsInChildren) { if ((Object)(object)val != (Object)null && !(val is ScanNodeProperties)) { list.Add(val); } } if (list.Count == 0 || list.Count >= num) { break; } num = list.Count; foreach (MonoBehaviour item in list) { Object.DestroyImmediate((Object)(object)item); } } Collider[] componentsInChildren2 = body.GetComponentsInChildren<Collider>(true); foreach (Collider val2 in componentsInChildren2) { if ((Object)(object)((Component)val2).GetComponent<ScanNodeProperties>() == (Object)null) { Object.DestroyImmediate((Object)(object)val2); } } Rigidbody[] componentsInChildren3 = body.GetComponentsInChildren<Rigidbody>(true); foreach (Rigidbody val3 in componentsInChildren3) { val3.isKinematic = true; } ScanNodeProperties[] componentsInChildren4 = body.GetComponentsInChildren<ScanNodeProperties>(true); foreach (ScanNodeProperties val4 in componentsInChildren4) { val4.creatureScanID = -1; } Animator[] componentsInChildren5 = body.GetComponentsInChildren<Animator>(true); foreach (Animator val5 in componentsInChildren5) { val5.fireEvents = false; val5.applyRootMotion = false; } } internal static void DestroyAll() { foreach (PhantomMonster item in alive) { if ((Object)(object)item != (Object)null) { Object.Destroy((Object)(object)((Component)item).gameObject); } } alive.Clear(); nextSpawnTime = -1f; } } internal class PhantomMonster : MonoBehaviour { private const float WalkSpeed = 3.5f; private const float ChaseSpeed = 6.5f; private const float ChaseRange = 18f; private const float LungeRange = 1.4f; private PlayerControllerB target; private NavMeshAgent agent; private Animator[] animators; private readonly List<KeyValuePair<Animator, int>> speedFloats = new List<KeyValuePair<Animator, int>>(); private readonly List<KeyValuePair<Animator, int>> movingBools = new List<KeyValuePair<Animator, int>>(); private AudioSource voice; private List<AudioClip> sounds; private bool freezesWhenSeen; private float lifetime; private float nextPath; private float nextSound; private bool vanishing; internal void Configure(List<AudioClip> harvested, bool freezes) { sounds = harvested; freezesWhenSeen = freezes; } internal void Begin(PlayerControllerB player) { //IL_0098: Unknown result type (might be due to invalid IL or missing references) target = player; lifetime = Random.Range(25f, 45f); nextSound = Time.time + Random.Range(1f, 4f); animators = ((Component)this).GetComponentsInChildren<Animator>(); FindMovementParameters(); voice = ((Component)this).GetComponentInChildren<AudioSource>(); agent = ((Component)this).GetComponent<NavMeshAgent>(); if ((Object)(object)agent == (Object)null) { agent = ((Component)this).gameObject.AddComponent<NavMeshAgent>(); } ((Behaviour)agent).enabled = true; agent.Warp(((Component)this).transform.position); agent.speed = 3.5f; agent.acceleration = 12f; agent.angularSpeed = 300f; agent.stoppingDistance = 0f; agent.autoBraking = false; } private void Update() { //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_011b: Unknown result type (might be due to invalid IL or missing references) if (vanishing) { return; } if ((Object)(object)target == (Object)null || target.isPlayerDead || !target.isInsideFactory) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } lifetime -= Time.deltaTime; if (lifetime <= 0f) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } float num = Vector3.Distance(((Component)this).transform.position, ((Component)target).transform.position); if (num < 1.4f) { Lunge(); return; } bool flag = PlayerCanSee(); bool flag2 = freezesWhenSeen && flag; if (agent.isOnNavMesh) { agent.isStopped = flag2; agent.speed = ((num < 18f && HasLineOfSight()) ? 6.5f : 3.5f); if (Time.time >= nextPath) { nextPath = Time.time + 0.25f; agent.SetDestination(((Component)target).transform.position); } } Animate(flag2); if (!flag2 && Time.time >= nextSound) { nextSound = Time.time + Random.Range(3f, 8f); PlayRandomSound(0.8f); } } private void FindMovementParameters() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Invalid comparison between Unknown and I4 //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Invalid comparison between Unknown and I4 Animator[] array = animators; foreach (Animator val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.runtimeAnimatorController == (Object)null) { continue; } AnimatorControllerParameter[] parameters = val.parameters; foreach (AnimatorControllerParameter val2 in parameters) { string text = val2.name.ToLowerInvariant(); if ((int)val2.type == 1 && text.Contains("speed")) { speedFloats.Add(new KeyValuePair<Animator, int>(val, val2.nameHash)); } else if ((int)val2.type == 4 && (text.Contains("walk") || text.Contains("moving") || text.Contains("run"))) { movingBools.Add(new KeyValuePair<Animator, int>(val, val2.nameHash)); } } } } private void Animate(bool frozen) { //IL_001b: 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) float num; if (!((Object)(object)agent != (Object)null)) { num = 0f; } else { Vector3 velocity = agent.velocity; num = ((Vector3)(ref velocity)).magnitude; } float num2 = num; Animator[] array = animators; foreach (Animator val in array) { if ((Object)(object)val != (Object)null) { val.speed = (frozen ? 0f : 1f); } } foreach (KeyValuePair<Animator, int> speedFloat in speedFloats) { speedFloat.Key.SetFloat(speedFloat.Value, Mathf.Clamp(num2 / 3.5f, 0f, 2f)); } foreach (KeyValuePair<Animator, int> movingBool in movingBools) { movingBool.Key.SetBool(movingBool.Value, num2 > 0.2f); } } private void Lunge() { vanishing = true; PlayRandomSound(1f); target.JumpToFearLevel(1f, true); if ((Object)(object)HUDManager.Instance != (Object)null) { HUDManager.Instance.ShakeCamera((ScreenShakeType)1); } Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(); foreach (Renderer val in componentsInChildren) { val.enabled = false; } ScanNodeProperties[] componentsInChildren2 = ((Component)this).GetComponentsInChildren<ScanNodeProperties>(); foreach (ScanNodeProperties val2 in componentsInChildren2) { ((Component)val2).gameObject.SetActive(false); } if ((Object)(object)agent != (Object)null) { ((Behaviour)agent).enabled = false; } Object.Destroy((Object)(object)((Component)this).gameObject, 3f); } private void PlayRandomSound(float volume) { if (!((Object)(object)voice == (Object)null) && sounds != null && sounds.Count != 0) { voice.PlayOneShot(sounds[Random.Range(0, sounds.Count)], volume); } } private bool PlayerCanSee() { //IL_0017: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) Transform transform = ((Component)target.gameplayCamera).transform; Vector3 val = ((Component)this).transform.position + Vector3.up - transform.position; if (Vector3.Angle(transform.forward, val) < 40f) { return HasLineOfSight(); } return false; } private bool HasLineOfSight() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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) return !Physics.Linecast(((Component)target.gameplayCamera).transform.position, ((Component)this).transform.position + Vector3.up, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1); } } [HarmonyPatch(typeof(StartOfRound))] internal static class StartOfRoundPatches { [HarmonyPostfix] [HarmonyPatch("Start")] private static void LobbyStarted() { Affliction.OnLobbyStarted(); } [HarmonyPatch("OnDestroy")] [HarmonyPrefix] private static void LobbyEnded() { Phantoms.OnRoundEnded(); Monsters.DestroyAll(); Affliction.OnLobbyEnded(); } [HarmonyPostfix] [HarmonyPatch("OnShipLandedMiscEvents")] private static void ShipLanded() { Affliction.HostAssignForRound(); } [HarmonyPostfix] [HarmonyPatch("ShipLeave")] private static void ShipLeft() { Phantoms.OnRoundEnded(); Monsters.DestroyAll(); Affliction.OnRoundEnded(); } } [HarmonyPatch(typeof(RoundManager), "DespawnPropsAtEndOfRound")] internal static class DespawnPropsPatch { private static void Prefix() { Phantoms.OnRoundEnded(); } } [HarmonyPatch(typeof(PlayerControllerB))] internal static class PlayerPatches { [HarmonyPrefix] [HarmonyPatch("BeginGrabObject")] private static bool BeginGrabObject(PlayerControllerB __instance) { if (!Phantoms.AnyExist || (Object)(object)__instance != (Object)(object)Affliction.LocalPlayer) { return true; } GrabbableObject val = Phantoms.PhantomUnderCursor(__instance); if ((Object)(object)val == (Object)null) { return true; } Phantoms.BeginGrab(__instance, val); return false; } [HarmonyPrefix] [HarmonyPatch("DiscardHeldObject")] private static bool DiscardHeldObject(PlayerControllerB __instance) { if (!((NetworkBehaviour)__instance).IsOwner || !Phantoms.IsPhantom(__instance.currentlyHeldObjectServer)) { return true; } if (!__instance.isGrabbingObjectAnimation) { Phantoms.Drop(__instance, __instance.currentlyHeldObjectServer); } return false; } [HarmonyPrefix] [HarmonyPatch("DropAllHeldItems")] private static void DropAllHeldItems(PlayerControllerB __instance) { if (Phantoms.AnyExist && (Object)(object)__instance == (Object)(object)Affliction.LocalPlayer) { Phantoms.VanishFromInventory(__instance); } } [HarmonyPatch("Update")] [HarmonyPostfix] private static void Update(PlayerControllerB __instance) { if (((NetworkBehaviour)__instance).IsOwner && Affliction.LocalPlayerIsAfflicted && (Object)(object)__instance == (Object)(object)Affliction.LocalPlayer) { Monsters.Tick(__instance); } if (Phantoms.AnyExist && (!__instance.isInsideFactory || __instance.isPlayerDead) && (Object)(object)__instance == (Object)(object)Affliction.LocalPlayer && Phantoms.HasPhantomInInventory(__instance)) { Phantoms.VanishFromInventory(__instance); } } [HarmonyPatch("FirstEmptyItemSlot")] [HarmonyPrefix] private static bool FirstEmptyItemSlot(PlayerControllerB __instance, GrabbableObject attemptingGrab, ref int __result) { int num = Affliction.PhantomSlotMaskOf(__instance); if (num == 0) { return true; } if ((Object)(object)__instance.ItemOnlySlot == (Object)null && (Object)(object)attemptingGrab != (Object)null && !attemptingGrab.itemProperties.isScrap && !attemptingGrab.itemProperties.twoHanded && !attemptingGrab.itemProperties.disallowUtilitySlot) { __result = 50; return false; } int currentItemSlot = __instance.currentItemSlot; if (currentItemSlot != 50 && currentItemSlot < __instance.ItemSlots.Length && IsFree(__instance, currentItemSlot, num)) { __result = currentItemSlot; return false; } __result = -1; for (int i = 0; i < __instance.ItemSlots.Length; i++) { if (IsFree(__instance, i, num)) { __result = i; break; } } return false; } private static bool IsFree(PlayerControllerB player, int slot, int phantomSlots) { if ((Object)(object)player.ItemSlots[slot] == (Object)null) { if (slot < 32) { return (phantomSlots & (1 << slot)) == 0; } return true; } return false; } } [HarmonyPatch(typeof(GrabbableObject))] internal static class GrabbableObjectPatches { [HarmonyPatch("Start")] [HarmonyPostfix] private static void Start(GrabbableObject __instance) { if (Phantoms.IsPhantom(__instance)) { HoarderBugAI.grabbableObjectsInMap.Remove(((Component)__instance).gameObject); } } [HarmonyPrefix] [HarmonyPatch("UseItemOnClient")] private static bool UseItemOnClient(GrabbableObject __instance) { return !Phantoms.IsPhantom(__instance); } [HarmonyPatch("ItemInteractLeftRightOnClient")] [HarmonyPrefix] private static bool ItemInteractLeftRightOnClient(GrabbableObject __instance) { return !Phantoms.IsPhantom(__instance); } } [HarmonyPatch(typeof(HoarderBugAI), "RefreshGrabbableObjectsInMapList")] internal static class HoarderBugRefreshPatch { private static void Postfix() { HoarderBugAI.grabbableObjectsInMap.RemoveAll((GameObject item) => (Object)(object)item != (Object)null && (Object)(object)item.GetComponent<PhantomItem>() != (Object)null); } } internal class PhantomItem : MonoBehaviour { } internal static class Phantoms { private static readonly List<GrabbableObject> spawned = new List<GrabbableObject>(); private static int spawnedForSeed = int.MinValue; private static readonly FieldInfo interactableObjectsMask = AccessTools.Field(typeof(PlayerControllerB), "interactableObjectsMask"); private static readonly FieldInfo throwingObject = AccessTools.Field(typeof(PlayerControllerB), "throwingObject"); private static readonly MethodInfo firstEmptyItemSlot = AccessTools.Method(typeof(PlayerControllerB), "FirstEmptyItemSlot", (Type[])null, (Type[])null); private static readonly MethodInfo switchToItemSlot = AccessTools.Method(typeof(PlayerControllerB), "SwitchToItemSlot", (Type[])null, (Type[])null); private static readonly MethodInfo switchToSlotServerRpc = AccessTools.Method(typeof(PlayerControllerB), "SwitchToSlotServerRpc", (Type[])null, (Type[])null); private static readonly MethodInfo setSpecialGrabAnimationBool = AccessTools.Method(typeof(PlayerControllerB), "SetSpecialGrabAnimationBool", (Type[])null, (Type[])null); internal static bool AnyExist => spawned.Count > 0; internal static bool IsPhantom(GrabbableObject item) { if ((Object)(object)item != (Object)null) { return (Object)(object)((Component)item).GetComponent<PhantomItem>() != (Object)null; } return false; } internal static void SpawnForRound() { //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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_0130: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)RoundManager.Instance == (Object)null || (Object)(object)instance.currentLevel == (Object)null || !instance.currentLevel.spawnEnemiesAndScrap || spawnedForSeed == instance.randomMapSeed) { return; } DestroyAll(); spawnedForSeed = instance.randomMapSeed; List<SpawnableItemWithRarity> list = new List<SpawnableItemWithRarity>(); int num = 0; foreach (SpawnableItemWithRarity item in instance.currentLevel.spawnableScrap) { if (item != null && item.rarity > 0 && IsPlainScrap(item.spawnableItem)) { list.Add(item); num += item.rarity; } } List<Vector3> list2 = PickSpawnPositions(Plugin.PhantomCount.Value); if (list.Count == 0 || list2.Count == 0) { Plugin.Log.LogWarning((object)"No usable scrap types or spawn points on this moon, so no phantoms this round."); return; } foreach (Vector3 item2 in list2) { Item val = PickWeighted(list, num); GameObject val2 = Object.Instantiate<GameObject>(val.spawnPrefab, item2, Quaternion.Euler(0f, Random.Range(0f, 360f), 0f)); NetworkObject component = val2.GetComponent<NetworkObject>(); if ((Object)(object)component != (Object)null) { component.AutoObjectParentSync = false; } val2.transform.SetParent(instance.propsContainer, true); val2.AddComponent<PhantomItem>(); GrabbableObject component2 = val2.GetComponent<GrabbableObject>(); component2.isInFactory = true; component2.grabbableToEnemies = false; component2.hasBeenHeld = true; float num2 = (float)Random.Range(val.minValue, val.maxValue) * RoundManager.Instance.scrapValueMultiplier; component2.SetScrapValue(Mathf.Max(1, (int)num2)); spawned.Add(component2); } } private static bool IsPlainScrap(Item item) { if ((Object)(object)item == (Object)null || !item.isScrap || (Object)(object)item.spawnPrefab == (Object)null) { return false; } GrabbableObject component = item.spawnPrefab.GetComponent<GrabbableObject>(); if ((Object)(object)component != (Object)null) { return ((object)component).GetType() == typeof(PhysicsProp); } return false; } private static Item PickWeighted(List<SpawnableItemWithRarity> pool, int totalRarity) { int num = Random.Range(0, totalRarity); foreach (SpawnableItemWithRarity item in pool) { num -= item.rarity; if (num < 0) { return item.spawnableItem; } } return pool[pool.Count - 1].spawnableItem; } private static List<Vector3> PickSpawnPositions(int count) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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_0080: 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_008f: 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) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0179: 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) List<Vector3> list = new List<Vector3>(); GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>(); foreach (GrabbableObject val in array) { list.Add(((Component)val).transform.position); } List<Vector3> list2 = new List<Vector3>(); RandomScrapSpawn[] array2 = Object.FindObjectsOfType<RandomScrapSpawn>(); NavMeshHit val4 = default(NavMeshHit); foreach (RandomScrapSpawn val2 in array2) { if (val2.spawnUsed || IsNearAny(((Component)val2).transform.position, list, 1.5f)) { continue; } Vector3 position = ((Component)val2).transform.position; if (val2.itemSpawnRange > 0f) { Vector2 val3 = Random.insideUnitCircle * val2.itemSpawnRange; if (NavMesh.SamplePosition(position + new Vector3(val3.x, 0f, val3.y), ref val4, val2.itemSpawnRange, -1)) { position = ((NavMeshHit)(ref val4)).position; } } list2.Add(position); } if (list2.Count < count && RoundManager.Instance.insideAINodes != null) { GameObject[] insideAINodes = RoundManager.Instance.insideAINodes; foreach (GameObject val5 in insideAINodes) { if ((Object)(object)val5 != (Object)null) { list2.Add(val5.transform.position); } } } List<Vector3> list3 = new List<Vector3>(); while (list3.Count < count && list2.Count > 0) { int index = Random.Range(0, list2.Count); list3.Add(list2[index] + Vector3.up * 0.5f); list2.RemoveAt(index); } return list3; } private static bool IsNearAny(Vector3 position, List<Vector3> others, float distance) { //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_0011: 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_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) foreach (Vector3 other in others) { Vector3 val = other - position; if (((Vector3)(ref val)).sqrMagnitude < distance * distance) { return true; } } return false; } internal static GrabbableObject PhantomUnderCursor(PlayerControllerB player) { //IL_000d: 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_0027: Unknown result type (might be due to invalid IL or missing references) Ray val = default(Ray); ((Ray)(ref val))..ctor(((Component)player.gameplayCamera).transform.position, ((Component)player.gameplayCamera).transform.forward); RaycastHit val2 = default(RaycastHit); if (!Physics.Raycast(val, ref val2, player.grabDistance, (int)interactableObjectsMask.GetValue(player))) { return null; } if (((Component)((RaycastHit)(ref val2)).collider).gameObject.layer == 8 || !((Component)((RaycastHit)(ref val2)).collider).CompareTag("PhysicsProp")) { return null; } GrabbableObject component = ((Component)((Component)((RaycastHit)(ref val2)).collider).transform).gameObject.GetComponent<GrabbableObject>(); if (!IsPhantom(component)) { return null; } return component; } internal static void BeginGrab(PlayerControllerB player, GrabbableObject item) { if (!player.twoHanded && !(player.sinkingValue > 0.73f) && !player.inSpecialInteractAnimation && !item.isHeld && !item.isPocketed) { int num = (int)firstEmptyItemSlot.Invoke(player, new object[1] { item }); if (num != -1 && num != 50) { player.playerBodyAnimator.SetBool("GrabInvalidated", false); player.playerBodyAnimator.SetBool("GrabValidated", false); player.playerBodyAnimator.SetBool("cancelHolding", false); player.playerBodyAnimator.ResetTrigger("Throw"); setSpecialGrabAnimationBool.Invoke(player, new object[2] { true, null }); player.isGrabbingObjectAnimation = true; ((Behaviour)player.cursorIcon).enabled = false; ((TMP_Text)player.cursorTip).text = ""; player.twoHanded = item.itemProperties.twoHanded; player.carryWeight = Mathf.Clamp(player.carryWeight + (item.itemProperties.weight - 1f), 1f, 10f); StartOfRound.Instance.SendChangedWeightEvent(); player.grabObjectAnimationTime = ((item.itemProperties.grabAnimationTime > 0f) ? item.itemProperties.grabAnimationTime : 0.4f); ((MonoBehaviour)player).StartCoroutine(GrabRoutine(player, item, num)); } } } private static IEnumerator GrabRoutine(PlayerControllerB player, GrabbableObject item, int slot) { yield return (object)new WaitForSeconds(0.1f); if ((Object)(object)item == (Object)null || player.isPlayerDead || !player.isInsideFactory) { player.isGrabbingObjectAnimation = false; player.twoHanded = false; if ((Object)(object)item != (Object)null) { player.carryWeight = Mathf.Clamp(player.carryWeight - (item.itemProperties.weight - 1f), 1f, 10f); } yield break; } AudioClip[] grabSounds = StartOfRound.Instance.playerGrabSFX; player.itemAudio.PlayOneShot(grabSounds[Random.Range(0, grabSounds.Length)]); item.parentObject = player.localItemHolder; if ((Object)(object)item.itemProperties.grabSFX != (Object)null) { player.itemAudio.PlayOneShot(item.itemProperties.grabSFX, 1f); } int previousSlot = player.currentItemSlot; switchToItemSlot.Invoke(player, new object[2] { slot, item }); item.EnablePhysics(false); item.isHeld = true; item.hasHitGround = false; item.isInFactory = player.isInsideFactory; player.twoHanded = item.itemProperties.twoHanded; player.twoHandedAnimation = item.itemProperties.twoHandedAnimation; if (slot != previousSlot) { switchToSlotServerRpc.Invoke(player, new object[1] { slot }); } Affliction.BroadcastLocalPhantomSlots(SlotMask(player)); HUDManager.Instance.ClearControlTips(); item.SetControlTipsForItem(); player.isHoldingObject = true; yield return (object)new WaitForSeconds(Mathf.Max(0f, player.grabObjectAnimationTime - 0.2f)); player.playerBodyAnimator.SetBool("GrabValidated", true); player.isGrabbingObjectAnimation = false; if (player.queueDiscardObject) { player.queueDiscardObject = false; if ((Object)(object)player.currentlyHeldObjectServer == (Object)(object)item) { Drop(player, item); } } } internal static void Drop(PlayerControllerB player, GrabbableObject item) { //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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008d: 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) setSpecialGrabAnimationBool.Invoke(player, new object[2] { false, item }); player.playerBodyAnimator.SetBool("cancelHolding", true); player.playerBodyAnimator.SetTrigger("Throw"); ((Behaviour)HUDManager.Instance.itemSlotIcons[player.currentItemSlot]).enabled = false; ((Behaviour)HUDManager.Instance.holdingTwoHandedItem).enabled = false; Vector3 itemFloorPosition = item.GetItemFloorPosition(default(Vector3)); Vector3 val = StartOfRound.Instance.propsContainer.InverseTransformPoint(itemFloorPosition); player.SetObjectAsNoLongerHeld(false, false, val, item, (int)((Component)player).transform.localEulerAngles.y); item.playerHeldBy = null; player.currentlyHeldObjectServer = null; throwingObject.SetValue(player, false); HUDManager.Instance.ClearControlTips(); Affliction.BroadcastLocalPhantomSlots(SlotMask(player)); } internal static void VanishFromInventory(PlayerControllerB player) { bool flag = false; for (int i = 0; i < player.ItemSlots.Length; i++) { GrabbableObject val = player.ItemSlots[i]; if (IsPhantom(val)) { if ((Object)(object)player.currentlyHeldObjectServer == (Object)(object)val) { setSpecialGrabAnimationBool.Invoke(player, new object[2] { false, val }); player.isHoldingObject = false; player.twoHanded = false; player.twoHandedAnimation = false; player.activatingItem = false; player.playerBodyAnimator.SetBool("cancelHolding", true); player.playerBodyAnimator.SetTrigger("Throw"); ((Behaviour)HUDManager.Instance.holdingTwoHandedItem).enabled = false; HUDManager.Instance.ClearControlTips(); player.currentlyHeldObjectServer = null; } player.carryWeight = Mathf.Clamp(player.carryWeight - (val.itemProperties.weight - 1f), 1f, 10f); ((Behaviour)HUDManager.Instance.itemSlotIcons[i]).enabled = false; player.ItemSlots[i] = null; spawned.Remove(val); Object.DestroyImmediate((Object)(object)((Component)val).gameObject); flag = true; } } if (flag) { StartOfRound.Instance.SendChangedWeightEvent(); Affliction.BroadcastLocalPhantomSlots(0); } } internal static bool HasPhantomInInventory(PlayerControllerB player) { return SlotMask(player) != 0; } private static int SlotMask(PlayerControllerB player) { int num = 0; for (int i = 0; i < player.ItemSlots.Length && i < 32; i++) { if (IsPhantom(player.ItemSlots[i])) { num |= 1 << i; } } return num; } internal static void DestroyAll() { PlayerControllerB localPlayer = Affliction.LocalPlayer; if ((Object)(object)localPlayer != (Object)null) { VanishFromInventory(localPlayer); } foreach (GrabbableObject item in spawned) { if ((Object)(object)item != (Object)null) { Object.DestroyImmediate((Object)(object)((Component)item).gameObject); } } spawned.Clear(); } internal static void OnRoundEnded() { DestroyAll(); spawnedForSeed = int.MinValue; } } [BepInPlugin("axelc.schizo", "Schizo", "1.1.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "axelc.schizo"; public const string PluginName = "Schizo"; public const string Version = "1.1.0"; internal static ManualLogSource Log; internal static ConfigEntry<int> PhantomCount; internal static ConfigEntry<bool> RerollEachRound; internal static ConfigEntry<bool> TellAfflictedPlayer; internal static ConfigEntry<bool> MonsterHallucinations; internal static ConfigEntry<float> MonsterIntervalMin; internal static ConfigEntry<float> MonsterIntervalMax; internal static ConfigEntry<int> MaxMonstersAtOnce; private void Awake() { //IL_00f8: Unknown result type (might be due to invalid IL or missing references) Log = ((BaseUnityPlugin)this).Logger; PhantomCount = ((BaseUnityPlugin)this).Config.Bind<int>("Hallucinations", "PhantomItemCount", 5, "How many fake scrap items the afflicted player sees in the facility each round."); RerollEachRound = ((BaseUnityPlugin)this).Config.Bind<bool>("Affliction", "RerollEachRound", false, "Off: one player is picked the first time the ship lands and keeps it for the whole lobby. On: a new player is picked every landing. Only the host's setting matters."); TellAfflictedPlayer = ((BaseUnityPlugin)this).Config.Bind<bool>("Affliction", "TellAfflictedPlayer", false, "Show the afflicted player a warning when they get it. Off by default so they don't know."); MonsterHallucinations = ((BaseUnityPlugin)this).Config.Bind<bool>("Monsters", "Enabled", true, "The afflicted player sees monsters in the facility that aren't there. They can't hurt anyone."); MonsterIntervalMin = ((BaseUnityPlugin)this).Config.Bind<float>("Monsters", "MinSecondsBetween", 45f, "Shortest wait between fake monsters while the afflicted player is inside."); MonsterIntervalMax = ((BaseUnityPlugin)this).Config.Bind<float>("Monsters", "MaxSecondsBetween", 120f, "Longest wait between fake monsters while the afflicted player is inside."); MaxMonstersAtOnce = ((BaseUnityPlugin)this).Config.Bind<int>("Monsters", "MaxAtOnce", 1, "Most fake monsters that can be around the afflicted player at the same time."); new Harmony("axelc.schizo").PatchAll(); Log.LogInfo((object)"Schizo 1.1.0 loaded."); } }