Decompiled source of PingoEnemy v1.0.0

BepInEx\plugins\PingoEnemy\PingoEnemy.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalLib.Modules;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("PingoEnemy")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PingoEnemy")]
[assembly: AssemblyTitle("PingoEnemy")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PingoEnemy
{
	[BepInPlugin("JLeonL.PingoEnemy", "Pingo Enemy", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class PingoEnemyPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "JLeonL.PingoEnemy";

		public const string PluginName = "Pingo Enemy";

		public const string PluginVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static PingoEnemyPlugin Instance;

		internal static AssetBundle? Bundle;

		internal static AudioClip? PingoClip;

		internal static EnemyType? RegisteredEnemyType;

		internal static Texture2D? LuigiBodyTexture;

		internal static Texture2D? LuigiBodyNormalTexture;

		internal static Texture2D? LuigiEyeTexture;

		internal static Texture2D? LuigiEye1Texture;

		internal static Texture2D? LuigiEye2Texture;

		private static ConfigEntry<int> spawnWeight;

		private static ConfigEntry<bool> enableDebugSpawnKey;

		private static ConfigEntry<bool> forceSpawnAfterLanding;

		private static ConfigEntry<float> minimumVisualHeight;

		private static ConfigEntry<bool> forceTestSpawnInFrontOfPlayer;

		private static ConfigEntry<bool> forceVisibleFallbackBody;

		private static bool spawnedAfterLanding;

		private float nextDebugSpawnAllowedAt;

		internal static bool ForceSpawnAfterLandingEnabled => forceSpawnAfterLanding.Value;

		internal static float MinimumVisualHeight => Mathf.Max(1.8f, minimumVisualHeight.Value);

		internal static bool ForceTestSpawnInFrontOfPlayer => forceTestSpawnInFrontOfPlayer.Value;

		internal static bool ForceVisibleFallbackBody => forceVisibleFallbackBody.Value;

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfig();
			Harmony.CreateAndPatchAll(typeof(PingoEnemyPlugin).Assembly, "JLeonL.PingoEnemy");
			try
			{
				LoadAssets();
				((MonoBehaviour)this).StartCoroutine(LoadAudioFromDisk());
				RegisterEnemy();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Pingo Enemy loaded.");
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)string.Format("Failed to load {0}: {1}", "Pingo Enemy", arg));
			}
		}

		private void Update()
		{
			if (enableDebugSpawnKey.Value && DebugSpawnKeyPressed() && Time.time >= nextDebugSpawnAllowedAt)
			{
				nextDebugSpawnAllowedAt = Time.time + 1f;
				SpawnPingoNearLocalPlayer("debug hotkey");
			}
			if (forceSpawnAfterLanding.Value && !spawnedAfterLanding && !((Object)(object)StartOfRound.Instance == (Object)null) && StartOfRound.Instance.shipHasLanded)
			{
				spawnedAfterLanding = true;
				SpawnPingoNearLocalPlayer("force spawn after landing");
			}
		}

		internal static void ResetForcedSpawnForNewRound()
		{
			spawnedAfterLanding = false;
			Log.LogInfo((object)"Reset Pingo forced spawn state for new round.");
		}

		internal static void TryForceSpawnAfterLanding(string reason)
		{
			if (!ForceSpawnAfterLandingEnabled)
			{
				Log.LogInfo((object)("Skipped forced Pingo spawn for " + reason + ": config disabled."));
				return;
			}
			if (spawnedAfterLanding)
			{
				Log.LogInfo((object)("Skipped forced Pingo spawn for " + reason + ": already spawned this round."));
				return;
			}
			spawnedAfterLanding = true;
			SpawnPingoNearLocalPlayer(reason);
		}

		private void BindConfig()
		{
			spawnWeight = ((BaseUnityPlugin)this).Config.Bind<int>("Spawning", "SpawnWeight", 175, "Normal LethalLib spawn weight for Pingo. Default is roughly x5 a common enemy weight.");
			enableDebugSpawnKey = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "EnableDebugSpawnKey", false, "If true, the host can press F6 in a round to spawn Pingo near the local player.");
			forceSpawnAfterLanding = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "ForceSpawnAfterLanding", false, "If true, the host spawns one Pingo near the local player after the ship lands.");
			forceTestSpawnInFrontOfPlayer = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "ForceTestSpawnInFrontOfPlayer", true, "If true, forced/debug spawns appear directly in front of the local player instead of snapping to dungeon NavMesh.");
			minimumVisualHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals", "MinimumVisualHeight", 2.1f, "Minimum world-space height for Pingo's visible model. A player is roughly this height.");
			forceVisibleFallbackBody = ((BaseUnityPlugin)this).Config.Bind<bool>("Visuals", "ForceVisibleFallbackBody", false, "If true, adds a simple player-sized visible body so Pingo can never be invisible while testing.");
		}

		private static bool DebugSpawnKeyPressed()
		{
			if (Input.GetKeyDown((KeyCode)287))
			{
				return true;
			}
			Keyboard current = Keyboard.current;
			if (current == null)
			{
				return false;
			}
			return ((ButtonControl)current.f6Key).wasPressedThisFrame;
		}

		private static void LoadAssets()
		{
			string text = Path.Combine(Path.GetDirectoryName(typeof(PingoEnemyPlugin).Assembly.Location), "pingoenemyassets");
			if (File.Exists(text))
			{
				Bundle = AssetBundle.LoadFromFile(text);
				AssetBundle? bundle = Bundle;
				PingoClip = ((bundle != null) ? bundle.LoadAsset<AudioClip>("pingo") : null);
				AssetBundle? bundle2 = Bundle;
				LuigiBodyTexture = ((bundle2 != null) ? bundle2.LoadAsset<Texture2D>("pc02_body") : null);
				AssetBundle? bundle3 = Bundle;
				LuigiBodyNormalTexture = ((bundle3 != null) ? bundle3.LoadAsset<Texture2D>("pc02_body_nml") : null);
				AssetBundle? bundle4 = Bundle;
				LuigiEyeTexture = ((bundle4 != null) ? bundle4.LoadAsset<Texture2D>("pc02_eye") : null);
				AssetBundle? bundle5 = Bundle;
				LuigiEye1Texture = ((bundle5 != null) ? bundle5.LoadAsset<Texture2D>("Luigi_eye_1") : null);
				AssetBundle? bundle6 = Bundle;
				LuigiEye2Texture = ((bundle6 != null) ? bundle6.LoadAsset<Texture2D>("Luigi_Eye2") : null);
				Log.LogInfo((object)(((Object)(object)Bundle == (Object)null) ? ("Failed to load AssetBundle from " + text) : ("Loaded AssetBundle from " + text)));
				Log.LogInfo((object)(((Object)(object)PingoClip == (Object)null) ? "AssetBundle did not contain an AudioClip named 'pingo'." : ("Loaded AudioClip '" + ((Object)PingoClip).name + "' from AssetBundle.")));
				Log.LogInfo((object)$"Loaded texture assets: body={(Object)(object)LuigiBodyTexture != (Object)null}; normal={(Object)(object)LuigiBodyNormalTexture != (Object)null}; pcEye={(Object)(object)LuigiEyeTexture != (Object)null}; luigiEye1={(Object)(object)LuigiEye1Texture != (Object)null}; luigiEye2={(Object)(object)LuigiEye2Texture != (Object)null}.");
			}
			else
			{
				Log.LogWarning((object)("Missing AssetBundle at " + text + "; Pingo will use the fallback prefab."));
			}
			if ((Object)(object)PingoClip == (Object)null)
			{
				Log.LogInfo((object)"No AssetBundle audio found. Falling back to direct pingo.mp3 loading.");
			}
		}

		private static IEnumerator LoadAudioFromDisk()
		{
			if ((Object)(object)PingoClip != (Object)null)
			{
				yield break;
			}
			string text = Path.Combine(Path.GetDirectoryName(typeof(PingoEnemyPlugin).Assembly.Location), "pingo.mp3");
			if (!File.Exists(text))
			{
				Log.LogWarning((object)("Missing audio file: " + text));
				yield break;
			}
			UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(new Uri(text).AbsoluteUri, (AudioType)13);
			try
			{
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					Log.LogWarning((object)("Could not load pingo.mp3: " + request.error));
					yield break;
				}
				PingoClip = DownloadHandlerAudioClip.GetContent(request);
				((Object)PingoClip).name = "pingo";
				Log.LogInfo((object)"Loaded pingo.mp3 directly from plugin folder.");
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		private static void RegisterEnemy()
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Expected O, but got Unknown
			//IL_00ec: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Expected O, but got Unknown
			AssetBundle? bundle = Bundle;
			GameObject val = ((bundle != null) ? bundle.LoadAsset<GameObject>("PingoEnemy") : null);
			if ((Object)(object)val == (Object)null)
			{
				Log.LogWarning((object)"AssetBundle did not contain a GameObject named 'PingoEnemy'; using fallback prefab.");
				val = CreatePlaceholderPrefab();
			}
			else
			{
				Log.LogInfo((object)("Loaded enemy prefab '" + ((Object)val).name + "' from AssetBundle."));
			}
			EnsureEnemyComponents(val);
			((Object)val).name = "Pingo";
			EnemyType val2 = ScriptableObject.CreateInstance<EnemyType>();
			((Object)val2).name = "Pingo";
			val2.enemyName = "Pingo";
			val2.enemyPrefab = val;
			val2.MaxCount = 1;
			val2.PowerLevel = 0f;
			val2.probabilityCurve = new AnimationCurve((Keyframe[])(object)new Keyframe[2]
			{
				new Keyframe(0f, 1f),
				new Keyframe(1f, 1f)
			});
			val2.numberSpawnedFalloff = new AnimationCurve((Keyframe[])(object)new Keyframe[2]
			{
				new Keyframe(0f, 1f),
				new Keyframe(1f, 1f)
			});
			val2.isOutsideEnemy = false;
			val2.isDaytimeEnemy = false;
			val2.normalizedTimeInDayToLeave = 1f;
			val2.stunTimeMultiplier = 1f;
			val2.canSeeThroughFog = false;
			((EnemyAI)val.GetComponent<PingoEnemyAI>()).enemyType = val2;
			TerminalNode val3 = ScriptableObject.CreateInstance<TerminalNode>();
			val3.creatureName = "Pingo";
			val3.displayText = "Pingo\n\nNo mata, no persigue y no se mueve. Solo se queda en la sala haciendo ruido con cada vez mas insistencia.\n";
			val3.clearPreviousText = true;
			val3.maxCharactersToType = 2000;
			TerminalKeyword val4 = ScriptableObject.CreateInstance<TerminalKeyword>();
			val4.word = "pingo";
			val4.isVerb = false;
			RegisteredEnemyType = val2;
			Enemies.RegisterEnemy(val2, Mathf.Max(0, spawnWeight.Value), (LevelTypes)(-1), val3, val4);
		}

		internal static bool SpawnPingoNearLocalPlayer(string reason)
		{
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)NetworkManager.Singleton == (Object)null || (!NetworkManager.Singleton.IsHost && !NetworkManager.Singleton.IsServer))
			{
				Log.LogInfo((object)("Ignored Pingo spawn request from non-host client: " + reason));
				return false;
			}
			if ((Object)(object)RoundManager.Instance == (Object)null)
			{
				Log.LogInfo((object)("Ignored Pingo spawn request before a round manager exists: " + reason));
				return false;
			}
			if ((Object)(object)RegisteredEnemyType == (Object)null)
			{
				Log.LogWarning((object)("Cannot spawn Pingo for " + reason + ": enemy type is not registered."));
				return false;
			}
			PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController;
			if ((Object)(object)val == (Object)null)
			{
				Log.LogWarning((object)("Cannot spawn Pingo for " + reason + ": no local player."));
				return false;
			}
			Vector3 val2 = GetSafeSpawnPositionInFrontOfPlayer(val);
			NavMeshHit val3 = default(NavMeshHit);
			if (!ForceTestSpawnInFrontOfPlayer && NavMesh.SamplePosition(val2, ref val3, 8f, -1))
			{
				val2 = ((NavMeshHit)(ref val3)).position;
			}
			else if (!ForceTestSpawnInFrontOfPlayer)
			{
				Log.LogWarning((object)$"Could not find nearby NavMesh for Pingo debug spawn; using raw position {val2}.");
			}
			float num = ((Component)val).transform.eulerAngles.y + 180f;
			RoundManager.Instance.SpawnEnemyGameObject(val2, num, -1, RegisteredEnemyType);
			Log.LogInfo((object)("Spawned Pingo near local player for " + reason + "."));
			return true;
		}

		private static Vector3 GetSafeSpawnPositionInFrontOfPlayer(PlayerControllerB player)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			Camera gameplayCamera = player.gameplayCamera;
			Vector3 val = (((Object)(object)gameplayCamera != (Object)null) ? ((Component)gameplayCamera).transform.position : (((Component)player).transform.position + Vector3.up * 1.6f));
			Vector3 val2 = (((Object)(object)gameplayCamera != (Object)null) ? ((Component)gameplayCamera).transform.forward : ((Component)player).transform.forward);
			val2.y = 0f;
			if (((Vector3)(ref val2)).sqrMagnitude < 0.01f)
			{
				val2 = ((Component)player).transform.forward;
			}
			Vector3 val3 = val + ((Vector3)(ref val2)).normalized * 3f;
			val3.y = val.y + 1f;
			RaycastHit val4 = default(RaycastHit);
			if (Physics.Raycast(val3, Vector3.down, ref val4, 12f, StartOfRound.Instance.collidersAndRoomMaskAndDefault))
			{
				val3 = ((RaycastHit)(ref val4)).point + Vector3.up * 0.05f;
			}
			else
			{
				val3.y = ((Component)player).transform.position.y + 0.05f;
			}
			Log.LogInfo((object)$"Pingo safe test spawn position: player={((Component)player).transform.position}; spawn={val3}; forceInFront={ForceTestSpawnInFrontOfPlayer}.");
			return val3;
		}

		private static GameObject CreatePlaceholderPrefab()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GameObject.CreatePrimitive((PrimitiveType)1);
			((Object)obj).name = "Pingo";
			obj.transform.localScale = new Vector3(0.8f, 1.25f, 0.8f);
			Object.DontDestroyOnLoad((Object)(object)obj);
			obj.SetActive(false);
			return obj;
		}

		private static void EnsureEnemyComponents(GameObject prefab)
		{
			if ((Object)(object)prefab.GetComponent<NetworkObject>() == (Object)null)
			{
				prefab.AddComponent<NetworkObject>();
			}
			((EnemyAI)(prefab.GetComponent<PingoEnemyAI>() ?? prefab.AddComponent<PingoEnemyAI>())).enemyType = null;
			NavMeshAgent obj = prefab.GetComponent<NavMeshAgent>() ?? prefab.AddComponent<NavMeshAgent>();
			obj.speed = 0f;
			obj.angularSpeed = 0f;
			obj.acceleration = 0f;
			obj.stoppingDistance = 0f;
			obj.updatePosition = false;
			obj.updateRotation = false;
			AudioSource obj2 = prefab.GetComponent<AudioSource>() ?? prefab.AddComponent<AudioSource>();
			obj2.playOnAwake = false;
			obj2.spatialize = false;
			obj2.spatializePostEffects = false;
			obj2.spatialBlend = 1f;
			obj2.rolloffMode = (AudioRolloffMode)1;
			obj2.minDistance = 4f;
			obj2.maxDistance = 35f;
			EnsureScanNode(prefab);
		}

		private static void EnsureScanNode(GameObject prefab)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_004f: 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_0069: 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)
			ScanNodeProperties val = prefab.GetComponentInChildren<ScanNodeProperties>(true);
			GameObject val2;
			if ((Object)(object)val == (Object)null)
			{
				val2 = new GameObject("PingoScanNode");
				val2.transform.SetParent(prefab.transform, false);
				val = val2.AddComponent<ScanNodeProperties>();
			}
			else
			{
				val2 = ((Component)val).gameObject;
			}
			((Object)val2).name = "PingoScanNode";
			val2.transform.localPosition = Vector3.up * 1.15f;
			val2.transform.localRotation = Quaternion.identity;
			val2.transform.localScale = Vector3.one;
			int num = LayerMask.NameToLayer("ScanNode");
			if (num >= 0)
			{
				val2.layer = num;
			}
			SphereCollider obj = val2.GetComponent<SphereCollider>() ?? val2.AddComponent<SphereCollider>();
			((Collider)obj).isTrigger = true;
			obj.radius = 1.25f;
			val.headerText = "Pingo";
			val.subText = "Enemy";
			val.nodeType = 1;
			val.creatureScanID = -1;
			val.minRange = 1;
			val.maxRange = 80;
			val.requiresLineOfSight = false;
		}
	}
	public sealed class PingoEnemyAI : EnemyAI
	{
		private const float BaseInterval = 16f;

		private const float MinimumInterval = 1.25f;

		private const float SameRoomMultiplier = 0.45f;

		private const float LookingMultiplier = 0.55f;

		private const float NearVolumeRadius = 18f;

		private const float NearVolumeStepSeconds = 10f;

		private const float NearVolumeStepGain = 0.1f;

		private const float OverlapRampStartSeconds = 60f;

		private const float IntervalReductionPerNearPlay = 0.1f;

		private const float OverlapMinimumInterval = 0.1f;

		private const int MinimumIntervalPlaysBeforeReset = 30;

		private AudioSource? source;

		private float nextNoiseAt;

		private float aliveForSeconds;

		private float nearPlayerSeconds;

		private float accumulatedIntervalReduction;

		private int minimumIntervalPlayCount;

		public override void Start()
		{
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			((EnemyAI)this).Start();
			source = ((Component)this).GetComponent<AudioSource>();
			if ((Object)(object)source != (Object)null && (Object)(object)PingoEnemyPlugin.PingoClip != (Object)null)
			{
				source.clip = PingoEnemyPlugin.PingoClip;
				source.spatialize = false;
				source.spatializePostEffects = false;
				source.volume = 1f;
			}
			((Object)((Component)this).gameObject).name = "Pingo";
			EnsureRuntimeScanNode();
			base.movingTowardsTargetPlayer = false;
			nextNoiseAt = Time.time + 3f;
			EnsureVisibleFallbackBody();
			ApplyLuigiMaterials();
			PingoEnemyPlugin.Log.LogInfo((object)$"PingoEnemyAI started at {((Component)this).transform.position}; hasAudio={(Object)(object)PingoEnemyPlugin.PingoClip != (Object)null}; isOwner={((NetworkBehaviour)this).IsOwner}.");
			((MonoBehaviour)this).StartCoroutine(NormalizeVisualsAfterSpawn());
		}

		private void ApplyLuigiMaterials()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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_009a: Unknown result type (might be due to invalid IL or missing references)
			Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
			if (componentsInChildren.Length == 0)
			{
				PingoEnemyPlugin.Log.LogWarning((object)"Cannot apply Luigi materials: Pingo has no renderers.");
				return;
			}
			Material body = CreateOpaqueMaterial("Pingo_Luigi_Body_Runtime", PingoEnemyPlugin.LuigiBodyTexture, PingoEnemyPlugin.LuigiBodyNormalTexture, Color.white);
			Material eyes = CreateEyeMaterial("Pingo_Luigi_Eyes_Runtime", PingoEnemyPlugin.LuigiEyeTexture);
			Material skin = CreateOpaqueMaterial("Pingo_Luigi_Skin_Runtime", null, null, new Color(1f, 0.72f, 0.52f, 1f));
			Material gloves = CreateOpaqueMaterial("Pingo_Luigi_Gloves_Runtime", null, null, Color.white);
			Material shoes = CreateOpaqueMaterial("Pingo_Luigi_Shoes_Runtime", null, null, new Color(0.35f, 0.16f, 0.06f, 1f));
			Renderer[] array = componentsInChildren;
			foreach (Renderer val in array)
			{
				Material[] sharedMaterials = val.sharedMaterials;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					string slotName = (((Object)(object)sharedMaterials[j] != (Object)null) ? ((Object)sharedMaterials[j]).name.ToLowerInvariant() : string.Empty);
					string rendererName = ((Object)val).name.ToLowerInvariant();
					sharedMaterials[j] = ChooseLuigiMaterial(rendererName, slotName, j, body, eyes, skin, gloves, shoes);
				}
				val.sharedMaterials = sharedMaterials;
				val.enabled = true;
			}
			PingoEnemyPlugin.Log.LogInfo((object)$"Applied runtime Luigi materials to {componentsInChildren.Length} renderer(s).");
		}

		private static Material ChooseLuigiMaterial(string rendererName, string slotName, int materialIndex, Material body, Material eyes, Material skin, Material gloves, Material shoes)
		{
			string text = rendererName + " " + slotName;
			if (rendererName == "eye" || text.Contains("eye"))
			{
				return eyes;
			}
			if (rendererName.StartsWith("newluigi_m1shape") && materialIndex >= 3 && materialIndex <= 6)
			{
				return eyes;
			}
			if (text.Contains("hand") || text.Contains("glove"))
			{
				return gloves;
			}
			if (text.Contains("shoe") || text.Contains("boot"))
			{
				return shoes;
			}
			if (text.Contains("skin") || text.Contains("face") || text.Contains("head") || text.Contains("nose") || text.Contains("ear"))
			{
				return skin;
			}
			return body;
		}

		private static Material CreateOpaqueMaterial(string name, Texture2D? baseTexture, Texture2D? normalTexture, Color color)
		{
			//IL_0018: 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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			Material val = new Material(Shader.Find("HDRP/Lit") ?? Shader.Find("Standard"))
			{
				name = name,
				color = color
			};
			SetColor(val, "_BaseColor", color);
			SetColor(val, "_Color", color);
			SetFloat(val, "_SurfaceType", 0f);
			SetFloat(val, "_BlendMode", 0f);
			SetFloat(val, "_AlphaCutoffEnable", 0f);
			SetFloat(val, "_TransparentSortPriority", 0f);
			val.renderQueue = -1;
			if ((Object)(object)baseTexture != (Object)null)
			{
				SetTexture(val, "_BaseColorMap", (Texture)(object)baseTexture);
				SetTexture(val, "_MainTex", (Texture)(object)baseTexture);
			}
			if ((Object)(object)normalTexture != (Object)null)
			{
				SetTexture(val, "_NormalMap", (Texture)(object)normalTexture);
				SetTexture(val, "_BumpMap", (Texture)(object)normalTexture);
				val.EnableKeyword("_NORMALMAP");
			}
			return val;
		}

		private static Material CreateEyeMaterial(string name, Texture2D? eyeTexture)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_006f: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: 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)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			ConfigureEyeTexture(eyeTexture);
			Shader obj = Shader.Find("HDRP/Unlit") ?? Shader.Find("Unlit/Texture") ?? Shader.Find("HDRP/Lit") ?? Shader.Find("Standard");
			Color color = default(Color);
			((Color)(ref color))..ctor(0.8f, 0.8f, 0.8f, 1f);
			Material val = new Material(obj)
			{
				name = name,
				color = color
			};
			SetColor(val, "_BaseColor", color);
			SetColor(val, "_Color", color);
			SetColor(val, "_UnlitColor", color);
			SetFloat(val, "_SurfaceType", 0f);
			SetFloat(val, "_BlendMode", 0f);
			SetFloat(val, "_AlphaCutoffEnable", 0f);
			SetFloat(val, "_Smoothness", 0.35f);
			SetFloat(val, "_Metallic", 0f);
			val.renderQueue = -1;
			if ((Object)(object)eyeTexture != (Object)null)
			{
				SetTexture(val, "_BaseColorMap", (Texture)(object)eyeTexture);
				SetTexture(val, "_UnlitColorMap", (Texture)(object)eyeTexture);
				SetTexture(val, "_MainTex", (Texture)(object)eyeTexture);
				SetTexture(val, "_EmissionMap", (Texture)(object)eyeTexture);
				SetTexture(val, "_EmissiveColorMap", (Texture)(object)eyeTexture);
				SetTextureScale(val, "_BaseColorMap", Vector2.one);
				SetTextureScale(val, "_UnlitColorMap", Vector2.one);
				SetTextureScale(val, "_MainTex", Vector2.one);
				SetTextureOffset(val, "_BaseColorMap", Vector2.zero);
				SetTextureOffset(val, "_UnlitColorMap", Vector2.zero);
				SetTextureOffset(val, "_MainTex", Vector2.zero);
			}
			if (val.HasProperty("_EmissiveColor"))
			{
				val.SetColor("_EmissiveColor", new Color(0.18f, 0.18f, 0.18f, 1f));
			}
			SetColor(val, "_EmissionColor", new Color(0.18f, 0.18f, 0.18f, 1f));
			if (val.HasProperty("_EmissiveIntensity"))
			{
				val.SetFloat("_EmissiveIntensity", 0.2f);
			}
			val.EnableKeyword("_EMISSION");
			return val;
		}

		private static void ConfigureEyeTexture(Texture2D? texture)
		{
			if (!((Object)(object)texture == (Object)null))
			{
				((Texture)texture).wrapMode = (TextureWrapMode)0;
				((Texture)texture).wrapModeU = (TextureWrapMode)0;
				((Texture)texture).wrapModeV = (TextureWrapMode)0;
				((Texture)texture).filterMode = (FilterMode)1;
			}
		}

		private static void SetTexture(Material material, string property, Texture texture)
		{
			if (material.HasProperty(property))
			{
				material.SetTexture(property, texture);
			}
		}

		private static void SetTextureScale(Material material, string property, Vector2 scale)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (material.HasProperty(property))
			{
				material.SetTextureScale(property, scale);
			}
		}

		private static void SetTextureOffset(Material material, string property, Vector2 offset)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (material.HasProperty(property))
			{
				material.SetTextureOffset(property, offset);
			}
		}

		private static void SetColor(Material material, string property, Color color)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (material.HasProperty(property))
			{
				material.SetColor(property, color);
			}
		}

		private static void SetFloat(Material material, string property, float value)
		{
			if (material.HasProperty(property))
			{
				material.SetFloat(property, value);
			}
		}

		private void EnsureVisibleFallbackBody()
		{
			//IL_0049: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			if (PingoEnemyPlugin.ForceVisibleFallbackBody && !((Object)(object)((Component)this).transform.Find("PingoVisibleFallbackBody") != (Object)null))
			{
				GameObject obj = GameObject.CreatePrimitive((PrimitiveType)1);
				((Object)obj).name = "PingoVisibleFallbackBody";
				obj.transform.SetParent(((Component)this).transform, false);
				obj.transform.localPosition = Vector3.up * (PingoEnemyPlugin.MinimumVisualHeight * 0.5f);
				obj.transform.localRotation = Quaternion.identity;
				obj.transform.localScale = new Vector3(0.75f, PingoEnemyPlugin.MinimumVisualHeight * 0.5f, 0.75f);
				Collider component = obj.GetComponent<Collider>();
				if ((Object)(object)component != (Object)null)
				{
					Object.Destroy((Object)(object)component);
				}
				Renderer component2 = obj.GetComponent<Renderer>();
				if ((Object)(object)component2 != (Object)null)
				{
					Material val = new Material(Shader.Find("HDRP/Lit") ?? Shader.Find("Standard"));
					val.color = new Color(0.1f, 0.8f, 0.25f, 1f);
					component2.material = val;
					component2.enabled = true;
				}
				PingoEnemyPlugin.Log.LogInfo((object)$"Added visible fallback body to Pingo. height={PingoEnemyPlugin.MinimumVisualHeight:0.00}; root={((Component)this).transform.position}.");
			}
		}

		private IEnumerator NormalizeVisualsAfterSpawn()
		{
			yield return null;
			Renderer[] renderers = ((Component)this).GetComponentsInChildren<Renderer>(true);
			if (renderers.Length == 0)
			{
				PingoEnemyPlugin.Log.LogWarning((object)"Pingo spawned with no renderers. The model will not be visible.");
				yield break;
			}
			Bounds bounds = renderers[0].bounds;
			for (int i = 1; i < renderers.Length; i++)
			{
				((Bounds)(ref bounds)).Encapsulate(renderers[i].bounds);
			}
			PingoEnemyPlugin.Log.LogInfo((object)$"Pingo visual bounds before normalize: center={((Bounds)(ref bounds)).center}; size={((Bounds)(ref bounds)).size}; rootScale={((Component)this).transform.localScale}.");
			if (((Bounds)(ref bounds)).size.y > 0.01f && ((Bounds)(ref bounds)).size.y < PingoEnemyPlugin.MinimumVisualHeight)
			{
				float scaleFactor = PingoEnemyPlugin.MinimumVisualHeight / ((Bounds)(ref bounds)).size.y;
				Transform transform = ((Component)this).transform;
				transform.localScale *= scaleFactor;
				yield return null;
				bounds = renderers[0].bounds;
				for (int j = 1; j < renderers.Length; j++)
				{
					((Bounds)(ref bounds)).Encapsulate(renderers[j].bounds);
				}
				PingoEnemyPlugin.Log.LogInfo((object)$"Scaled Pingo visual by {scaleFactor:0.00} to meet minimum height {PingoEnemyPlugin.MinimumVisualHeight:0.00}. New size={((Bounds)(ref bounds)).size}; rootScale={((Component)this).transform.localScale}.");
			}
			float y = ((Component)this).transform.position.y;
			if (!(((Bounds)(ref bounds)).min.y < y - 0.05f))
			{
				yield break;
			}
			float num = y - ((Bounds)(ref bounds)).min.y;
			Renderer[] array = renderers;
			for (int k = 0; k < array.Length; k++)
			{
				Transform transform2 = ((Component)array[k]).transform;
				if (!((Object)(object)transform2 == (Object)(object)((Component)this).transform))
				{
					transform2.position += Vector3.up * num;
				}
			}
			PingoEnemyPlugin.Log.LogInfo((object)$"Lifted Pingo visual renderers by {num:0.00} so the model is not below the floor.");
		}

		public override void DoAIInterval()
		{
			((EnemyAI)this).DoAIInterval();
			StopMoving();
		}

		public override void Update()
		{
			((EnemyAI)this).Update();
			StopMoving();
			if (!((NetworkBehaviour)this).IsOwner || (Object)(object)source == (Object)null || (Object)(object)PingoEnemyPlugin.PingoClip == (Object)null)
			{
				return;
			}
			aliveForSeconds += Time.deltaTime;
			UpdateNearPlayerVolumeTimer();
			if (Time.time < nextNoiseAt)
			{
				return;
			}
			float num = CalculateNoiseInterval();
			bool num2 = nearPlayerSeconds >= 60f;
			bool flag = false;
			if (num2)
			{
				num = Mathf.Max(0.1f, num - accumulatedIntervalReduction);
				accumulatedIntervalReduction += 0.1f;
				if (num <= 0.101f)
				{
					minimumIntervalPlayCount++;
					if (minimumIntervalPlayCount >= 30)
					{
						flag = true;
					}
				}
				else
				{
					minimumIntervalPlayCount = 0;
				}
			}
			else
			{
				accumulatedIntervalReduction = 0f;
				minimumIntervalPlayCount = 0;
			}
			nextNoiseAt = Time.time + num;
			PlayPingoClientRpc(Mathf.Clamp01(1f - num / 16f), CalculateNearVolumeScale(), num, nearPlayerSeconds);
			if (flag)
			{
				accumulatedIntervalReduction = 0f;
				minimumIntervalPlayCount = 0;
				PingoEnemyPlugin.Log.LogInfo((object)"Pingo overlap loop played 30 times at minimum interval; resetting interval ramp.");
			}
		}

		public override void HitEnemy(int force = 1, PlayerControllerB? playerWhoHit = null, bool playHitSFX = false, int hitID = -1)
		{
		}

		[ClientRpc]
		private void PlayPingoClientRpc(float intensity, float volumeScale, float nextInterval, float nearbySeconds)
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)source == (Object)null) && !((Object)(object)PingoEnemyPlugin.PingoClip == (Object)null))
			{
				source.pitch = Random.Range(0.94f, 1.06f);
				source.volume = 1f;
				float num = Mathf.Lerp(0.45f, 1f, intensity) * Mathf.Max(1f, volumeScale);
				source.PlayOneShot(PingoEnemyPlugin.PingoClip, num);
				PingoEnemyPlugin.Log.LogInfo((object)$"Pingo played sound. intensity={intensity:0.00}; volumeScale={volumeScale:0.00}; finalVolume={num:0.00}; nextInterval={nextInterval:0.00}; nearbySeconds={nearbySeconds:0.0}; position={((Component)this).transform.position}.");
			}
		}

		private void UpdateNearPlayerVolumeTimer()
		{
			if (AnyPlayerWithinDistance(18f))
			{
				nearPlayerSeconds += Time.deltaTime;
			}
		}

		private void EnsureRuntimeScanNode()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_004f: 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_0069: 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)
			ScanNodeProperties val = ((Component)this).GetComponentInChildren<ScanNodeProperties>(true);
			GameObject val2;
			if ((Object)(object)val == (Object)null)
			{
				val2 = new GameObject("PingoScanNode");
				val2.transform.SetParent(((Component)this).transform, false);
				val = val2.AddComponent<ScanNodeProperties>();
			}
			else
			{
				val2 = ((Component)val).gameObject;
			}
			((Object)val2).name = "PingoScanNode";
			val2.transform.localPosition = Vector3.up * 1.15f;
			val2.transform.localRotation = Quaternion.identity;
			val2.transform.localScale = Vector3.one;
			int num = LayerMask.NameToLayer("ScanNode");
			if (num >= 0)
			{
				val2.layer = num;
			}
			SphereCollider obj = val2.GetComponent<SphereCollider>() ?? val2.AddComponent<SphereCollider>();
			((Collider)obj).isTrigger = true;
			obj.radius = 1.25f;
			val.headerText = "Pingo";
			val.subText = "Enemy";
			val.nodeType = 1;
			val.creatureScanID = -1;
			val.minRange = 1;
			val.maxRange = 80;
			val.requiresLineOfSight = false;
		}

		private float CalculateNearVolumeScale()
		{
			float num = Mathf.Floor(nearPlayerSeconds / 10f);
			return 1f + num * 0.1f;
		}

		private float CalculateNoiseInterval()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Clamp01(aliveForSeconds / 600f);
			float num2 = Mathf.Lerp(16f, 4f, num);
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead)
				{
					float num3 = Vector3.Distance(((Component)val).transform.position, ((Component)this).transform.position);
					if (num3 <= 18f)
					{
						num2 *= 0.45f;
					}
					if (num3 <= 35f && PlayerIsLookingAtPingo(val))
					{
						num2 *= 0.55f;
					}
				}
			}
			return Mathf.Max(1.25f, num2);
		}

		private bool AnyPlayerWithinDistance(float maxDistance)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)StartOfRound.Instance == (Object)null || StartOfRound.Instance.allPlayerScripts == null)
			{
				return false;
			}
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (!((Object)(object)val == (Object)null) && val.isPlayerControlled && !val.isPlayerDead && Vector3.Distance(((Component)val).transform.position, ((Component)this).transform.position) <= maxDistance)
				{
					return true;
				}
			}
			return false;
		}

		private bool PlayerIsLookingAtPingo(PlayerControllerB player)
		{
			//IL_0018: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			Camera gameplayCamera = player.gameplayCamera;
			if ((Object)(object)gameplayCamera == (Object)null)
			{
				return false;
			}
			Vector3 val = ((Component)this).transform.position + Vector3.up - ((Component)gameplayCamera).transform.position;
			Vector3 normalized = ((Vector3)(ref val)).normalized;
			return Vector3.Dot(((Component)gameplayCamera).transform.forward, normalized) > 0.75f;
		}

		private void StopMoving()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)base.agent == (Object)null))
			{
				base.agent.speed = 0f;
				base.agent.velocity = Vector3.zero;
				base.agent.isStopped = true;
			}
		}
	}
	[HarmonyPatch(typeof(StartOfRound))]
	internal static class StartOfRoundPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch("StartGame")]
		private static void StartGamePostfix()
		{
			PingoEnemyPlugin.ResetForcedSpawnForNewRound();
		}

		[HarmonyPostfix]
		[HarmonyPatch("OnShipLandedMiscEvents")]
		private static void OnShipLandedMiscEventsPostfix()
		{
			PingoEnemyPlugin.Log.LogInfo((object)"StartOfRound.OnShipLandedMiscEvents fired; checking forced Pingo spawn.");
			PingoEnemyPlugin.TryForceSpawnAfterLanding("OnShipLandedMiscEvents");
		}
	}
}