Please disclose if your mod was created primarily 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 SpewOnDemand v1.0.3
OnDemandSpew.dll
Decompiled 7 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Microsoft.CodeAnalysis; using Photon.Pun; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("Igor_Does_Nothing")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("OnDemandSpew")] [assembly: AssemblyTitle("OnDemandSpew")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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; } } } [BepInPlugin("com.igor.repo.ondemandspew", "On-Demand Spew", "1.0.0")] public class OnDemandSpew : BaseUnityPlugin { public const string PluginGuid = "com.igor.repo.ondemandspew"; public const string PluginName = "On-Demand Spew"; public const string PluginVersion = "1.0.0"; private ManualLogSource _log; private ConfigEntry<KeyboardShortcut> _spewSelfKey; private ConfigEntry<KeyboardShortcut> _spewAllKey; private ConfigEntry<bool> _onlyInMainGame; private ConfigEntry<bool> _enabled; private ConfigEntry<float> _attachDelay; private ConfigEntry<float> _pukeDelay; private ConfigEntry<bool> _requireMaster; private ConfigEntry<bool> _autoSpawnIfMissing; private ConfigEntry<float> _spawnAheadMeters; private ConfigEntry<bool> _spawnDisabled; private ConfigEntry<bool> _heartbeatEnabled; private ConfigEntry<float> _heartbeatIntervalSec; private ConfigEntry<bool> _invisibleSpewerAttach; private ConfigEntry<float> _invisibleSpewerCleanupSec; private static SpewController _controller; private void Awake() { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) _log = ((BaseUnityPlugin)this).Logger; _enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the mod on/off without removing it."); _onlyInMainGame = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "OnlyInMainGame", true, "If true, hotkeys only work during gameplay (not in menus)."); _requireMaster = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "RequireMasterClient", true, "If true, actions only run when you are master client or in singleplayer (network-safe prank)."); _spewSelfKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "SpewSelf", new KeyboardShortcut((KeyCode)112, Array.Empty<KeyCode>()), "Trigger spew on yourself using a Spewer."); _spewAllKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "SpewAll", new KeyboardShortcut((KeyCode)112, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Attempt to spew all players (host-only; needs enough Spewers)."); _attachDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "AttachDelay", 0.1f, "Seconds to wait after forcing Attack/Attached before puking."); _pukeDelay = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "PukeDelay", 0.75f, "Seconds to keep in Puke before returning to Attached."); _autoSpawnIfMissing = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawn", "AutoSpawnIfMissing", true, "If true and no Spewer exists, host will spawn one near the target (networked)."); _spawnAheadMeters = ((BaseUnityPlugin)this).Config.Bind<float>("Spawn", "SpawnAheadMeters", 4f, "Distance in front of the player to spawn a Spewer when needed."); _spawnDisabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawn", "Disabled", false, "If true, never spawn Spewers (host-only prank disabled; only local visual fallback possible)."); _heartbeatEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "HeartbeatEnabled", true, "If true, writes periodic heartbeat logs from the controller."); _heartbeatIntervalSec = ((BaseUnityPlugin)this).Config.Bind<float>("Debug", "HeartbeatIntervalSeconds", 10f, "Seconds between heartbeat logs."); _invisibleSpewerAttach = ((BaseUnityPlugin)this).Config.Bind<bool>("Network", "InvisibleSpewerAttach", true, "If true (host only), always spawn a new Spewer, hide its body, and drive Attack→Puke over the network."); _invisibleSpewerCleanupSec = ((BaseUnityPlugin)this).Config.Bind<float>("Network", "InvisibleSpewerCleanupSeconds", 0f, "If > 0, auto-destroy the spawned invisible spewer after this many seconds (host only). 0 = no auto-destroy."); _log.LogInfo((object)string.Format("{0} {1} loaded. Self={2}, All={3}, RequireMaster={4}", "On-Demand Spew", "1.0.0", _spewSelfKey.Value, _spewAllKey.Value, _requireMaster.Value)); BuildOrAttachController(); SceneManager.activeSceneChanged += OnActiveSceneChanged; } private void Update() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) if (!_enabled.Value || (_onlyInMainGame.Value && (Object)(object)GameDirector.instance == (Object)null)) { return; } KeyboardShortcut value = _spewSelfKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { _log.LogInfo((object)string.Format("[{0}] SpewSelf hotkey pressed: {1}", "On-Demand Spew", _spewSelfKey.Value)); if ((Object)(object)_controller != (Object)null) { _controller.TriggerSpewSelf(); } } value = _spewAllKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { _log.LogInfo((object)string.Format("[{0}] SpewAll hotkey pressed: {1}", "On-Demand Spew", _spewAllKey.Value)); if ((Object)(object)_controller != (Object)null) { _controller.TriggerSpewAll(); } } } private void OnDestroy() { SceneManager.activeSceneChanged -= OnActiveSceneChanged; ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"OnDemandSpew plugin OnDestroy() called (controller left running if hosted)."); } } private void OnActiveSceneChanged(Scene from, Scene to) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)("[Scene] activeSceneChanged: " + ((Scene)(ref from)).name + " -> " + ((Scene)(ref to)).name)); } BuildOrAttachController(); } private void BuildOrAttachController() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown try { GameObject val = null; val = GameObject.Find("SpewOnDemandHost (BepInEx)"); if ((Object)(object)val == (Object)null) { val = new GameObject("SpewOnDemandHost (BepInEx)"); Object.DontDestroyOnLoad((Object)(object)val); ((Object)val).hideFlags = (HideFlags)61; ManualLogSource log = _log; if (log != null) { log.LogInfo((object)"Created persistent SpewOnDemand host GameObject."); } } _controller = val.GetComponent<SpewController>(); if ((Object)(object)_controller == (Object)null) { _controller = val.AddComponent<SpewController>(); ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)"Attached SpewController to host."); } } _controller.Configure(_log, _enabled, _onlyInMainGame, _spewSelfKey, _spewAllKey, _attachDelay, _pukeDelay, _requireMaster, _autoSpawnIfMissing, _spawnAheadMeters, _spawnDisabled, _heartbeatEnabled, _heartbeatIntervalSec, _invisibleSpewerAttach, _invisibleSpewerCleanupSec); } catch (Exception ex) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogWarning((object)("[BuildOrAttachController] Exception: " + ex.GetType().Name + ": " + ex.Message)); } } } private bool IsMasterOrSingle() { try { return SemiFunc.IsMasterClientOrSingleplayer(); } catch { return PhotonNetwork.OfflineMode || (!PhotonNetwork.IsConnected && !PhotonNetwork.InRoom); } } private void TrySpewSelf() { _log.LogInfo((object)"[OnDemandSpew] TrySpewSelf() start"); PlayerAvatar instance = PlayerAvatar.instance; if ((Object)(object)instance == (Object)null) { _log.LogWarning((object)"No PlayerAvatar.instance found."); return; } _log.LogInfo((object)"[OnDemandSpew] Using local attach+puke route (no network)"); ((MonoBehaviour)this).StartCoroutine(AttachJawAndPukeLocal(instance)); } private void TrySpewAll() { if (_requireMaster.Value && !IsMasterOrSingle()) { _log.LogWarning((object)"SpewAll blocked: not master client."); return; } List<PlayerAvatar> list = SafeGetPlayers(); if (list.Count == 0) { _log.LogWarning((object)"No players found."); return; } if (_spawnDisabled.Value) { _log.LogWarning((object)"Spawn disabled by config; cannot spew-all over network. Skipping."); return; } _log.LogInfo((object)$"Spawning Spewer per player and triggering puke on {list.Count} players."); foreach (PlayerAvatar item in list) { ((MonoBehaviour)this).StartCoroutine(EnsureSpewerThenPuke(item)); } } private IEnumerator EnsureSpewerThenPuke(PlayerAvatar target) { Vector3 position = target.localCameraTransform.position + target.localCameraTransform.forward * Mathf.Max(1f, _spawnAheadMeters.Value); EnemySlowMouth val = DirectInstantiateSpewer(position); if ((Object)(object)val != (Object)null) { yield return ForceAttachAndPuke(val, target); yield break; } if (!SpawnSpewerAt(position)) { _log.LogWarning((object)"Failed to spawn Spewer via EnemySetup; trying direct resource instantiate..."); val = DirectInstantiateSpewer(position); if ((Object)(object)val != (Object)null) { yield return ForceAttachAndPuke(val, target); yield break; } } float wait = 0f; EnemySlowMouth val2 = null; while (wait < 2f && (Object)(object)val2 == (Object)null) { yield return null; wait += Time.deltaTime; val2 = FindClosestSpewer(((Component)target).transform.position); } if ((Object)(object)val2 == (Object)null) { _log.LogWarning((object)"Could not find Spewer after spawn attempt."); TryLocalOnlyPuke(); } else { yield return ForceAttachAndPuke(val2, target); } } private IEnumerator AttachJawAndPukeLocal(PlayerAvatar target) { _log.LogInfo((object)"[OnDemandSpew] AttachJawAndPukeLocal: begin"); GameObject val = Resources.Load<GameObject>("Enemies/Enemy - Slow Mouth"); if ((Object)(object)val == (Object)null) { _log.LogWarning((object)"Resources.Load failed for 'Enemies/Enemy - Slow Mouth'. Cannot spawn jaw."); yield break; } _log.LogInfo((object)"[OnDemandSpew] Loaded Slow Mouth prefab from Resources"); EnemySlowMouth componentInChildren = val.GetComponentInChildren<EnemySlowMouth>(); if ((Object)(object)componentInChildren == (Object)null || (Object)(object)componentInChildren.enemySlowMouthAttack == (Object)null) { _log.LogWarning((object)"EnemySlowMouth prefab or its enemySlowMouthAttack is missing."); yield break; } Vector3 val2 = target.localCameraTransform.position + target.localCameraTransform.forward * Mathf.Max(0.25f, _spawnAheadMeters.Value * 0.25f); Quaternion rotation = target.localCameraTransform.rotation; GameObject val3 = Object.Instantiate<GameObject>(componentInChildren.enemySlowMouthAttack, val2, rotation); _log.LogInfo((object)$"[OnDemandSpew] Instantiated attack prefab at {val2}"); EnemySlowMouthAttaching component = val3.GetComponent<EnemySlowMouthAttaching>(); if ((Object)(object)component == (Object)null) { _log.LogWarning((object)"Spawned attack prefab has no EnemySlowMouthAttaching component."); yield break; } component.targetPlayerAvatar = target; _log.LogInfo((object)"[OnDemandSpew] Calling GoTime() on EnemySlowMouthAttaching"); component.GoTime(); yield return null; yield return (object)new WaitForSeconds(0.05f); EnemySlowMouthCameraVisuals componentInChildren2 = ((Component)target.localCameraTransform).GetComponentInChildren<EnemySlowMouthCameraVisuals>(); if ((Object)(object)componentInChildren2 != (Object)null) { _log.LogInfo((object)"[OnDemandSpew] Found EnemySlowMouthCameraVisuals under local camera"); try { FieldInfo field = typeof(EnemySlowMouthCameraVisuals).GetField("playerTarget", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(componentInChildren2, target); _log.LogInfo((object)"[OnDemandSpew] playerTarget set via reflection"); } else { _log.LogWarning((object)"[OnDemandSpew] Could not reflect playerTarget field"); } } catch { } componentInChildren2.StateSet((State)2); try { DisableJawMeshesKeepParticles(((Component)componentInChildren2).transform); object? obj2 = typeof(EnemySlowMouthCameraVisuals).GetField("pukeCapsuleRenderer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(componentInChildren2); Renderer val4 = (Renderer)((obj2 is Renderer) ? obj2 : null); if ((Object)(object)val4 != (Object)null) { val4.enabled = false; } } catch { } _log.LogInfo((object)"Forced local camera visuals to Puke."); } else { SemiPuke componentInChildren3 = ((Component)target).GetComponentInChildren<SemiPuke>(); if ((Object)(object)componentInChildren3 != (Object)null) { componentInChildren3.PukeActive(target.localCameraTransform.position, target.localCameraTransform.rotation); _log.LogInfo((object)"Triggered SemiPuke directly (fallback)."); } else { _log.LogWarning((object)"No EnemySlowMouthCameraVisuals or SemiPuke found for local attach."); } } _log.LogInfo((object)"[OnDemandSpew] AttachJawAndPukeLocal: end"); } private bool SpawnSpewerAt(Vector3 position) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) try { EnemySetup val = FindSlowMouthSetup(); if ((Object)(object)val == (Object)null) { return false; } LevelGenerator.Instance.EnemySpawn(val, position); _log.LogInfo((object)"Spawned Spewer via EnemySetup."); return true; } catch (Exception ex) { _log.LogWarning((object)("EnemySpawn via setup failed: " + ex.Message)); return false; } } private EnemySetup? FindSlowMouthSetup() { try { List<EnemySetup> list = new List<EnemySetup>(); if (EnemyDirector.instance.enemiesDifficulty1 != null) { list.AddRange(EnemyDirector.instance.enemiesDifficulty1); } if (EnemyDirector.instance.enemiesDifficulty2 != null) { list.AddRange(EnemyDirector.instance.enemiesDifficulty2); } if (EnemyDirector.instance.enemiesDifficulty3 != null) { list.AddRange(EnemyDirector.instance.enemiesDifficulty3); } foreach (EnemySetup item in list) { if ((Object)(object)item == (Object)null || item.spawnObjects == null) { continue; } foreach (GameObject spawnObject in item.spawnObjects) { if ((Object)(object)spawnObject != (Object)null && ((Object)spawnObject).name.IndexOf("Slow Mouth", StringComparison.OrdinalIgnoreCase) >= 0) { return item; } } } } catch { } return null; } private EnemySlowMouth? DirectInstantiateSpewer(Vector3 position) { //IL_0005: 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_0024: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = PhotonNetwork.InstantiateRoomObject("Enemies/Enemy - Slow Mouth", position, Quaternion.identity, (byte)0, (object[])null); Enemy componentInChildren = val.GetComponentInChildren<Enemy>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.EnemyTeleported(position); } _log.LogInfo((object)"Direct-instantiated Spewer prefab."); return val.GetComponentInChildren<EnemySlowMouth>(); } catch (Exception ex) { _log.LogWarning((object)("Direct instantiate failed: " + ex.Message)); return null; } } private EnemySlowMouth? FindClosestSpewer(Vector3 pos) { //IL_001e: 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_0024: Unknown result type (might be due to invalid IL or missing references) EnemySlowMouth result = null; float num = float.MaxValue; EnemySlowMouth[] array = Object.FindObjectsOfType<EnemySlowMouth>(); foreach (EnemySlowMouth val in array) { float num2 = Vector3.SqrMagnitude(((Component)val).transform.position - pos); if (num2 < num) { num = num2; result = val; } } return result; } private List<PlayerAvatar> SafeGetPlayers() { try { return new List<PlayerAvatar>(SemiFunc.PlayerGetList()); } catch { PlayerAvatar instance = PlayerAvatar.instance; return ((Object)(object)instance != (Object)null) ? new List<PlayerAvatar> { instance } : new List<PlayerAvatar>(); } } private void DisableJawMeshesKeepParticles(Transform root) { if ((Object)(object)root == (Object)null) { return; } Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { if (!(val is ParticleSystemRenderer) && val.enabled) { val.enabled = false; } } } private IEnumerator ForceAttachAndPuke(EnemySlowMouth spewer, PlayerAvatar target) { if (!((Object)(object)spewer == (Object)null) && !((Object)(object)target == (Object)null)) { PhotonView component = ((Component)spewer).GetComponent<PhotonView>(); if ((Object)(object)component == (Object)null) { _log.LogWarning((object)"Selected spewer has no PhotonView; aborting."); yield break; } component.RPC("UpdatePlayerTargetRPC", (RpcTarget)0, new object[1] { target.photonView.ViewID }); spewer.UpdateState((State)8); yield return (object)new WaitForSeconds(Mathf.Max(0.02f, _attachDelay.Value)); spewer.UpdateState((State)9); yield return (object)new WaitForSeconds(0.05f); spewer.UpdateState((State)10); yield return (object)new WaitForSeconds(Mathf.Max(0.05f, _pukeDelay.Value)); spewer.UpdateState((State)9); _log.LogInfo((object)("Spewed: " + ((Object)target).name + " using " + ((Object)spewer).name)); } } private void TryLocalOnlyPuke() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) try { EnemySlowMouthCameraVisuals[] array = Object.FindObjectsOfType<EnemySlowMouthCameraVisuals>(); EnemySlowMouthCameraVisuals[] array2 = array; foreach (EnemySlowMouthCameraVisuals val in array2) { if ((Object)(object)val.semiPuke != (Object)null && (Object)(object)PlayerAvatar.instance != (Object)null) { val.semiPuke.PukeActive(((Component)val.semiPuke).transform.position, PlayerAvatar.instance.localCameraTransform.rotation); _log.LogInfo((object)"Triggered local camera puke visuals."); return; } } } catch { } _log.LogInfo((object)"Local-only puke not available (no camera jaw/semiPuke present)."); } } public class SpewController : MonoBehaviour { private ManualLogSource _log; private ConfigEntry<KeyboardShortcut> _spewSelfKey; private ConfigEntry<KeyboardShortcut> _spewAllKey; private ConfigEntry<bool> _onlyInMainGame; private ConfigEntry<bool> _enabled; private ConfigEntry<bool> _requireMaster; private ConfigEntry<float> _attachDelay; private ConfigEntry<float> _pukeDelay; private ConfigEntry<float> _spawnAheadMeters; private ConfigEntry<bool> _autoSpawnIfMissing; private ConfigEntry<bool> _spawnDisabled; private ConfigEntry<bool> _heartbeatEnabled; private ConfigEntry<float> _heartbeatIntervalSec; private float _nextHeartbeatAt; private ConfigEntry<bool> _invisibleSpewerAttach; private ConfigEntry<float> _invisibleSpewerCleanupSec; private float _nextKeyAllowedAt; public void Configure(ManualLogSource log, ConfigEntry<bool> enabled, ConfigEntry<bool> onlyMain, ConfigEntry<KeyboardShortcut> spewSelf, ConfigEntry<KeyboardShortcut> spewAll, ConfigEntry<float> attachDelay, ConfigEntry<float> pukeDelay, ConfigEntry<bool> requireMaster, ConfigEntry<bool> autoSpawn, ConfigEntry<float> spawnAhead, ConfigEntry<bool> spawnDisabled, ConfigEntry<bool> heartbeat, ConfigEntry<float> heartbeatInterval, ConfigEntry<bool> invisibleSpewerAttach, ConfigEntry<float> invisibleSpewerCleanupSec) { _log = log; _enabled = enabled; _onlyInMainGame = onlyMain; _spewSelfKey = spewSelf; _spewAllKey = spewAll; _attachDelay = attachDelay; _pukeDelay = pukeDelay; _requireMaster = requireMaster; _autoSpawnIfMissing = autoSpawn; _spawnAheadMeters = spawnAhead; _spawnDisabled = spawnDisabled; _heartbeatEnabled = heartbeat; _heartbeatIntervalSec = heartbeatInterval; _invisibleSpewerAttach = invisibleSpewerAttach; _invisibleSpewerCleanupSec = invisibleSpewerCleanupSec; _nextHeartbeatAt = Time.unscaledTime + Mathf.Max(1f, _heartbeatIntervalSec?.Value ?? 10f); ((MonoBehaviour)this).CancelInvoke("RealtimeTick"); ((MonoBehaviour)this).InvokeRepeating("RealtimeTick", 1f, 1f); ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)"SpewController configured and alive."); } } public void TriggerSpewSelf() { try { ((MonoBehaviour)this).StartCoroutine(SpewHostOrLocal(PlayerAvatar.instance)); } catch { } } public void TriggerSpewAll() { try { foreach (PlayerAvatar item in SafeGetPlayers()) { ((MonoBehaviour)this).StartCoroutine(SpewHostOrLocal(item)); } } catch { } } private void Update() { //IL_0054: 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) try { if (_enabled != null && !_enabled.Value) { return; } bool flag = (Object)(object)GameDirector.instance != (Object)null; bool flag2 = _onlyInMainGame != null && _onlyInMainGame.Value && !flag; KeyboardShortcut value; if (_spewSelfKey != null) { value = _spewSelfKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { if (flag2) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)"[Controller] Self hotkey ignored (OnlyInMainGame=true, not in gameplay)."); } } else if (Time.unscaledTime >= _nextKeyAllowedAt) { _nextKeyAllowedAt = Time.unscaledTime + 0.25f; ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)$"[Controller] Self hotkey pressed: {_spewSelfKey.Value}"); } ((MonoBehaviour)this).StartCoroutine(SpewHostOrLocal(PlayerAvatar.instance)); } } } if (_spewAllKey != null) { value = _spewAllKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { if (flag2) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogInfo((object)"[Controller] All hotkey ignored (OnlyInMainGame=true, not in gameplay)."); } } else if (Time.unscaledTime >= _nextKeyAllowedAt) { _nextKeyAllowedAt = Time.unscaledTime + 0.25f; ManualLogSource log4 = _log; if (log4 != null) { log4.LogInfo((object)$"[Controller] All hotkey pressed: {_spewAllKey.Value}"); } foreach (PlayerAvatar item in SafeGetPlayers()) { ((MonoBehaviour)this).StartCoroutine(SpewHostOrLocal(item)); } } } } if (_heartbeatEnabled != null && _heartbeatEnabled.Value && Time.unscaledTime >= _nextHeartbeatAt) { _nextHeartbeatAt = Time.unscaledTime + Mathf.Max(1f, _heartbeatIntervalSec?.Value ?? 10f); ManualLogSource log5 = _log; if (log5 != null) { log5.LogInfo((object)"[Controller] Heartbeat alive"); } } } catch (Exception ex) { ManualLogSource log6 = _log; if (log6 != null) { log6.LogWarning((object)("[Controller.Update] " + ex.GetType().Name + ": " + ex.Message)); } } } private void RealtimeTick() { try { if (_heartbeatEnabled != null && _heartbeatEnabled.Value) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)"[Controller.Tick] alive"); } } } catch { } } private List<PlayerAvatar> SafeGetPlayers() { try { return new List<PlayerAvatar>(SemiFunc.PlayerGetList()); } catch { PlayerAvatar instance = PlayerAvatar.instance; return ((Object)(object)instance != (Object)null) ? new List<PlayerAvatar> { instance } : new List<PlayerAvatar>(); } } private IEnumerator AttachJawAndPukeLocal(PlayerAvatar target) { if ((Object)(object)target == (Object)null) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"[Controller] No PlayerAvatar for local puke."); } yield break; } ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)"[Controller] AttachJawAndPukeLocal: begin"); } EnemySlowMouthCameraVisuals componentInChildren = ((Component)target.localCameraTransform).GetComponentInChildren<EnemySlowMouthCameraVisuals>(); if ((Object)(object)componentInChildren != (Object)null) { try { FieldInfo field = typeof(EnemySlowMouthCameraVisuals).GetField("playerTarget", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(componentInChildren, target); } } catch { } componentInChildren.StateSet((State)2); ManualLogSource log3 = _log; if (log3 != null) { log3.LogInfo((object)"[Controller] Forced camera visuals to Puke."); } } else { SemiPuke componentInChildren2 = ((Component)target).GetComponentInChildren<SemiPuke>(true); if ((Object)(object)componentInChildren2 != (Object)null) { componentInChildren2.PukeActive(target.localCameraTransform.position, target.localCameraTransform.rotation); ManualLogSource log4 = _log; if (log4 != null) { log4.LogInfo((object)"[Controller] Triggered SemiPuke directly (existing)."); } } else if (!SpawnStandaloneSemiPuke(target)) { ManualLogSource log5 = _log; if (log5 != null) { log5.LogWarning((object)"[Controller] No camera visuals and failed to spawn standalone SemiPuke."); } } } ManualLogSource log6 = _log; if (log6 != null) { log6.LogInfo((object)"[Controller] AttachJawAndPukeLocal: end"); } } private bool SpawnStandaloneSemiPuke(PlayerAvatar target) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = Resources.Load<GameObject>("Enemies/Enemy - Slow Mouth"); if ((Object)(object)val == (Object)null) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"[Controller] Resources.Load failed for 'Enemies/Enemy - Slow Mouth' (for standalone SemiPuke)."); } return false; } SemiPuke componentInChildren = val.GetComponentInChildren<SemiPuke>(true); if ((Object)(object)componentInChildren == (Object)null) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)"[Controller] Could not find SemiPuke inside Slow Mouth prefab."); } return false; } Transform localCameraTransform = target.localCameraTransform; Vector3 val2 = localCameraTransform.position + localCameraTransform.forward * Mathf.Max(0.25f, (_spawnAheadMeters?.Value ?? 4f) * 0.15f); GameObject val3 = Object.Instantiate<GameObject>(((Component)componentInChildren).gameObject, val2, localCameraTransform.rotation, localCameraTransform); SemiPuke component = val3.GetComponent<SemiPuke>(); if ((Object)(object)component == (Object)null) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogWarning((object)"[Controller] SemiPuke clone missing component."); } Object.Destroy((Object)(object)val3); return false; } component.PukeActive(val2, localCameraTransform.rotation); ManualLogSource log4 = _log; if (log4 != null) { log4.LogInfo((object)"[Controller] Spawned standalone SemiPuke and triggered puke."); } ((MonoBehaviour)this).StartCoroutine(DestroyAfter(val3, 2f)); return true; } catch (Exception ex) { ManualLogSource log5 = _log; if (log5 != null) { log5.LogWarning((object)("[Controller] SpawnStandaloneSemiPuke exception: " + ex.GetType().Name + ": " + ex.Message)); } return false; } } private IEnumerator DestroyAfter(GameObject go, float seconds) { yield return (object)new WaitForSeconds(Mathf.Max(0.05f, seconds)); if ((Object)(object)go != (Object)null) { Object.Destroy((Object)(object)go); } } private IEnumerator SpewHostOrLocal(PlayerAvatar target) { if ((Object)(object)target == (Object)null) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"[Controller] Target player is null."); } yield break; } ((MonoBehaviour)this).StartCoroutine(KnockdownInvulnAllPlayers(0.5f)); bool flag; try { flag = SemiFunc.IsMasterClientOrSingleplayer(); } catch { flag = PhotonNetwork.OfflineMode || PhotonNetwork.IsMasterClient; } if (flag && (_spawnDisabled == null || !_spawnDisabled.Value)) { if (_invisibleSpewerAttach != null && _invisibleSpewerAttach.Value) { yield return NetInvisibleAttachAndPuke(target); } else { yield return NetSpawnAndPuke(target); } yield break; } ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)"[Controller] Falling back to local-only puke."); } yield return AttachJawAndPukeLocal(target); } private IEnumerator KnockdownInvulnAllPlayers(float seconds) { float end = Time.unscaledTime + Mathf.Max(0.05f, seconds); while (Time.unscaledTime < end) { List<PlayerAvatar> list; try { list = new List<PlayerAvatar>(SemiFunc.PlayerGetList()); } catch { PlayerAvatar instance = PlayerAvatar.instance; list = (((Object)(object)instance != (Object)null) ? new List<PlayerAvatar> { instance } : new List<PlayerAvatar>()); } foreach (PlayerAvatar item in list) { if ((Object)(object)item == (Object)null) { continue; } try { PlayerTumble tumble = item.tumble; if ((Object)(object)tumble != (Object)null) { MethodInfo method = ((object)tumble).GetType().GetMethod("TumbleRequest", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null && method.GetParameters().Length == 2) { method.Invoke(tumble, new object[2] { false, false }); } } try { Type type = ((object)item).GetType(); PropertyInfo property = type.GetProperty("isDisabled", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { property.SetValue(item, false); } FieldInfo field = type.GetField("isDisabled", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(item, false); } } catch { } } catch { } } yield return null; } } private IEnumerator NetSpawnAndPuke(PlayerAvatar target) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)"[Controller] NetSpawnAndPuke: begin"); } EnemySlowMouth spewer = null; Vector3 val = target.localCameraTransform.position + target.localCameraTransform.forward * Mathf.Max(1f, _spawnAheadMeters?.Value ?? 4f); GameObject val2 = null; try { val2 = PhotonNetwork.InstantiateRoomObject("Enemies/Enemy - Slow Mouth", val, target.localCameraTransform.rotation, (byte)0, (object[])null); } catch (Exception ex) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("[Controller] Photon instantiate failed: " + ex.Message)); } } if ((Object)(object)val2 != (Object)null) { spewer = val2.GetComponentInChildren<EnemySlowMouth>(); } if ((Object)(object)spewer == (Object)null) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogWarning((object)"[Controller] Could not get a Spewer. Aborting network puke."); } yield break; } PhotonView component = ((Component)spewer).GetComponent<PhotonView>(); if ((Object)(object)component == (Object)null) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogWarning((object)"[Controller] Spewer has no PhotonView."); } yield break; } component.RPC("UpdatePlayerTargetRPC", (RpcTarget)0, new object[1] { target.photonView.ViewID }); yield return null; spewer.UpdateState((State)8); yield return (object)new WaitForSeconds(Mathf.Max(0.02f, _attachDelay?.Value ?? 0.1f)); spewer.UpdateState((State)9); yield return (object)new WaitForSeconds(0.05f); spewer.UpdateState((State)10); yield return (object)new WaitForSeconds(Mathf.Max(0.05f, _pukeDelay?.Value ?? 0.75f)); spewer.UpdateState((State)9); ManualLogSource log5 = _log; if (log5 != null) { log5.LogInfo((object)"[Controller] NetSpawnAndPuke: done"); } } private IEnumerator NetInvisibleAttachAndPuke(PlayerAvatar target) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)"[Controller] NetInvisibleAttachAndPuke: begin"); } if ((Object)(object)target == (Object)null) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)"[Controller] Target null."); } yield break; } if (!PhotonNetwork.OfflineMode && !PhotonNetwork.IsMasterClient) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogWarning((object)"[Controller] Not MasterClient; cannot run invisible spewer attach."); } yield break; } Vector3 val = target.localCameraTransform.position + target.localCameraTransform.forward * Mathf.Max(1f, _spawnAheadMeters?.Value ?? 4f); Quaternion rotation = target.localCameraTransform.rotation; GameObject go = null; try { go = PhotonNetwork.InstantiateRoomObject("Enemies/Enemy - Slow Mouth", val, rotation, (byte)0, (object[])null); } catch (Exception ex) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogWarning((object)("[Controller] Photon instantiate failed: " + ex.Message)); } } if ((Object)(object)go == (Object)null) { ManualLogSource log5 = _log; if (log5 != null) { log5.LogWarning((object)"[Controller] Could not spawn Spewer (Photon). Aborting."); } yield break; } EnemySlowMouth spewer = go.GetComponentInChildren<EnemySlowMouth>(); if ((Object)(object)spewer == (Object)null) { ManualLogSource log6 = _log; if (log6 != null) { log6.LogWarning((object)"[Controller] Spawned object missing EnemySlowMouth component."); } yield break; } int num = 0; try { GameObject val2 = (((Object)(object)spewer.enemyVisuals != (Object)null) ? ((Component)spewer.enemyVisuals).gameObject : ((Component)spewer).gameObject); Renderer[] componentsInChildren = val2.GetComponentsInChildren<Renderer>(true); foreach (Renderer val3 in componentsInChildren) { val3.enabled = false; num++; } ManualLogSource log7 = _log; if (log7 != null) { log7.LogInfo((object)$"[Controller] Hidden {num} renderers for invisible Spewer."); } } catch (Exception ex2) { ManualLogSource log8 = _log; if (log8 != null) { log8.LogWarning((object)("[Controller] Hiding renderers failed: " + ex2.Message)); } } PhotonView component = ((Component)spewer).GetComponent<PhotonView>(); if ((Object)(object)component == (Object)null) { ManualLogSource log9 = _log; if (log9 != null) { log9.LogWarning((object)"[Controller] Spewer has no PhotonView."); } yield break; } component.RPC("UpdatePlayerTargetRPC", (RpcTarget)0, new object[1] { target.photonView.ViewID }); ManualLogSource log10 = _log; if (log10 != null) { log10.LogInfo((object)"[Controller] UpdatePlayerTargetRPC sent."); } yield return null; ((MonoBehaviour)this).StartCoroutine(HideAttackVisualsForWindow(target, spewer, 5f)); ((MonoBehaviour)this).StartCoroutine(HideJawMeshesForWindow(target, 5f)); ((MonoBehaviour)this).StartCoroutine(SuppressTumbleForWindow(target, 3f)); spewer.UpdateState((State)8); ManualLogSource log11 = _log; if (log11 != null) { log11.LogInfo((object)"[Controller] Spewer state -> Attack"); } float num2 = Mathf.Max(0.2f, _attachDelay?.Value ?? 0.1f); yield return WaitForAttachOverlayOrTimeout(target, Mathf.Max(1.25f, num2 + 0.5f)); yield return ((MonoBehaviour)this).StartCoroutine(HideAttackVisualsForWindow(target, spewer, 2f)); spewer.UpdateState((State)10); ManualLogSource log12 = _log; if (log12 != null) { log12.LogInfo((object)"[Controller] Spewer state -> Puke"); } float num3 = Mathf.Max(1f, _pukeDelay?.Value ?? 0.75f); float pukeHoldEnd = Time.unscaledTime + num3; ((MonoBehaviour)this).StartCoroutine(HideJawMeshesForWindow(target, 3f)); while (Time.unscaledTime < pukeHoldEnd) { yield return null; } spewer.UpdateState((State)6); ManualLogSource log13 = _log; if (log13 != null) { log13.LogInfo((object)"[Controller] Spewer state -> Leave (no knockdown)"); } yield return (object)new WaitForSeconds(2f); spewer.UpdateState((State)2); yield return (object)new WaitForSeconds(0.5f); float num4 = ((_invisibleSpewerCleanupSec != null && _invisibleSpewerCleanupSec.Value > 0.01f) ? _invisibleSpewerCleanupSec.Value : 0.25f); ManualLogSource log14 = _log; if (log14 != null) { log14.LogInfo((object)$"[Controller] Scheduling spewer cleanup in {num4:F2}s."); } ((MonoBehaviour)this).StartCoroutine(DestroyNetworkObjectAfter(go, num4)); ManualLogSource log15 = _log; if (log15 != null) { log15.LogInfo((object)"[Controller] NetInvisibleAttachAndPuke: done"); } } private IEnumerator WaitForAttachOverlayOrTimeout(PlayerAvatar target, float timeoutSec) { float end = Time.unscaledTime + Mathf.Max(0.1f, timeoutSec); while (Time.unscaledTime < end) { bool flag = false; try { if ((Object)(object)target != (Object)null) { Transform localCameraTransform = target.localCameraTransform; if ((Object)(object)localCameraTransform != (Object)null && (Object)(object)((Component)localCameraTransform).GetComponentInChildren<EnemySlowMouthCameraVisuals>(true) != (Object)null) { flag = true; } if (!flag) { PlayerAvatarVisuals playerAvatarVisuals = target.playerAvatarVisuals; if ((Object)(object)playerAvatarVisuals != (Object)null && (Object)(object)((Component)playerAvatarVisuals).GetComponentInChildren<EnemySlowMouthPlayerAvatarAttached>(true) != (Object)null) { flag = true; } } } } catch { } if (flag) { break; } yield return null; } } private IEnumerator SuppressTumbleForWindow(PlayerAvatar target, float seconds) { float end = Time.unscaledTime + Mathf.Max(0.05f, seconds); while (Time.unscaledTime < end) { try { if ((Object)(object)target != (Object)null) { PlayerTumble tumble = target.tumble; if ((Object)(object)tumble != (Object)null) { MethodInfo method = ((object)tumble).GetType().GetMethod("TumbleRequest", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length == 2) { method.Invoke(tumble, new object[2] { false, false }); } } } try { Type type = ((object)target).GetType(); PropertyInfo property = type.GetProperty("isDisabled", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { property.SetValue(target, false); } FieldInfo field = type.GetField("isDisabled", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(target, false); } } catch { } } } catch { } yield return null; } } private IEnumerator HideAttackVisualsForWindow(PlayerAvatar target, EnemySlowMouth spewer, float seconds) { float end = Time.unscaledTime + Mathf.Max(0.05f, seconds); while (Time.unscaledTime < end) { try { EnemySlowMouthAttaching[] array = Object.FindObjectsOfType<EnemySlowMouthAttaching>(); foreach (EnemySlowMouthAttaching val in array) { bool flag = false; try { if ((Object)(object)val != (Object)null && (Object)(object)val.targetPlayerAvatar == (Object)(object)target) { flag = true; } if (!flag) { object obj = typeof(EnemySlowMouthAttaching).GetField("enemySlowMouth", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(val); if (obj != null && obj == spewer) { flag = true; } } } catch { } if (!flag) { continue; } int num = 0; Renderer[] componentsInChildren = ((Component)val).GetComponentsInChildren<Renderer>(true); foreach (Renderer val2 in componentsInChildren) { if (val2.enabled) { val2.enabled = false; num++; } } if (num > 0) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"[Controller] Hid {num} attach-visual renderers."); } } } } catch { } yield return null; } } private IEnumerator HideJawMeshesForWindow(PlayerAvatar target, float seconds) { float end = Time.unscaledTime + Mathf.Max(0.05f, seconds); while (Time.unscaledTime < end) { try { EnemySlowMouthPlayerAvatarAttached[] array = (EnemySlowMouthPlayerAvatarAttached[])(((Object)(object)target.playerAvatarVisuals != (Object)null) ? ((Array)((Component)target.playerAvatarVisuals).GetComponentsInChildren<EnemySlowMouthPlayerAvatarAttached>(true)) : ((Array)new EnemySlowMouthPlayerAvatarAttached[0])); EnemySlowMouthPlayerAvatarAttached[] array2 = array; foreach (EnemySlowMouthPlayerAvatarAttached val in array2) { DisableJawMeshesKeepParticles(((Component)val).transform); } } catch { } try { Transform localCameraTransform = target.localCameraTransform; if ((Object)(object)localCameraTransform != (Object)null) { EnemySlowMouthCameraVisuals[] componentsInChildren = ((Component)localCameraTransform).GetComponentsInChildren<EnemySlowMouthCameraVisuals>(true); foreach (EnemySlowMouthCameraVisuals val2 in componentsInChildren) { DisableJawMeshesKeepParticles(((Component)val2).transform); try { object? obj2 = typeof(EnemySlowMouthCameraVisuals).GetField("pukeCapsuleRenderer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(val2); Renderer val3 = (Renderer)((obj2 is Renderer) ? obj2 : null); if ((Object)(object)val3 != (Object)null && val3.enabled) { val3.enabled = false; } } catch { } } } } catch { } yield return null; } } private void DisableJawMeshesKeepParticles(Transform root) { if ((Object)(object)root == (Object)null) { return; } Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { if (!(val is ParticleSystemRenderer) && val.enabled) { val.enabled = false; } } } private IEnumerator DestroyNetworkObjectAfter(GameObject go, float seconds) { yield return (object)new WaitForSeconds(Mathf.Max(0.05f, seconds)); if ((Object)(object)go == (Object)null) { yield break; } try { if (!PhotonNetwork.OfflineMode && PhotonNetwork.IsMasterClient) { PhotonNetwork.Destroy(go); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)"[Controller] PhotonNetwork.Destroy called for invisible spewer."); } } else { Object.Destroy((Object)(object)go); ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)"[Controller] Locally destroyed invisible spewer (not master)."); } } } catch (Exception ex) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogWarning((object)("[Controller] DestroyNetworkObjectAfter exception: " + ex.Message)); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }