Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of PingoEnemy v1.0.0
BepInEx\plugins\PingoEnemy\PingoEnemy.dll
Decompiled 4 days agousing 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"); } } }