Decompiled source of No Forest Falling Trees v1.3.2

No Forest Falling Trees.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("No Forest Falling Trees")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("No Forest Falling Trees")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("10de6438-cfe4-4ae6-835a-f54300972333")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace NoForestFallingTrees;

[BepInPlugin("kumo.sulfur.no_forest_falling_trees", "No Forest Falling Trees", "1.3.1")]
public sealed class Plugin : BaseUnityPlugin
{
	[CompilerGenerated]
	private sealed class <CleanupBurst>d__19 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public string reason;

		public Plugin <>4__this;

		private int <iterations>5__1;

		private float <interval>5__2;

		private int <i>5__3;

		private int <neutralized>5__4;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <CleanupBurst>d__19(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<>4__this.cleanupBurstRunning = true;
				if (<>4__this.logActions.Value)
				{
					((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)("Starting Forest Falling Trees cleanup burst: " + reason));
				}
				<iterations>5__1 = Mathf.Max(1, <>4__this.cleanupBurstIterations.Value);
				<interval>5__2 = Mathf.Max(0.05f, <>4__this.cleanupBurstInterval.Value);
				<i>5__3 = 0;
				break;
			case 1:
				<>1__state = -1;
				<i>5__3++;
				break;
			}
			if (<i>5__3 < <iterations>5__1)
			{
				<neutralized>5__4 = <>4__this.NeutralizeForestAmbushObjectsInLoadedScenes();
				if (<>4__this.logActions.Value && <neutralized>5__4 > 0)
				{
					((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)("Cleanup pass neutralized Forest Ambush objects: " + <neutralized>5__4));
				}
				<>2__current = (object)new WaitForSeconds(<interval>5__2);
				<>1__state = 1;
				return true;
			}
			<>4__this.cleanupBurstRunning = false;
			if (<>4__this.logActions.Value)
			{
				((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)("Finished Forest Falling Trees cleanup burst: " + reason));
			}
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	[CompilerGenerated]
	private sealed class <SceneSignatureWatchLoop>d__16 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public Plugin <>4__this;

		private int <currentSignature>5__1;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <SceneSignatureWatchLoop>d__16(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				break;
			case 1:
				<>1__state = -1;
				<currentSignature>5__1 = <>4__this.ComputeSceneSignature();
				if (<currentSignature>5__1 != <>4__this.lastSceneSignature)
				{
					<>4__this.lastSceneSignature = <currentSignature>5__1;
					<>4__this.StartCleanupBurst("scene hierarchy changed");
				}
				break;
			}
			<>2__current = (object)new WaitForSeconds(Mathf.Max(0.25f, <>4__this.sceneSignatureCheckInterval.Value));
			<>1__state = 1;
			return true;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	internal static ManualLogSource Log;

	private ConfigEntry<bool> logActions;

	private ConfigEntry<bool> muteAmbushContentAudio;

	private ConfigEntry<bool> hideTreeRenderers;

	private ConfigEntry<bool> disableTreeColliders;

	private ConfigEntry<bool> disableTreeShakeable;

	private ConfigEntry<bool> stopParticles;

	private ConfigEntry<float> sceneSignatureCheckInterval;

	private ConfigEntry<int> cleanupBurstIterations;

	private ConfigEntry<float> cleanupBurstInterval;

	private int lastSceneSignature;

	private bool cleanupBurstRunning;

	private readonly HashSet<int> neutralizedAmbushIds = new HashSet<int>();

	private void Awake()
	{
		Log = ((BaseUnityPlugin)this).Logger;
		logActions = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "LogActions", false, "Log neutralized Forest Ambush visual objects.");
		muteAmbushContentAudio = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "MuteAmbushContentAudio", true, "Mute the AudioSource on Forest Ambush Contents without disabling the component.");
		hideTreeRenderers = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "HideTreeRenderers", true, "Hide Forest Ambush tree renderers.");
		disableTreeColliders = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DisableTreeColliders", true, "Disable Forest Ambush tree colliders.");
		disableTreeShakeable = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "DisableTreeShakeable", true, "Disable TreeShakeable components only on Forest Ambush tree objects.");
		stopParticles = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "StopParticles", true, "Stop and hide Forest Ambush particle effects.");
		sceneSignatureCheckInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "SceneSignatureCheckInterval", 1f, "How often to cheaply check whether the loaded scene hierarchy changed.");
		cleanupBurstIterations = ((BaseUnityPlugin)this).Config.Bind<int>("Performance", "CleanupBurstIterations", 24, "How many cleanup passes to run after a scene hierarchy change.");
		cleanupBurstInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Performance", "CleanupBurstInterval", 0.2f, "Delay between cleanup passes during a cleanup burst.");
		SceneManager.sceneLoaded += OnSceneLoaded;
		lastSceneSignature = ComputeSceneSignature();
		StartCleanupBurst("startup");
		((MonoBehaviour)this).StartCoroutine(SceneSignatureWatchLoop());
		((BaseUnityPlugin)this).Logger.LogInfo((object)"No Forest Falling Trees 1.3.1 loaded.");
	}

	private void OnDestroy()
	{
		SceneManager.sceneLoaded -= OnSceneLoaded;
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		neutralizedAmbushIds.Clear();
		lastSceneSignature = ComputeSceneSignature();
		StartCleanupBurst("scene loaded");
	}

	[IteratorStateMachine(typeof(<SceneSignatureWatchLoop>d__16))]
	private IEnumerator SceneSignatureWatchLoop()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <SceneSignatureWatchLoop>d__16(0)
		{
			<>4__this = this
		};
	}

	private int ComputeSceneSignature()
	{
		//IL_0016: 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)
		int num = 17;
		num = num * 31 + SceneManager.sceneCount;
		for (int i = 0; i < SceneManager.sceneCount; i++)
		{
			Scene sceneAt = SceneManager.GetSceneAt(i);
			if (((Scene)(ref sceneAt)).IsValid() && ((Scene)(ref sceneAt)).isLoaded)
			{
				num = num * 31 + ((Scene)(ref sceneAt)).name.GetHashCode();
				num = num * 31 + ((Scene)(ref sceneAt)).rootCount;
			}
		}
		return num;
	}

	private void StartCleanupBurst(string reason)
	{
		if (!cleanupBurstRunning)
		{
			((MonoBehaviour)this).StartCoroutine(CleanupBurst(reason));
		}
	}

	[IteratorStateMachine(typeof(<CleanupBurst>d__19))]
	private IEnumerator CleanupBurst(string reason)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <CleanupBurst>d__19(0)
		{
			<>4__this = this,
			reason = reason
		};
	}

	private int NeutralizeForestAmbushObjectsInLoadedScenes()
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		int num = 0;
		for (int i = 0; i < SceneManager.sceneCount; i++)
		{
			Scene sceneAt = SceneManager.GetSceneAt(i);
			if (((Scene)(ref sceneAt)).IsValid() && ((Scene)(ref sceneAt)).isLoaded)
			{
				GameObject[] rootGameObjects = ((Scene)(ref sceneAt)).GetRootGameObjects();
				GameObject[] array = rootGameObjects;
				foreach (GameObject val in array)
				{
					num += ScanTransformForForestAmbush(val.transform);
				}
			}
		}
		return num;
	}

	private int ScanTransformForForestAmbush(Transform transform)
	{
		if ((Object)(object)transform == (Object)null)
		{
			return 0;
		}
		GameObject gameObject = ((Component)transform).gameObject;
		if ((Object)(object)gameObject == (Object)null)
		{
			return 0;
		}
		if (IsForestAmbushRootName(((Object)gameObject).name))
		{
			int instanceID = ((Object)gameObject).GetInstanceID();
			if (neutralizedAmbushIds.Contains(instanceID))
			{
				return 0;
			}
			neutralizedAmbushIds.Add(instanceID);
			NeutralizeForestAmbushRoot(gameObject);
			return 1;
		}
		int num = 0;
		for (int num2 = transform.childCount - 1; num2 >= 0; num2--)
		{
			num += ScanTransformForForestAmbush(transform.GetChild(num2));
		}
		return num;
	}

	private void NeutralizeForestAmbushRoot(GameObject ambushRoot)
	{
		if ((Object)(object)ambushRoot == (Object)null)
		{
			return;
		}
		Transform val = ambushRoot.transform.Find("Contents");
		if ((Object)(object)val == (Object)null)
		{
			if (logActions.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Forest Ambush has no Contents: " + GetPath(ambushRoot)));
			}
			return;
		}
		GameObject gameObject = ((Component)val).gameObject;
		if (muteAmbushContentAudio.Value)
		{
			MuteAudioSourcesWithoutDisabling(gameObject);
		}
		for (int num = val.childCount - 1; num >= 0; num--)
		{
			Transform child = val.GetChild(num);
			if (!((Object)(object)child == (Object)null))
			{
				GameObject gameObject2 = ((Component)child).gameObject;
				string name = ((Object)gameObject2).name;
				if (IsAmbushTreeObject(name))
				{
					NeutralizeTreeVisualObject(gameObject2);
					if (logActions.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Neutralized Forest Ambush tree visual object: " + GetPath(gameObject2)));
					}
				}
				else if (IsAmbushParticleObject(name))
				{
					NeutralizeParticleVisualObject(gameObject2);
					if (logActions.Value)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)("Neutralized Forest Ambush particle object: " + GetPath(gameObject2)));
					}
				}
			}
		}
		if (logActions.Value)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Neutralized Forest Ambush falling-tree presentation only: " + GetPath(ambushRoot)));
		}
	}

	private void NeutralizeTreeVisualObject(GameObject go)
	{
		if (!((Object)(object)go == (Object)null))
		{
			if (hideTreeRenderers.Value)
			{
				DisableRenderers(go);
			}
			if (disableTreeColliders.Value)
			{
				DisableColliders(go);
			}
			if (disableTreeShakeable.Value)
			{
				DisableBehaviourComponentByTypeNameRecursive(go, "TreeShakeable");
			}
		}
	}

	private void NeutralizeParticleVisualObject(GameObject go)
	{
		if (!((Object)(object)go == (Object)null))
		{
			DisableRenderers(go);
			if (stopParticles.Value)
			{
				StopParticleSystems(go);
			}
		}
	}

	private void MuteAudioSourcesWithoutDisabling(GameObject go)
	{
		if ((Object)(object)go == (Object)null)
		{
			return;
		}
		Component[] components = go.GetComponents<Component>();
		Component[] array = components;
		foreach (Component val in array)
		{
			if ((Object)(object)val == (Object)null || !string.Equals(((object)val).GetType().Name, "AudioSource", StringComparison.OrdinalIgnoreCase))
			{
				continue;
			}
			try
			{
				AudioSource val2 = (AudioSource)(object)((val is AudioSource) ? val : null);
				if ((Object)(object)val2 != (Object)null)
				{
					val2.volume = 0f;
					val2.mute = true;
				}
			}
			catch (Exception ex)
			{
				if (logActions.Value)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to mute AudioSource: " + ex.Message));
				}
			}
		}
	}

	private static void DisableRenderers(GameObject go)
	{
		if ((Object)(object)go == (Object)null)
		{
			return;
		}
		Renderer[] componentsInChildren = go.GetComponentsInChildren<Renderer>(true);
		Renderer[] array = componentsInChildren;
		foreach (Renderer val in array)
		{
			if ((Object)(object)val != (Object)null)
			{
				val.enabled = false;
			}
		}
	}

	private static void DisableColliders(GameObject go)
	{
		if ((Object)(object)go == (Object)null)
		{
			return;
		}
		Collider[] componentsInChildren = go.GetComponentsInChildren<Collider>(true);
		Collider[] array = componentsInChildren;
		foreach (Collider val in array)
		{
			if ((Object)(object)val != (Object)null)
			{
				val.enabled = false;
			}
		}
	}

	private static void StopParticleSystems(GameObject go)
	{
		if ((Object)(object)go == (Object)null)
		{
			return;
		}
		ParticleSystem[] componentsInChildren = go.GetComponentsInChildren<ParticleSystem>(true);
		ParticleSystem[] array = componentsInChildren;
		foreach (ParticleSystem val in array)
		{
			if (!((Object)(object)val == (Object)null))
			{
				val.Stop(true, (ParticleSystemStopBehavior)0);
			}
		}
	}

	private static void DisableBehaviourComponentByTypeNameRecursive(GameObject go, string typeName)
	{
		if ((Object)(object)go == (Object)null || string.IsNullOrEmpty(typeName))
		{
			return;
		}
		Component[] componentsInChildren = go.GetComponentsInChildren<Component>(true);
		Component[] array = componentsInChildren;
		foreach (Component val in array)
		{
			if (!((Object)(object)val == (Object)null) && string.Equals(((object)val).GetType().Name, typeName, StringComparison.OrdinalIgnoreCase))
			{
				Behaviour val2 = (Behaviour)(object)((val is Behaviour) ? val : null);
				if ((Object)(object)val2 != (Object)null)
				{
					val2.enabled = false;
				}
			}
		}
	}

	private static bool IsAmbushTreeObject(string objectName)
	{
		if (string.IsNullOrEmpty(objectName))
		{
			return false;
		}
		return objectName.StartsWith("Tree", StringComparison.OrdinalIgnoreCase);
	}

	private static bool IsAmbushParticleObject(string objectName)
	{
		if (string.IsNullOrEmpty(objectName))
		{
			return false;
		}
		return objectName.IndexOf("Particles", StringComparison.OrdinalIgnoreCase) >= 0;
	}

	private static bool IsForestAmbushRootName(string objectName)
	{
		if (string.IsNullOrEmpty(objectName))
		{
			return false;
		}
		return objectName.IndexOf("Event_Forest_Ambush", StringComparison.OrdinalIgnoreCase) >= 0 || objectName.IndexOf("Forest_Ambush", StringComparison.OrdinalIgnoreCase) >= 0;
	}

	private static string GetPath(GameObject go)
	{
		if ((Object)(object)go == (Object)null)
		{
			return string.Empty;
		}
		string text = ((Object)go).name;
		Transform parent = go.transform.parent;
		while ((Object)(object)parent != (Object)null)
		{
			text = ((Object)parent).name + "/" + text;
			parent = parent.parent;
		}
		return text;
	}
}