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 Nutcracker Fire Warning v1.0.6
BepInEx/plugins/NutcrackerFireWarning/NutcrackerFireWarning.dll
Decompiled 5 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("NutcrackerFireWarning.Tests")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("NutcrackerFireWarning")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("NutcrackerFireWarning")] [assembly: AssemblyTitle("NutcrackerFireWarning")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace NutcrackerShotUI { internal readonly struct NutcrackerCombatState { public readonly bool AimingGun; public readonly bool ReloadingGun; public readonly float TimeSinceFiringGun; public readonly float AimDuration; public NutcrackerCombatState(bool aimingGun, bool reloadingGun, float timeSinceFiringGun, float aimDuration) { AimingGun = aimingGun; ReloadingGun = reloadingGun; TimeSinceFiringGun = timeSinceFiringGun; AimDuration = aimDuration; } } internal static class NutcrackerCombatStateReader { private static readonly FieldRef<NutcrackerEnemyAI, bool> AimingGunRef = CreateFieldRef<bool>("aimingGun"); private static readonly FieldRef<NutcrackerEnemyAI, bool> ReloadingGunRef = CreateFieldRef<bool>("reloadingGun"); private static readonly FieldRef<NutcrackerEnemyAI, float> TimeSinceFiringGunRef = CreateFieldRef<float>("timeSinceFiringGun"); public static bool TryRead(NutcrackerEnemyAI nutcracker, out NutcrackerCombatState combatState) { combatState = default(NutcrackerCombatState); if ((Object)(object)nutcracker == (Object)null || AimingGunRef == null || ReloadingGunRef == null || TimeSinceFiringGunRef == null) { return false; } combatState = new NutcrackerCombatState(AimingGunRef.Invoke(nutcracker), ReloadingGunRef.Invoke(nutcracker), TimeSinceFiringGunRef.Invoke(nutcracker), GetAimDuration(nutcracker)); return true; } public static float GetAimDuration(NutcrackerEnemyAI nutcracker) { if (((EnemyAI)nutcracker).enemyHP <= 1) { return 0.5f; } ShotgunItem gun = nutcracker.gun; if ((Object)(object)gun != (Object)null && gun.shellsLoaded == 1) { return 1.3f; } return 1.75f; } private static FieldRef<NutcrackerEnemyAI, T> CreateFieldRef<T>(string fieldName) { try { return AccessTools.FieldRefAccess<NutcrackerEnemyAI, T>(fieldName); } catch { return null; } } } internal static class NutcrackerConfigLanguageDetector { internal const string ChineseProjectGuid = "cn.codex.v81testchn"; private const string ChineseProjectFolderName = "V81TestChn"; private const string ChineseProjectDllName = "V81TestChn.dll"; public static bool ShouldUseChineseConfig() { try { if (Chainloader.PluginInfos != null && Chainloader.PluginInfos.ContainsKey("cn.codex.v81testchn")) { return true; } } catch { } return ShouldUseChineseConfig(null, Paths.PluginPath); } internal static bool ShouldUseChineseConfig(IEnumerable<string> pluginGuids, string pluginRoot) { if (ContainsChineseProjectGuid(pluginGuids)) { return true; } return ContainsChineseProjectDll(pluginRoot); } private static bool ContainsChineseProjectGuid(IEnumerable<string> pluginGuids) { if (pluginGuids == null) { return false; } foreach (string pluginGuid in pluginGuids) { if (string.Equals(pluginGuid, "cn.codex.v81testchn", StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static bool ContainsChineseProjectDll(string pluginRoot) { if (string.IsNullOrWhiteSpace(pluginRoot) || !Directory.Exists(pluginRoot)) { return false; } if (File.Exists(Path.Combine(pluginRoot, "V81TestChn", "V81TestChn.dll")) || File.Exists(Path.Combine(pluginRoot, "V81TestChn.dll"))) { return true; } try { foreach (string item in Directory.EnumerateFiles(pluginRoot, "V81TestChn.dll", SearchOption.AllDirectories)) { if (string.Equals(Path.GetFileName(item), "V81TestChn.dll", StringComparison.OrdinalIgnoreCase)) { return true; } } } catch { return false; } return false; } } internal sealed class NutcrackerModelFireWindowOutline { private sealed class MeshOutlineClone { private readonly Renderer source; private readonly GameObject cloneObject; private readonly MeshFilter cloneFilter; private readonly MeshRenderer cloneMeshRenderer; private readonly SkinnedMeshRenderer cloneSkinnedRenderer; private readonly SkinnedMeshRenderer skinnedSource; private readonly Mesh sourceSkinnedMesh; private readonly Mesh sourceStaticMesh; private readonly Mesh expandedMesh; private readonly Material[] outlineMaterials; private readonly Vector3 sourceLocalScale; private readonly bool canExpandMesh; private float lastOutlineWidth = -1f; private MeshOutlineClone(Renderer source, GameObject cloneObject, MeshFilter cloneFilter, MeshRenderer cloneMeshRenderer, SkinnedMeshRenderer cloneSkinnedRenderer, SkinnedMeshRenderer skinnedSource, Mesh sourceSkinnedMesh, Mesh sourceStaticMesh, Mesh expandedMesh, Material[] outlineMaterials, bool canExpandMesh) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) this.source = source; this.cloneObject = cloneObject; this.cloneFilter = cloneFilter; this.cloneMeshRenderer = cloneMeshRenderer; this.cloneSkinnedRenderer = cloneSkinnedRenderer; this.skinnedSource = skinnedSource; this.sourceSkinnedMesh = sourceSkinnedMesh; this.sourceStaticMesh = sourceStaticMesh; this.expandedMesh = expandedMesh; this.outlineMaterials = outlineMaterials; this.canExpandMesh = canExpandMesh; sourceLocalScale = ((Component)source).transform.localScale; } public static MeshOutlineClone TryCreate(Renderer source, Material material) { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Expected O, but got Unknown //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null || (Object)(object)material == (Object)null) { return null; } Mesh val = null; Mesh val2 = null; Mesh val3 = null; Mesh val4 = null; bool flag = false; SkinnedMeshRenderer val5 = (SkinnedMeshRenderer)(object)((source is SkinnedMeshRenderer) ? source : null); if ((Object)(object)val5 != (Object)null) { if ((Object)(object)val5.sharedMesh == (Object)null) { return null; } val4 = val5.sharedMesh; flag = val4.isReadable; if (flag) { val2 = Object.Instantiate<Mesh>(val4); ((Object)val2).name = ((Object)source).name + "_NutcrackerOutlineExpandedMesh"; val2.MarkDynamic(); val = val2; } else { val = val4; } } else { MeshFilter component = ((Component)source).GetComponent<MeshFilter>(); if ((Object)(object)component == (Object)null || (Object)(object)component.sharedMesh == (Object)null) { return null; } val3 = component.sharedMesh; flag = val3.isReadable; if (flag) { val2 = Object.Instantiate<Mesh>(val3); ((Object)val2).name = ((Object)source).name + "_NutcrackerOutlineExpandedMesh"; val = val2; } else { val = val3; } } GameObject val6 = new GameObject(((Object)source).name + "_NutcrackerMeshOutline"); ((Object)val6).hideFlags = (HideFlags)61; val6.layer = ((Component)source).gameObject.layer; val6.transform.SetParent(((Component)source).transform.parent, false); MeshFilter val7 = null; MeshRenderer val8 = null; SkinnedMeshRenderer val9 = null; Material[] sharedMaterials = CreateOutlineMaterials(source.sharedMaterials, material); if ((Object)(object)val5 != (Object)null) { val9 = val6.AddComponent<SkinnedMeshRenderer>(); val9.sharedMesh = val; ((Renderer)val9).sharedMaterials = sharedMaterials; val9.bones = val5.bones; val9.rootBone = val5.rootBone; val9.quality = val5.quality; val9.updateWhenOffscreen = true; ((Renderer)val9).localBounds = ExpandBounds(((Renderer)val5).localBounds, 0.25f); ConfigureCloneRenderer((Renderer)(object)val9); } else { val7 = val6.AddComponent<MeshFilter>(); val8 = val6.AddComponent<MeshRenderer>(); val7.sharedMesh = val; ((Renderer)val8).sharedMaterials = sharedMaterials; ConfigureCloneRenderer((Renderer)(object)val8); } val6.SetActive(false); MeshOutlineClone meshOutlineClone = new MeshOutlineClone(source, val6, val7, val8, val9, val5, val4, val3, val2, sharedMaterials, flag); meshOutlineClone.CopyTransform(1f); return meshOutlineClone; } public bool Update(Color color, float outlineWidth, float scale) { //IL_0103: 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_00a8: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null || (Object)(object)cloneObject == (Object)null || !source.enabled) { SetVisible(visible: false); return false; } if ((Object)(object)skinnedSource != (Object)null) { if (canExpandMesh && (Object)(object)expandedMesh != (Object)null && !Mathf.Approximately(lastOutlineWidth, outlineWidth)) { ExpandMeshAlongNormals(sourceSkinnedMesh, expandedMesh, outlineWidth); cloneSkinnedRenderer.sharedMesh = expandedMesh; ((Renderer)cloneSkinnedRenderer).localBounds = ExpandBounds(((Renderer)skinnedSource).localBounds, outlineWidth + 0.25f); } } else if (canExpandMesh && (Object)(object)expandedMesh != (Object)null && !Mathf.Approximately(lastOutlineWidth, outlineWidth)) { ExpandMeshAlongNormals(sourceStaticMesh, expandedMesh, outlineWidth); cloneFilter.sharedMesh = expandedMesh; } lastOutlineWidth = outlineWidth; SetMaterialsColor(color); CopyTransform(scale); SetVisible(visible: true); return true; } public string GetShaderSummary() { if (outlineMaterials == null || outlineMaterials.Length == 0 || (Object)(object)outlineMaterials[0] == (Object)null || (Object)(object)outlineMaterials[0].shader == (Object)null) { return "none"; } return ((Object)outlineMaterials[0].shader).name; } public void SetVisible(bool visible) { if ((Object)(object)cloneObject != (Object)null && cloneObject.activeSelf != visible) { cloneObject.SetActive(visible); } } public void Dispose() { if ((Object)(object)expandedMesh != (Object)null) { Object.Destroy((Object)(object)expandedMesh); } if (outlineMaterials != null) { for (int i = 0; i < outlineMaterials.Length; i++) { if ((Object)(object)outlineMaterials[i] != (Object)null) { Object.Destroy((Object)(object)outlineMaterials[i]); } } } if ((Object)(object)cloneObject != (Object)null) { Object.Destroy((Object)(object)cloneObject); } } private void CopyTransform(float scale) { //IL_0019: 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_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)source).transform; Transform transform2 = cloneObject.transform; transform2.localPosition = transform.localPosition; transform2.localRotation = transform.localRotation; transform2.localScale = sourceLocalScale * scale; } private void SetMaterialsColor(Color color) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (outlineMaterials != null) { for (int i = 0; i < outlineMaterials.Length; i++) { SetMaterialColor(outlineMaterials[i], color); } } } private static void ConfigureCloneRenderer(Renderer renderer) { renderer.shadowCastingMode = (ShadowCastingMode)0; renderer.receiveShadows = false; renderer.allowOcclusionWhenDynamic = false; renderer.forceRenderingOff = false; renderer.enabled = true; } private static Bounds ExpandBounds(Bounds bounds, float amount) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) ((Bounds)(ref bounds)).Expand(amount); return bounds; } private static void ExpandMeshAlongNormals(Mesh sourceMesh, Mesh targetMesh, float outlineWidth) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)sourceMesh == (Object)null) && !((Object)(object)targetMesh == (Object)null)) { Vector3[] vertices = sourceMesh.vertices; Vector3[] normals = sourceMesh.normals; if (normals == null || normals.Length != vertices.Length) { sourceMesh.RecalculateNormals(); normals = sourceMesh.normals; } Vector3[] array = (Vector3[])(object)new Vector3[vertices.Length]; for (int i = 0; i < vertices.Length; i++) { Vector3 val = ((normals.Length > i) ? ((Vector3)(ref normals[i])).normalized : Vector3.zero); array[i] = vertices[i] + val * outlineWidth; } targetMesh.Clear(); targetMesh.vertices = array; targetMesh.normals = normals; targetMesh.tangents = sourceMesh.tangents; targetMesh.uv = sourceMesh.uv; targetMesh.uv2 = sourceMesh.uv2; targetMesh.colors = sourceMesh.colors; targetMesh.subMeshCount = sourceMesh.subMeshCount; for (int j = 0; j < sourceMesh.subMeshCount; j++) { targetMesh.SetTriangles(sourceMesh.GetTriangles(j), j); } Bounds bounds = sourceMesh.bounds; ((Bounds)(ref bounds)).Expand(outlineWidth * 2f + 0.02f); targetMesh.bounds = bounds; } } private static Material[] CreateOutlineMaterials(Material[] sourceMaterials, Material fallbackMaterial) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) int num = ((sourceMaterials != null) ? sourceMaterials.Length : 0); num = Mathf.Max(1, num); Material[] array = (Material[])(object)new Material[num]; for (int i = 0; i < array.Length; i++) { Material val = ((sourceMaterials != null && i < sourceMaterials.Length) ? sourceMaterials[i] : null); Material val2 = (((Object)(object)val != (Object)null) ? Object.Instantiate<Material>(val) : Object.Instantiate<Material>(fallbackMaterial)); ((Object)val2).name = (((Object)(object)val != (Object)null) ? ((Object)val).name : "Fallback") + "_NutcrackerOutlineMaterial"; ((Object)val2).hideFlags = (HideFlags)61; ConfigureOutlineMaterial(val2); SetMaterialColor(val2, new Color(1f, 0.02f, 0.02f, 0.86f)); array[i] = val2; } return array; } } private sealed class SourceRendererPulse { private static readonly int BaseColorId = Shader.PropertyToID("_BaseColor"); private static readonly int ColorId = Shader.PropertyToID("_Color"); private static readonly int UnlitColorId = Shader.PropertyToID("_UnlitColor"); private static readonly int EmissionColorId = Shader.PropertyToID("_EmissionColor"); private static readonly int EmissiveColorId = Shader.PropertyToID("_EmissiveColor"); private static readonly int EmissiveColorLdrId = Shader.PropertyToID("_EmissiveColorLDR"); private readonly Renderer renderer; private readonly MaterialPropertyBlock originalBlock = new MaterialPropertyBlock(); private readonly MaterialPropertyBlock pulseBlock = new MaterialPropertyBlock(); private bool capturedOriginalBlock; private bool hadOriginalBlock; private bool active; private SourceRendererPulse(Renderer renderer) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown this.renderer = renderer; } public static SourceRendererPulse TryCreate(Renderer renderer) { if ((Object)(object)renderer == (Object)null) { return null; } return new SourceRendererPulse(renderer); } public bool Apply(Color color, float emissionMultiplier) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_008b: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)renderer == (Object)null || !renderer.enabled) { Restore(); return false; } CaptureOriginalBlockIfNeeded(); renderer.GetPropertyBlock(pulseBlock); Color val = default(Color); ((Color)(ref val))..ctor(color.r * emissionMultiplier, color.g * emissionMultiplier, color.b * emissionMultiplier, 1f); pulseBlock.SetColor(BaseColorId, color); pulseBlock.SetColor(ColorId, color); pulseBlock.SetColor(UnlitColorId, color); pulseBlock.SetColor(EmissionColorId, val); pulseBlock.SetColor(EmissiveColorId, val); pulseBlock.SetColor(EmissiveColorLdrId, val); renderer.SetPropertyBlock(pulseBlock); active = true; return true; } public void Restore() { if (!((Object)(object)renderer == (Object)null) && active) { if (hadOriginalBlock) { renderer.SetPropertyBlock(originalBlock); } else { renderer.SetPropertyBlock((MaterialPropertyBlock)null); } active = false; } } public void Dispose() { Restore(); } private void CaptureOriginalBlockIfNeeded() { if (!capturedOriginalBlock) { renderer.GetPropertyBlock(originalBlock); hadOriginalBlock = !originalBlock.isEmpty; capturedOriginalBlock = true; } } } private const float ScreenPadding = 18f; private const float MinScreenWidth = 72f; private const float MinScreenHeight = 120f; private readonly NutcrackerEnemyAI nutcracker; private readonly Material meshMaterial; private readonly List<MeshOutlineClone> meshClones = new List<MeshOutlineClone>(); private readonly List<SourceRendererPulse> sourcePulses = new List<SourceRendererPulse>(); private readonly List<Renderer> eligibleRenderers = new List<Renderer>(); private GameObject screenRoot; private Canvas screenCanvas; private RectTransform screenCanvasRect; private RectTransform screenBoxRect; private Image screenTop; private Image screenBottom; private Image screenLeft; private Image screenRight; private Renderer[] cachedRenderers; private float nextRendererRefreshTime; private bool meshBuilt; private bool loggedMeshVisible; private bool loggedScreenVisible; public NutcrackerModelFireWindowOutline(NutcrackerEnemyAI nutcracker, Transform parent) { this.nutcracker = nutcracker; meshMaterial = CreateMeshMaterial(); } public void Dispose() { ClearMeshClones(); if ((Object)(object)meshMaterial != (Object)null) { Object.Destroy((Object)(object)meshMaterial); } if ((Object)(object)screenRoot != (Object)null) { Object.Destroy((Object)(object)screenRoot); } } public void Update(ModelWarningPhase phase) { if (phase == ModelWarningPhase.None || (Object)(object)nutcracker == (Object)null) { loggedMeshVisible = false; loggedScreenVisible = false; SetMeshVisible(visible: false); SetScreenVisible(visible: false); return; } bool flag = IsModelStateTintEnabled(); bool flag2 = flag && UpdateStateTint(phase); if (phase != ModelWarningPhase.FireWindow || !IsModelFireWindowEnabled()) { loggedScreenVisible = false; SetCloneShellVisible(visible: false); SetScreenVisible(visible: false); if (!flag2) { RestoreSourcePulses(); } } else if (GetMode() == ModelOutlineMode.ScreenBox) { SetCloneShellVisible(visible: false); if (!flag2) { RestoreSourcePulses(); } UpdateScreenBox(); } else { SetScreenVisible(visible: false); UpdateMeshSilhouette(!flag); } } private void UpdateMeshSilhouette(bool includeSourcePulse) { //IL_0050: 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) //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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) EnsureMeshClones(); if (meshClones.Count == 0 && (!includeSourcePulse || sourcePulses.Count == 0)) { SetCloneShellVisible(visible: false); if (includeSourcePulse) { RestoreSourcePulses(); } return; } float pulseAlpha = GetPulseAlpha(); float pulseIntensity = GetPulseIntensity(); Color color = Color.Lerp(new Color(1f, 0.02f, 0.02f, pulseAlpha), new Color(1f, 1f, 1f, pulseAlpha), Mathf.PingPong(Time.time * 10f, 1f)); SetMaterialColor(meshMaterial, color, pulseIntensity); float num = Mathf.Max(0.01f, NutcrackerShotConfig.ModelOutlineWidth.Value); float num2 = Mathf.Max(1f, NutcrackerShotConfig.MeshOutlineScale.Value); int num3 = 0; int num4 = 0; if (includeSourcePulse) { for (int i = 0; i < sourcePulses.Count; i++) { if (sourcePulses[i].Apply(color, pulseIntensity)) { num4++; } } } for (int j = 0; j < meshClones.Count; j++) { if (meshClones[j].Update(color, num, num2)) { num3++; } } if (num3 == 0 && num4 == 0) { SetCloneShellVisible(visible: false); if (includeSourcePulse) { RestoreSourcePulses(); } } else if (IsDebugLoggingEnabled() && !loggedMeshVisible) { loggedMeshVisible = true; string text = ((meshClones.Count > 0) ? meshClones[0].GetShaderSummary() : "none"); Plugin.Log.LogInfo((object)$"Nutcracker #{((EnemyAI)nutcracker).thisEnemyIndex} mesh silhouette outline visible. CloneRenderers={num3} SourcePulses={num4} Width={num:0.000} Scale={num2:0.000} Shader={text}"); } } private void EnsureMeshClones() { if (meshBuilt) { return; } meshBuilt = true; RefreshRenderersIfNeeded(force: true); if (cachedRenderers == null) { return; } for (int i = 0; i < eligibleRenderers.Count; i++) { Renderer val = eligibleRenderers[i]; if (ShouldUseCloneShell()) { MeshOutlineClone meshOutlineClone = MeshOutlineClone.TryCreate(val, meshMaterial); if (meshOutlineClone != null) { meshClones.Add(meshOutlineClone); } } if (ShouldUseSourcePulse() || IsModelStateTintEnabled()) { SourceRendererPulse sourceRendererPulse = SourceRendererPulse.TryCreate(val); if (sourceRendererPulse != null) { sourcePulses.Add(sourceRendererPulse); } } } if (IsDebugLoggingEnabled()) { Plugin.Log.LogInfo((object)$"Nutcracker #{((EnemyAI)nutcracker).thisEnemyIndex} built mesh silhouette outline clones: {meshClones.Count}, source pulses: {sourcePulses.Count}"); } if (ShouldDumpModelAudit()) { DumpModelAudit(); } } private bool UpdateStateTint(ModelWarningPhase phase) { //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_003f: Unknown result type (might be due to invalid IL or missing references) EnsureMeshClones(); if (sourcePulses.Count == 0) { return false; } Color stateTintColor = GetStateTintColor(phase); float emissionMultiplier = ((phase == ModelWarningPhase.FireWindow) ? GetFireWindowTintIntensity() : GetChaseTintIntensity()); int num = 0; for (int i = 0; i < sourcePulses.Count; i++) { if (sourcePulses[i].Apply(stateTintColor, emissionMultiplier)) { num++; } } if (IsDebugLoggingEnabled() && num > 0 && !loggedMeshVisible) { loggedMeshVisible = true; Plugin.Log.LogInfo((object)$"Nutcracker #{((EnemyAI)nutcracker).thisEnemyIndex} model state tint visible. Phase={phase} SourcePulses={num}"); } return num > 0; } private void DumpModelAudit() { if (cachedRenderers == null) { return; } Plugin.Log.LogInfo((object)$"Nutcracker #{((EnemyAI)nutcracker).thisEnemyIndex} model audit: renderers={cachedRenderers.Length}, outlineClones={meshClones.Count}"); for (int i = 0; i < cachedRenderers.Length; i++) { Renderer val = cachedRenderers[i]; if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).GetComponentInParent<NutcrackerShotIndicator>() != (Object)null) { continue; } string text = "none"; int num = 0; int num2 = 0; SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)(object)((val is SkinnedMeshRenderer) ? val : null); if ((Object)(object)val2 != (Object)null && (Object)(object)val2.sharedMesh != (Object)null) { text = ((Object)val2.sharedMesh).name; num = val2.sharedMesh.vertexCount; num2 = val2.sharedMesh.subMeshCount; } else { MeshFilter component = ((Component)val).GetComponent<MeshFilter>(); if ((Object)(object)component != (Object)null && (Object)(object)component.sharedMesh != (Object)null) { text = ((Object)component.sharedMesh).name; num = component.sharedMesh.vertexCount; num2 = component.sharedMesh.subMeshCount; } } Plugin.Log.LogInfo((object)$"Nutcracker model audit renderer[{i}]: type={GetRendererTypeName(val)}, eligible={IsModelOutlineRenderer(val)}, path={GetPath(((Component)val).transform)}, mesh={text}, vertices={num}, subMeshes={num2}, materials={val.sharedMaterials.Length}"); } } private void ClearMeshClones() { for (int i = 0; i < meshClones.Count; i++) { meshClones[i].Dispose(); } meshClones.Clear(); for (int j = 0; j < sourcePulses.Count; j++) { sourcePulses[j].Dispose(); } sourcePulses.Clear(); meshBuilt = false; } private void SetMeshVisible(bool visible) { SetCloneShellVisible(visible); if (!visible) { RestoreSourcePulses(); } } private void SetCloneShellVisible(bool visible) { for (int i = 0; i < meshClones.Count; i++) { meshClones[i].SetVisible(visible); } } private void RestoreSourcePulses() { for (int i = 0; i < sourcePulses.Count; i++) { sourcePulses[i].Restore(); } } private void UpdateScreenBox() { //IL_002e: 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_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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: 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_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) EnsureScreenBox(); if (!TryGetBounds(out var bounds)) { loggedScreenVisible = false; SetScreenVisible(visible: false); return; } Camera activeCamera = GetActiveCamera(); if ((Object)(object)activeCamera == (Object)null || !TryProjectBounds(bounds, activeCamera, out var screenRect)) { loggedScreenVisible = false; SetScreenVisible(visible: false); return; } screenCanvasRect.sizeDelta = new Vector2((float)Screen.width, (float)Screen.height); float num = Mathf.Max(72f, ((Rect)(ref screenRect)).width + 36f); float num2 = Mathf.Max(120f, ((Rect)(ref screenRect)).height + 36f); Vector2 center = ((Rect)(ref screenRect)).center; screenBoxRect.anchoredPosition = new Vector2(center.x - (float)Screen.width * 0.5f, center.y - (float)Screen.height * 0.5f); screenBoxRect.sizeDelta = new Vector2(num, num2); float thickness = Mathf.Max(3f, NutcrackerShotConfig.ModelOutlineWidth.Value * 120f); SetStripThickness(((Graphic)screenTop).rectTransform, thickness, horizontal: true); SetStripThickness(((Graphic)screenBottom).rectTransform, thickness, horizontal: true); SetStripThickness(((Graphic)screenLeft).rectTransform, thickness, horizontal: false); SetStripThickness(((Graphic)screenRight).rectTransform, thickness, horizontal: false); Color color = Color.Lerp(new Color(1f, 0.02f, 0.02f, 0.96f), Color.white, Mathf.PingPong(Time.unscaledTime * 10f, 1f)); ((Graphic)screenTop).color = color; ((Graphic)screenBottom).color = color; ((Graphic)screenLeft).color = color; ((Graphic)screenRight).color = color; SetScreenVisible(visible: true); if (IsDebugLoggingEnabled() && !loggedScreenVisible) { loggedScreenVisible = true; Plugin.Log.LogInfo((object)$"Nutcracker #{((EnemyAI)nutcracker).thisEnemyIndex} screen outline visible. Rect={num:0}x{num2:0} Center={center.x:0},{center.y:0}"); } } private void EnsureScreenBox() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)screenRoot != (Object)null)) { screenRoot = new GameObject("NutcrackerFireWindowScreenOutline"); ((Object)screenRoot).hideFlags = (HideFlags)61; screenCanvas = screenRoot.AddComponent<Canvas>(); screenCanvas.renderMode = (RenderMode)0; screenCanvas.sortingOrder = 2500; screenCanvasRect = ((Component)screenCanvas).GetComponent<RectTransform>(); GameObject val = new GameObject("ProjectedModelBox"); val.transform.SetParent(screenRoot.transform, false); screenBoxRect = val.AddComponent<RectTransform>(); screenBoxRect.anchorMin = new Vector2(0.5f, 0.5f); screenBoxRect.anchorMax = new Vector2(0.5f, 0.5f); screenBoxRect.pivot = new Vector2(0.5f, 0.5f); screenTop = CreateStrip("Top", (Transform)(object)screenBoxRect); screenBottom = CreateStrip("Bottom", (Transform)(object)screenBoxRect); screenLeft = CreateStrip("Left", (Transform)(object)screenBoxRect); screenRight = CreateStrip("Right", (Transform)(object)screenBoxRect); AnchorHorizontal(((Graphic)screenTop).rectTransform, 1f); AnchorHorizontal(((Graphic)screenBottom).rectTransform, 0f); AnchorVertical(((Graphic)screenLeft).rectTransform, 0f); AnchorVertical(((Graphic)screenRight).rectTransform, 1f); SetScreenVisible(visible: false); } } private static Material CreateMeshMaterial() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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_0052: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_0077: Expected O, but got Unknown Material val = new Material(Shader.Find("HDRP/Unlit") ?? Shader.Find("Hidden/Internal-Colored") ?? Shader.Find("Sprites/Default") ?? Shader.Find("UI/Default")) { name = "NutcrackerMeshSilhouetteOutlineMaterial", hideFlags = (HideFlags)61 }; ConfigureOutlineMaterial(val); SetMaterialColor(val, new Color(1f, 0.02f, 0.02f, 0.86f)); return val; } private static void ConfigureOutlineMaterial(Material material) { if (!((Object)(object)material == (Object)null)) { material.renderQueue = 5500; material.SetOverrideTag("RenderType", "Transparent"); TrySetInt(material, "_Cull", 0); TrySetInt(material, "_CullMode", 0); TrySetInt(material, "_CullModeForward", 0); TrySetInt(material, "_SrcBlend", 5); TrySetInt(material, "_DstBlend", 10); TrySetInt(material, "_AlphaSrcBlend", 5); TrySetInt(material, "_AlphaDstBlend", 10); TrySetInt(material, "_ZWrite", 0); TrySetInt(material, "_ZTest", 8); TrySetInt(material, "_ZTestTransparent", 8); TrySetInt(material, "_ZTestDepthEqualForOpaque", 8); TrySetFloat(material, "_SurfaceType", 1f); TrySetFloat(material, "_BlendMode", 0f); TrySetFloat(material, "_AlphaCutoffEnable", 0f); TrySetFloat(material, "_EnableFogOnTransparent", 0f); TrySetFloat(material, "_UseEmissiveIntensity", 1f); TrySetFloat(material, "_EmissiveIntensity", 2.5f); material.EnableKeyword("_SURFACE_TYPE_TRANSPARENT"); material.EnableKeyword("_BLENDMODE_ALPHA"); } } private static void SetMaterialColor(Material material, Color color, float emissionMultiplier = 3f) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)material == (Object)null)) { if (material.HasProperty("_BaseColor")) { material.SetColor("_BaseColor", color); } if (material.HasProperty("_UnlitColor")) { material.SetColor("_UnlitColor", color); } if (material.HasProperty("_Color")) { material.SetColor("_Color", color); } Color val = default(Color); ((Color)(ref val))..ctor(color.r * emissionMultiplier, color.g * emissionMultiplier, color.b * emissionMultiplier, color.a); if (material.HasProperty("_EmissionColor")) { material.SetColor("_EmissionColor", val); } if (material.HasProperty("_EmissiveColor")) { material.SetColor("_EmissiveColor", val); } if (material.HasProperty("_EmissiveColorLDR")) { material.SetColor("_EmissiveColorLDR", val); } } } private static void TrySetInt(Material material, string property, int value) { if ((Object)(object)material != (Object)null && material.HasProperty(property)) { material.SetInt(property, value); } } private static void TrySetFloat(Material material, string property, float value) { if ((Object)(object)material != (Object)null && material.HasProperty(property)) { material.SetFloat(property, value); } } private static Image CreateStrip(string name, Transform parent) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(parent, false); Image obj = val.AddComponent<Image>(); ((Graphic)obj).raycastTarget = false; ((Graphic)obj).color = Color.clear; return obj; } private static void AnchorHorizontal(RectTransform strip, float y) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) strip.anchorMin = new Vector2(0f, y); strip.anchorMax = new Vector2(1f, y); strip.pivot = new Vector2(0.5f, 0.5f); strip.anchoredPosition = Vector2.zero; } private static void AnchorVertical(RectTransform strip, float x) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) strip.anchorMin = new Vector2(x, 0f); strip.anchorMax = new Vector2(x, 1f); strip.pivot = new Vector2(0.5f, 0.5f); strip.anchoredPosition = Vector2.zero; } private static void SetStripThickness(RectTransform strip, float thickness, bool horizontal) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) strip.sizeDelta = (horizontal ? new Vector2(0f, thickness) : new Vector2(thickness, 0f)); } private static bool TryProjectBounds(Bounds bounds, Camera camera, out Rect screenRect) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: 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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: 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_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: 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_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) Vector3 min = ((Bounds)(ref bounds)).min; Vector3 max = ((Bounds)(ref bounds)).max; float minX = float.MaxValue; float minY = float.MaxValue; float maxX = float.MinValue; float maxY = float.MinValue; bool anyVisible = false; ProjectCorner(camera, new Vector3(min.x, min.y, min.z), ref anyVisible, ref minX, ref minY, ref maxX, ref maxY); ProjectCorner(camera, new Vector3(max.x, min.y, min.z), ref anyVisible, ref minX, ref minY, ref maxX, ref maxY); ProjectCorner(camera, new Vector3(min.x, max.y, min.z), ref anyVisible, ref minX, ref minY, ref maxX, ref maxY); ProjectCorner(camera, new Vector3(max.x, max.y, min.z), ref anyVisible, ref minX, ref minY, ref maxX, ref maxY); ProjectCorner(camera, new Vector3(min.x, min.y, max.z), ref anyVisible, ref minX, ref minY, ref maxX, ref maxY); ProjectCorner(camera, new Vector3(max.x, min.y, max.z), ref anyVisible, ref minX, ref minY, ref maxX, ref maxY); ProjectCorner(camera, new Vector3(min.x, max.y, max.z), ref anyVisible, ref minX, ref minY, ref maxX, ref maxY); ProjectCorner(camera, new Vector3(max.x, max.y, max.z), ref anyVisible, ref minX, ref minY, ref maxX, ref maxY); if (!anyVisible) { screenRect = default(Rect); return false; } minX = Mathf.Clamp(minX, 0f, (float)Screen.width); maxX = Mathf.Clamp(maxX, 0f, (float)Screen.width); minY = Mathf.Clamp(minY, 0f, (float)Screen.height); maxY = Mathf.Clamp(maxY, 0f, (float)Screen.height); if (maxX <= minX || maxY <= minY) { screenRect = default(Rect); return false; } screenRect = Rect.MinMaxRect(minX, minY, maxX, maxY); return true; } private static void ProjectCorner(Camera camera, Vector3 corner, ref bool anyVisible, ref float minX, ref float minY, ref float maxX, ref float maxY) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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) //IL_004f: Unknown result type (might be due to invalid IL or missing references) Vector3 val = camera.WorldToScreenPoint(corner); if (!(val.z <= 0.01f)) { anyVisible = true; minX = Mathf.Min(minX, val.x); minY = Mathf.Min(minY, val.y); maxX = Mathf.Max(maxX, val.x); maxY = Mathf.Max(maxY, val.y); } } private bool TryGetBounds(out Bounds bounds) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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) RefreshRenderersIfNeeded(force: false); bounds = default(Bounds); bool flag = false; if (cachedRenderers == null) { return false; } for (int i = 0; i < eligibleRenderers.Count; i++) { Renderer val = eligibleRenderers[i]; if (!((Object)(object)val == (Object)null) && val.enabled) { if (!flag) { bounds = val.bounds; flag = true; } else { ((Bounds)(ref bounds)).Encapsulate(val.bounds); } } } return flag; } private void RefreshRenderersIfNeeded(bool force) { if (!force && cachedRenderers != null && Time.time < nextRendererRefreshTime) { return; } cachedRenderers = ((Component)nutcracker).GetComponentsInChildren<Renderer>(false); eligibleRenderers.Clear(); for (int i = 0; i < cachedRenderers.Length; i++) { Renderer val = cachedRenderers[i]; if (IsModelOutlineRenderer(val)) { eligibleRenderers.Add(val); } } nextRendererRefreshTime = Time.time + 1f; } private static Camera GetActiveCamera() { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null) { return val.gameplayCamera; } return Camera.main; } private void SetScreenVisible(bool visible) { if ((Object)(object)screenCanvas != (Object)null && ((Behaviour)screenCanvas).enabled != visible) { ((Behaviour)screenCanvas).enabled = visible; } } private static ModelOutlineMode GetMode() { if (NutcrackerShotConfig.ModelOutlineMode != null) { return NutcrackerShotConfig.ModelOutlineMode.Value; } return ModelOutlineMode.MeshSilhouette; } private static ModelPulseMode GetPulseMode() { if (NutcrackerShotConfig.ModelPulseMode != null) { return NutcrackerShotConfig.ModelPulseMode.Value; } return ModelPulseMode.SourcePulse; } private static bool ShouldUseSourcePulse() { ModelPulseMode pulseMode = GetPulseMode(); if (pulseMode != 0) { return pulseMode == ModelPulseMode.Both; } return true; } private static bool ShouldUseCloneShell() { ModelPulseMode pulseMode = GetPulseMode(); if (pulseMode != ModelPulseMode.CloneShell) { return pulseMode == ModelPulseMode.Both; } return true; } private static bool IsModelFireWindowEnabled() { if (NutcrackerShotConfig.EnableModelOutlineFireWindow != null) { return NutcrackerShotConfig.EnableModelOutlineFireWindow.Value; } return true; } private static bool IsModelStateTintEnabled() { if (NutcrackerShotConfig.EnableModelStateTint != null) { return NutcrackerShotConfig.EnableModelStateTint.Value; } return false; } private static Color GetStateTintColor(ModelWarningPhase phase) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (phase == ModelWarningPhase.FireWindow) { return new Color(1f, 0.02f, 0.02f, GetFireWindowTintAlpha()); } return new Color(1f, 1f, 1f, GetChaseTintAlpha()); } private static float GetPulseIntensity() { if (NutcrackerShotConfig.ModelPulseIntensity != null) { return Mathf.Clamp(NutcrackerShotConfig.ModelPulseIntensity.Value, 0f, 12f); } return 4f; } private static float GetPulseAlpha() { if (NutcrackerShotConfig.ModelPulseAlpha != null) { return Mathf.Clamp01(NutcrackerShotConfig.ModelPulseAlpha.Value); } return 0.92f; } private static float GetChaseTintAlpha() { if (NutcrackerShotConfig.ModelChaseTintAlpha != null) { return Mathf.Clamp01(NutcrackerShotConfig.ModelChaseTintAlpha.Value); } return 0.58f; } private static float GetChaseTintIntensity() { if (NutcrackerShotConfig.ModelChaseTintIntensity != null) { return Mathf.Clamp(NutcrackerShotConfig.ModelChaseTintIntensity.Value, 0f, 12f); } return 1.8f; } private static float GetFireWindowTintAlpha() { if (NutcrackerShotConfig.ModelFireWindowTintAlpha != null) { return Mathf.Clamp01(NutcrackerShotConfig.ModelFireWindowTintAlpha.Value); } return 0.96f; } private static float GetFireWindowTintIntensity() { if (NutcrackerShotConfig.ModelFireWindowTintIntensity != null) { return Mathf.Clamp(NutcrackerShotConfig.ModelFireWindowTintIntensity.Value, 0f, 12f); } return 5f; } private static bool IsDebugLoggingEnabled() { if (NutcrackerShotConfig.EnableDebugLogs != null) { return NutcrackerShotConfig.EnableDebugLogs.Value; } return false; } private static bool ShouldDumpModelAudit() { if (NutcrackerShotConfig.DumpModelAudit != null) { return NutcrackerShotConfig.DumpModelAudit.Value; } return false; } private static bool IsModelOutlineRenderer(Renderer renderer) { if ((Object)(object)renderer == (Object)null || renderer is LineRenderer || renderer is ParticleSystemRenderer) { return false; } if ((Object)(object)((Component)renderer).GetComponentInParent<NutcrackerShotIndicator>() != (Object)null) { return false; } string path = GetPath(((Component)renderer).transform); if (ContainsAny(path, "MapDot", "ScanNode", "BloodSpurtParticle", "Collider", "NutcrackerMeshOutline", "NutcrackerFireWindow")) { return false; } SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null); if ((Object)(object)val != (Object)null) { string value = (((Object)(object)val.sharedMesh == (Object)null) ? string.Empty : ((Object)val.sharedMesh).name); if (!ContainsAny(path, "LOD0")) { return ContainsAny(value, "LOD0"); } return true; } MeshFilter component = ((Component)renderer).GetComponent<MeshFilter>(); if ((Object)(object)component == (Object)null || (Object)(object)component.sharedMesh == (Object)null) { return false; } string name = ((Object)component.sharedMesh).name; if (!ContainsAny(path, "Gun", "Shotgun")) { return ContainsAny(name, "Gun", "Shotgun"); } return true; } private static string GetRendererTypeName(Renderer renderer) { if (renderer is SkinnedMeshRenderer) { return "SkinnedMeshRenderer"; } if (renderer is MeshRenderer) { return "MeshRenderer"; } if (renderer is ParticleSystemRenderer) { return "ParticleSystemRenderer"; } if (renderer is LineRenderer) { return "LineRenderer"; } return "Renderer"; } private static bool ContainsAny(string value, params string[] fragments) { if (string.IsNullOrEmpty(value)) { return false; } for (int i = 0; i < fragments.Length; i++) { if (value.IndexOf(fragments[i], StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } private static string GetPath(Transform transform) { if ((Object)(object)transform == (Object)null) { return string.Empty; } string text = ((Object)transform).name; Transform parent = transform.parent; while ((Object)(object)parent != (Object)null) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } } internal enum ModelWarningPhase { None, Chase, FireWindow } internal static class NutcrackerModelWarningPhaseSelector { public static ModelWarningPhase Select(bool modelEnabled, bool stateTintEnabled, bool fireWindowActive, bool chaseActive) { if (!modelEnabled) { return ModelWarningPhase.None; } if (fireWindowActive) { return ModelWarningPhase.FireWindow; } if (!(stateTintEnabled && chaseActive)) { return ModelWarningPhase.None; } return ModelWarningPhase.Chase; } } [HarmonyPatch(typeof(NutcrackerEnemyAI))] internal static class NutcrackerPatches { [HarmonyPostfix] [HarmonyPatch("Update")] private static void UpdatePostfix(NutcrackerEnemyAI __instance) { if (NutcrackerShotConfig.IsModEnabled() && !((Object)(object)__instance == (Object)null) && !((EnemyAI)__instance).isEnemyDead && NutcrackerCombatStateReader.TryRead(__instance, out var combatState)) { NutcrackerShotIndicator nutcrackerShotIndicator = NutcrackerShotIndicator.GetExisting(__instance); if ((Object)(object)nutcrackerShotIndicator == (Object)null && NutcrackerShotIndicator.ShouldCreateForCombatState(__instance, combatState)) { nutcrackerShotIndicator = NutcrackerShotIndicator.For(__instance); } nutcrackerShotIndicator?.ObserveCombatState(combatState); } } [HarmonyPostfix] [HarmonyPatch("AimGunClientRpc")] private static void AimGunClientRpcPostfix(NutcrackerEnemyAI __instance) { if (NutcrackerShotConfig.IsModEnabled()) { NutcrackerShotIndicator.For(__instance)?.BeginAim(NutcrackerCombatStateReader.GetAimDuration(__instance)); } } [HarmonyPostfix] [HarmonyPatch("FireGunClientRpc")] private static void FireGunClientRpcPostfix(NutcrackerEnemyAI __instance) { if (NutcrackerShotConfig.IsModEnabled()) { NutcrackerShotIndicator.For(__instance)?.MarkFired(); } } [HarmonyPostfix] [HarmonyPatch("ReloadGunClientRpc")] private static void ReloadGunClientRpcPostfix(NutcrackerEnemyAI __instance) { if (NutcrackerShotConfig.IsModEnabled()) { NutcrackerShotIndicator.For(__instance)?.BeginReload(1.74f); } } [HarmonyPostfix] [HarmonyPatch("KillEnemy")] private static void KillEnemyPostfix(NutcrackerEnemyAI __instance) { NutcrackerShotIndicator.Remove(__instance); } } internal static class NutcrackerShotConfig { internal const bool DefaultEnableModelOutlineFireWindow = false; internal const bool DefaultEnableModelStateTint = true; private static bool useChineseDescriptions; public static ConfigEntry<bool> EnableMod { get; private set; } public static ConfigEntry<bool> EnableUiFireWindow { get; private set; } public static ConfigEntry<bool> EnableModelOutlineFireWindow { get; private set; } public static ConfigEntry<bool> EnableModelStateTint { get; private set; } public static ConfigEntry<float> ModelOutlineWidth { get; private set; } public static ConfigEntry<ModelOutlineMode> ModelOutlineMode { get; private set; } public static ConfigEntry<ModelPulseMode> ModelPulseMode { get; private set; } public static ConfigEntry<float> MeshOutlineScale { get; private set; } public static ConfigEntry<float> ModelPulseIntensity { get; private set; } public static ConfigEntry<float> ModelPulseAlpha { get; private set; } public static ConfigEntry<float> ModelChaseTintAlpha { get; private set; } public static ConfigEntry<float> ModelChaseTintIntensity { get; private set; } public static ConfigEntry<float> ModelFireWindowTintAlpha { get; private set; } public static ConfigEntry<float> ModelFireWindowTintIntensity { get; private set; } public static ConfigEntry<float> ModelWarningMaxDistance { get; private set; } public static ConfigEntry<bool> ModelWarningRequireCameraVisible { get; private set; } public static ConfigEntry<float> FireWindowSeconds { get; private set; } public static ConfigEntry<float> PreAimMaxDistance { get; private set; } public static ConfigEntry<float> MonitorActiveScanInterval { get; private set; } public static ConfigEntry<float> MonitorIdleScanInterval { get; private set; } public static ConfigEntry<bool> EnableDebugLogs { get; private set; } public static ConfigEntry<bool> DumpModelAudit { get; private set; } public static void Bind(ConfigFile config) { useChineseDescriptions = NutcrackerConfigLanguageDetector.ShouldUseChineseConfig(); EnableMod = config.Bind<bool>("General", "EnableMod", true, Description("Enable Nutcracker Fire Warning. When disabled, the plugin does not create or update warning UI, model warnings, or fallback scans.", "启用 Nutcracker Fire Warning 总开关。关闭后不会创建或更新预警 UI、模型预警或备用扫描。")); EnableUiFireWindow = config.Bind<bool>("Warnings", "EnableUiFireWindow", true, Description("Show the world-space side warning bar, including countdown, FIRE pulse, reload bar, and pre-aim danger bar.", "显示胡桃身侧的世界空间竖条 UI,包括倒计时、FIRE 脉冲、换弹条和预瞄危险提示。")); EnableModelOutlineFireWindow = config.Bind<bool>("Warnings", "EnableModelOutlineFireWindow", false, Description("Enable the extra fire-window-only model overlay: red-white pulse, clone shell, or screen box depending on ModelOutlineMode and ModelPulseMode. Usually keep this disabled when EnableModelStateTint is enabled.", "启用仅在最终开火窗口出现的额外模型叠加预警:根据 ModelOutlineMode 和 ModelPulseMode 显示红白脉冲、克隆外壳或屏幕框。启用 EnableModelStateTint 时通常保持关闭。")); EnableModelStateTint = config.Bind<bool>("Warnings", "EnableModelStateTint", true, Description("Enable the recommended model state warning: white while the Nutcracker is chasing a target, red during the final fire window.", "启用推荐的模型状态预警:胡桃追击目标时模型变白,进入最终开火窗口时模型变红。")); ModelOutlineWidth = config.Bind<float>("Warnings", "ModelOutlineWidth", 0.075f, Description("Line width for ScreenBox mode. In MeshSilhouette mode this is the normal-expanded edge thickness.", "ScreenBox 模式的线宽;MeshSilhouette 模式下为法线扩展轮廓厚度。")); ModelOutlineMode = config.Bind<ModelOutlineMode>("Warnings", "ModelOutlineMode", NutcrackerShotUI.ModelOutlineMode.MeshSilhouette, Description("Model outline style. MeshSilhouette follows the Nutcracker model; ScreenBox draws a reliable HUD fallback rectangle.", "模型轮廓样式。MeshSilhouette 贴合胡桃模型;ScreenBox 绘制稳定的屏幕空间备用矩形。")); ModelPulseMode = config.Bind<ModelPulseMode>("Warnings", "ModelPulseMode", NutcrackerShotUI.ModelPulseMode.SourcePulse, Description("Implementation used by the extra fire-window overlay in MeshSilhouette mode. SourcePulse recolors the original model; CloneShell adds a cloned shell; Both enables both.", "MeshSilhouette 模式下额外开火窗口叠加层的实现方式。SourcePulse 重染原模型;CloneShell 添加克隆外壳;Both 同时启用两者。")); MeshOutlineScale = config.Bind<float>("Warnings", "MeshOutlineScale", 1f, Description("Extra transform scale for MeshSilhouette outline clones. Keep this at 1 for the most exact model edge.", "MeshSilhouette 克隆轮廓的额外缩放。保持 1 可获得最贴近模型边缘的效果。")); ModelPulseIntensity = config.Bind<float>("Warnings", "ModelPulseIntensity", 4f, Description("Emission multiplier for the extra red-white fire-window overlay.", "额外红白开火窗口叠加层的发光强度倍率。")); ModelPulseAlpha = config.Bind<float>("Warnings", "ModelPulseAlpha", 0.92f, Description("Alpha used by the extra red-white fire-window overlay.", "额外红白开火窗口叠加层的不透明度。")); ModelChaseTintAlpha = config.Bind<float>("Warnings", "ModelChaseTintAlpha", 0.58f, Description("Alpha used when the Nutcracker model is tinted white while chasing.", "胡桃追击目标时白色模型染色的不透明度。")); ModelChaseTintIntensity = config.Bind<float>("Warnings", "ModelChaseTintIntensity", 1.8f, Description("Emission multiplier for the white chase-state model tint.", "胡桃追击目标时白色模型染色的发光强度倍率。")); ModelFireWindowTintAlpha = config.Bind<float>("Warnings", "ModelFireWindowTintAlpha", 0.96f, Description("Alpha used when the Nutcracker model is tinted red during the final fire window.", "胡桃进入最终开火窗口时红色模型染色的不透明度。")); ModelFireWindowTintIntensity = config.Bind<float>("Warnings", "ModelFireWindowTintIntensity", 5f, Description("Emission multiplier for the red fire-window model tint.", "胡桃进入最终开火窗口时红色模型染色的发光强度倍率。")); ModelWarningMaxDistance = config.Bind<float>("Warnings", "ModelWarningMaxDistance", 45f, Description("Maximum local-player distance for model warnings. Set to 0 or lower to disable distance filtering.", "模型预警相对本地玩家的最大显示距离。设为 0 或更低可关闭距离过滤。")); ModelWarningRequireCameraVisible = config.Bind<bool>("Warnings", "ModelWarningRequireCameraVisible", false, Description("Only show model warnings when the Nutcracker is inside the local camera viewport.", "仅当胡桃位于本地摄像机视野内时显示模型预警。")); FireWindowSeconds = config.Bind<float>("Warnings", "FireWindowSeconds", 0.35f, Description("Seconds before the predicted shot when fire-window warnings activate.", "预测开火前多少秒进入最终开火窗口预警。")); PreAimMaxDistance = config.Bind<float>("Warnings", "PreAimMaxDistance", 30f, Description("Maximum distance for the yellow pre-aim danger bar. Set to 0 or lower to disable the pre-aim bar.", "黄色预瞄危险条的最大距离。设为 0 或更低可关闭预瞄条。")); MonitorActiveScanInterval = config.Bind<float>("Performance", "MonitorActiveScanInterval", 0.5f, Description("Fallback monitor scan interval while Nutcrackers are present.", "场上存在胡桃时,备用监视器的扫描间隔。")); MonitorIdleScanInterval = config.Bind<float>("Performance", "MonitorIdleScanInterval", 2f, Description("Fallback monitor scan interval while no Nutcrackers are present.", "场上没有胡桃时,备用监视器的扫描间隔。")); EnableDebugLogs = config.Bind<bool>("Debug", "EnableDebugLogs", false, Description("Log aim/fire warning events to BepInEx LogOutput.log.", "将瞄准/开火预警事件记录到 BepInEx LogOutput.log。")); DumpModelAudit = config.Bind<bool>("Debug", "DumpModelAudit", false, Description("Log Nutcracker renderer and mesh names/counts once when the model outline is built.", "构建模型轮廓时记录一次胡桃 Renderer 和 Mesh 的名称/数量。")); } public static bool IsModEnabled() { if (EnableMod != null) { return EnableMod.Value; } return true; } private static string Description(string english, string chinese) { if (!useChineseDescriptions) { return english; } return chinese; } } internal enum ModelOutlineMode { MeshSilhouette, ScreenBox } internal enum ModelPulseMode { SourcePulse, CloneShell, Both } internal sealed class NutcrackerShotIndicator : MonoBehaviour { private enum WarningState { Idle, Aiming, Fired, Reloading } private const float BarWidth = 0.09f; private const float BarHeight = 1.35f; private const float SideOffset = 1.08f; private const float HeightOffset = 2.05f; private const float FiredHoldTime = 0.35f; private const float DefaultCriticalAimWindow = 0.35f; private const float PreAimMaxDistance = 30f; private const float PreAimCheckInterval = 0.12f; private const float PreAimAngleDegrees = 35f; private static readonly Color BackgroundColor = new Color(0.02f, 0.02f, 0.02f, 0.55f); private static readonly Color PreAimColor = new Color(1f, 0.72f, 0.12f, 0.55f); private static readonly Color AimColor = new Color(1f, 0.12f, 0.06f, 0.92f); private static readonly Color FiredColor = new Color(1f, 1f, 1f, 0.96f); private static readonly Color ReloadColor = new Color(0.2f, 0.65f, 1f, 0.55f); private static readonly float PreAimMinimumDot = Mathf.Cos(0.61086524f); private static readonly string[] CountdownLabels = CreateCountdownLabels(); private static readonly Dictionary<NutcrackerEnemyAI, NutcrackerShotIndicator> Indicators = new Dictionary<NutcrackerEnemyAI, NutcrackerShotIndicator>(); private NutcrackerEnemyAI nutcracker; private Canvas canvas; private Image background; private Image fill; private Image rim; private Image criticalPulse; private Text countdownText; private NutcrackerModelFireWindowOutline modelOutline; private WarningState state; private float stateStartedAt; private float stateDuration; private float lastPreAimAmount; private float nextPreAimCheckTime; private float cachedPreAimDanger; private bool observedAimingLastFrame; private bool observedReloadingLastFrame; private NutcrackerCombatState lastCombatState; public static NutcrackerShotIndicator For(NutcrackerEnemyAI nutcracker) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (!NutcrackerShotConfig.IsModEnabled() || (Object)(object)nutcracker == (Object)null) { return null; } NutcrackerShotIndicator existing = GetExisting(nutcracker); if ((Object)(object)existing != (Object)null) { return existing; } GameObject val = new GameObject("NutcrackerShotUI"); val.transform.SetParent(((Component)nutcracker).transform, false); existing = val.AddComponent<NutcrackerShotIndicator>(); existing.Initialize(nutcracker); Indicators[nutcracker] = existing; return existing; } public static NutcrackerShotIndicator GetExisting(NutcrackerEnemyAI nutcracker) { if ((Object)(object)nutcracker == (Object)null) { return null; } if (Indicators.TryGetValue(nutcracker, out var value)) { if ((Object)(object)value != (Object)null) { return value; } Indicators.Remove(nutcracker); } return null; } public static bool ShouldCreateForCombatState(NutcrackerEnemyAI nutcracker, NutcrackerCombatState combatState) { if (!NutcrackerShotConfig.IsModEnabled() || (Object)(object)nutcracker == (Object)null || ((EnemyAI)nutcracker).isEnemyDead) { return false; } if (combatState.AimingGun || combatState.ReloadingGun) { return true; } if (IsModelStateTintEnabled() && IsChaseTintCandidate(nutcracker, combatState)) { return true; } if (IsUiFireWindowEnabled() && GetPreAimMaxDistance() > 0f && ((EnemyAI)nutcracker).currentBehaviourStateIndex == 2 && (Object)(object)nutcracker.gun != (Object)null) { return nutcracker.gun.shellsLoaded > 0; } return false; } public static void Remove(NutcrackerEnemyAI nutcracker) { if (!((Object)(object)nutcracker == (Object)null)) { NutcrackerShotIndicator existing = GetExisting(nutcracker); if ((Object)(object)existing != (Object)null) { Indicators.Remove(nutcracker); Object.Destroy((Object)(object)((Component)existing).gameObject); } } } public void BeginAim(float duration) { if (NutcrackerShotConfig.IsModEnabled()) { if (duration <= 0f) { duration = 0.5f; } state = WarningState.Aiming; stateStartedAt = Time.time; stateDuration = duration; SetVisible(visible: true); if (IsDebugLoggingEnabled()) { Plugin.Log.LogInfo((object)$"Nutcracker #{((EnemyAI)nutcracker).thisEnemyIndex} aim warning started. Duration={duration:0.00}s Shells={nutcracker.gun?.shellsLoaded ?? (-1)} HP={((EnemyAI)nutcracker).enemyHP}"); } } } public void MarkFired() { if (NutcrackerShotConfig.IsModEnabled()) { state = WarningState.Fired; stateStartedAt = Time.time; stateDuration = 0.35f; SetVisible(visible: true); if (IsDebugLoggingEnabled()) { Plugin.Log.LogInfo((object)$"Nutcracker #{((EnemyAI)nutcracker).thisEnemyIndex} fired."); } } } public void BeginReload(float duration) { if (NutcrackerShotConfig.IsModEnabled()) { state = WarningState.Reloading; stateStartedAt = Time.time; stateDuration = duration; SetVisible(visible: true); } } public void ObserveCombatState(bool aimingGun, bool reloadingGun, float timeSinceFiringGun, float aimDuration) { if (!NutcrackerShotConfig.IsModEnabled()) { return; } if (aimingGun) { float num = Mathf.Max(0.05f, aimDuration); float num2 = Mathf.Clamp(timeSinceFiringGun, 0f, num); if (!observedAimingLastFrame || state != WarningState.Aiming) { state = WarningState.Aiming; stateDuration = num; stateStartedAt = Time.time - num2; SetVisible(visible: true); if (IsDebugLoggingEnabled()) { Plugin.Log.LogInfo((object)$"Nutcracker #{((EnemyAI)nutcracker).thisEnemyIndex} aim warning observed from Update. Duration={num:0.00}s Elapsed={num2:0.00}s Shells={nutcracker.gun?.shellsLoaded ?? (-1)} HP={((EnemyAI)nutcracker).enemyHP}"); } } } else if (observedAimingLastFrame && state == WarningState.Aiming) { state = WarningState.Idle; SetCriticalPulse(0f); UpdateModelWarningPhase(fireWindowActive: false); } if (reloadingGun && !observedReloadingLastFrame && state != WarningState.Aiming) { BeginReload(1.74f); } observedAimingLastFrame = aimingGun; observedReloadingLastFrame = reloadingGun; } public void ObserveCombatState(NutcrackerCombatState combatState) { lastCombatState = combatState; ObserveCombatState(combatState.AimingGun, combatState.ReloadingGun, combatState.TimeSinceFiringGun, combatState.AimDuration); } private void Initialize(NutcrackerEnemyAI owner) { nutcracker = owner; BuildWorldBar(); modelOutline = new NutcrackerModelFireWindowOutline(owner, ((Component)this).transform); SetVisible(visible: false); } private void OnDestroy() { modelOutline?.Dispose(); if ((Object)(object)nutcracker != (Object)null && Indicators.TryGetValue(nutcracker, out var value) && (Object)(object)value == (Object)(object)this) { Indicators.Remove(nutcracker); } } private void OnDisable() { SetModelWarningPhase(ModelWarningPhase.None); } private void LateUpdate() { if (!NutcrackerShotConfig.IsModEnabled() || (Object)(object)nutcracker == (Object)null || ((EnemyAI)nutcracker).isEnemyDead) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } if (IsUiFireWindowEnabled()) { UpdateTransform(); } UpdateState(); } private void BuildWorldBar() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) canvas = ((Component)this).gameObject.AddComponent<Canvas>(); canvas.renderMode = (RenderMode)2; canvas.sortingOrder = 200; RectTransform component = ((Component)canvas).GetComponent<RectTransform>(); component.sizeDelta = new Vector2(0.09f, 1.35f); ((Transform)component).localScale = Vector3.one; background = CreateImage("Background", ((Component)this).transform, BackgroundColor); RectTransform rectTransform = ((Graphic)background).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = Vector2.zero; fill = CreateImage("Fill", ((Component)background).transform, AimColor); fill.type = (Type)3; fill.fillMethod = (FillMethod)1; fill.fillOrigin = 0; fill.fillAmount = 0f; RectTransform rectTransform2 = ((Graphic)fill).rectTransform; rectTransform2.anchorMin = Vector2.zero; rectTransform2.anchorMax = Vector2.one; rectTransform2.offsetMin = new Vector2(0.012f, 0.012f); rectTransform2.offsetMax = new Vector2(-0.012f, -0.012f); rim = CreateImage("Rim", ((Component)this).transform, new Color(1f, 1f, 1f, 0.26f)); RectTransform rectTransform3 = ((Graphic)rim).rectTransform; rectTransform3.anchorMin = Vector2.zero; rectTransform3.anchorMax = Vector2.one; rectTransform3.offsetMin = Vector2.zero; rectTransform3.offsetMax = Vector2.zero; criticalPulse = CreateImage("CriticalPulse", ((Component)this).transform, new Color(1f, 0f, 0f, 0f)); RectTransform rectTransform4 = ((Graphic)criticalPulse).rectTransform; rectTransform4.anchorMin = Vector2.zero; rectTransform4.anchorMax = Vector2.one; rectTransform4.offsetMin = new Vector2(-0.018f, -0.018f); rectTransform4.offsetMax = new Vector2(0.018f, 0.018f); countdownText = CreateText("Countdown", ((Component)this).transform); RectTransform rectTransform5 = ((Graphic)countdownText).rectTransform; rectTransform5.anchorMin = new Vector2(0.5f, 1f); rectTransform5.anchorMax = new Vector2(0.5f, 1f); rectTransform5.pivot = new Vector2(0.5f, 0f); rectTransform5.sizeDelta = new Vector2(0.58f, 0.22f); rectTransform5.anchoredPosition = new Vector2(0f, 0.08f); } private static Image CreateImage(string name, Transform parent, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(parent, false); Image obj = val.AddComponent<Image>(); ((Graphic)obj).color = color; ((Graphic)obj).raycastTarget = false; return obj; } private static Text CreateText(string name, Transform parent) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.transform.SetParent(parent, false); Text obj = val.AddComponent<Text>(); obj.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); obj.alignment = (TextAnchor)4; ((Graphic)obj).color = new Color(1f, 1f, 1f, 0.95f); obj.fontSize = 42; obj.resizeTextForBestFit = true; obj.resizeTextMinSize = 14; obj.resizeTextMaxSize = 42; ((Graphic)obj).raycastTarget = false; return obj; } private void UpdateTransform() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: 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_0037: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: 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_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)nutcracker).transform; ((Component)this).transform.position = transform.position + transform.right * 1.08f + Vector3.up * 2.05f; Camera activeCamera = GetActiveCamera(); if ((Object)(object)activeCamera != (Object)null) { Vector3 val = ((Component)this).transform.position - ((Component)activeCamera).transform.position; if (((Vector3)(ref val)).sqrMagnitude > 0.001f) { ((Component)this).transform.rotation = Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up); } } } private void UpdateState() { //IL_0034: 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) bool flag = IsUiFireWindowEnabled(); if (!flag) { HideUiElements(); } switch (state) { case WarningState.Aiming: UpdateAimingState(); return; case WarningState.Fired: UpdateTimedState(FiredColor, 1f, hideWhenFinished: true); return; case WarningState.Reloading: UpdateTimedState(ReloadColor, reverse: true, hideWhenFinished: true); return; } if (flag) { UpdatePreAim(); return; } lastPreAimAmount = 0f; UpdateModelWarningPhase(fireWindowActive: false); } private void UpdateTimedState(Color color, float alpha, bool hideWhenFinished) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) UpdateTimedState(color.WithAlpha(alpha), reverse: false, hideWhenFinished); } private void UpdateTimedState(Color color, bool reverse, bool hideWhenFinished) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp01((Time.time - stateStartedAt) / Mathf.Max(0.01f, stateDuration)); if (IsUiFireWindowEnabled()) { ((Graphic)fill).color = color; fill.fillAmount = (reverse ? (1f - num) : num); SetCountdownText(null, Color.white); SetCriticalPulse(0f); } else { HideUiElements(); } UpdateModelWarningPhase(fireWindowActive: false); if (num >= 1f) { state = WarningState.Idle; if (hideWhenFinished) { SetVisible(visible: false); } } } private void UpdateAimingState() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: 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_0146: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) float num = Time.time - stateStartedAt; float num2 = Mathf.Max(0.01f, stateDuration); float num3 = Mathf.Clamp01(num / num2); float num4 = Mathf.Max(0f, num2 - num); float fireWindowSeconds = GetFireWindowSeconds(); float num5 = 1f - Mathf.Clamp01(num4 / fireWindowSeconds); bool flag = IsUiFireWindowEnabled(); if (flag) { fill.fillAmount = num3; ((Graphic)fill).color = Color.Lerp(AimColor, FiredColor, num5 * 0.45f); } else { HideUiElements(); } if (num4 <= fireWindowSeconds) { UpdateModelWarningPhase(fireWindowActive: true); if (flag) { float num6 = 0.45f + Mathf.PingPong(Time.time * 12f, 0.55f); SetCriticalPulse(num6); SetCountdownText("FIRE", Color.Lerp(AimColor, FiredColor, num6)); } } else { UpdateModelWarningPhase(fireWindowActive: false); if (flag) { SetCriticalPulse(0f); SetRemainingCountdown(num4); } } if (num3 >= 1f) { state = WarningState.Idle; UpdateModelWarningPhase(fireWindowActive: false); if (flag) { fill.fillAmount = 1f; SetCountdownText("NOW", FiredColor); SetCriticalPulse(0.85f); } else { SetCountdownText(null, Color.white); SetCriticalPulse(0f); } } } private void UpdatePreAim() { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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_0074: Unknown result type (might be due to invalid IL or missing references) if (Time.time >= nextPreAimCheckTime) { cachedPreAimDanger = GetPreAimDanger(); nextPreAimCheckTime = Time.time + 0.12f; } float num = cachedPreAimDanger; lastPreAimAmount = Mathf.MoveTowards(lastPreAimAmount, num, Time.deltaTime * 4f); if (lastPreAimAmount <= 0.01f) { SetVisible(visible: false); fill.fillAmount = 0f; SetCountdownText(null, Color.white); SetCriticalPulse(0f); UpdateModelWarningPhase(fireWindowActive: false); } else { SetVisible(visible: true); ((Graphic)fill).color = PreAimColor; fill.fillAmount = Mathf.Clamp01(lastPreAimAmount); SetCountdownText("!", PreAimColor.WithAlpha(0.92f)); SetCriticalPulse(0f); UpdateModelWarningPhase(fireWindowActive: false); } } private float GetPreAimDanger() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_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_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: 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_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) if (((EnemyAI)nutcracker).currentBehaviourStateIndex != 2 || (Object)(object)nutcracker.gun == (Object)null || nutcracker.gun.shellsLoaded <= 0) { return 0f; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val == (Object)null || val.isPlayerDead || (Object)(object)val.gameplayCamera == (Object)null) { return 0f; } Vector3 position = ((Component)val.gameplayCamera).transform.position; Vector3 position2 = nutcracker.gun.shotgunRayPoint.position; Vector3 val2 = position - position2; float preAimMaxDistance = GetPreAimMaxDistance(); if (preAimMaxDistance <= 0f || ((Vector3)(ref val2)).sqrMagnitude > preAimMaxDistance * preAimMaxDistance) { return 0f; } float magnitude = ((Vector3)(ref val2)).magnitude; if (magnitude <= 0.001f) { return 0f; } Vector3 val3 = val2 / magnitude; float num = Vector3.Dot(nutcracker.gun.shotgunRayPoint.forward, val3); if (num < PreAimMinimumDot) { return 0f; } if (Physics.Linecast(position2, position, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1)) { return 0f; } float num2 = 1f - Mathf.Clamp01(magnitude / preAimMaxDistance); float num3 = Mathf.InverseLerp(PreAimMinimumDot, 1f, num); return Mathf.Clamp01(0.2f + num2 * 0.35f + num3 * 0.45f); } private static Camera GetActiveCamera() { PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; if ((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null) { return val.gameplayCamera; } return Camera.main; } private void SetVisible(bool visible) { bool flag = visible && IsUiFireWindowEnabled(); if ((Object)(object)canvas != (Object)null && ((Behaviour)canvas).enabled != flag) { ((Behaviour)canvas).enabled = flag; } } private void HideUiElements() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) SetVisible(visible: false); if ((Object)(object)fill != (Object)null) { fill.fillAmount = 0f; } SetCountdownText(null, Color.white); SetCriticalPulse(0f); } private void SetCountdownText(string text, Color color) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)countdownText == (Object)null)) { bool flag = !string.IsNullOrEmpty(text); string text2 = text ?? string.Empty; if (((Behaviour)countdownText).enabled != flag) { ((Behaviour)countdownText).enabled = flag; } if (countdownText.text != text2) { countdownText.text = text2; } ((Graphic)countdownText).color = color; } } private void SetRemainingCountdown(float remaining) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Clamp(Mathf.RoundToInt(remaining * 10f), 0, CountdownLabels.Length - 1); SetCountdownText(CountdownLabels[num], AimColor.WithAlpha(0.96f)); } private static string[] CreateCountdownLabels() { string[] array = new string[21]; for (int i = 0; i < array.Length; i++) { array[i] = ((float)i * 0.1f).ToString("0.0"); } return array; } private void SetCriticalPulse(float alpha) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)criticalPulse == (Object)null)) { Color color = ((Graphic)criticalPulse).color; color.a = Mathf.Clamp01(alpha) * 0.55f; ((Graphic)criticalPulse).color = color; } } private void SetModelWarningPhase(ModelWarningPhase phase) { modelOutline?.Update(phase); } private void UpdateModelWarningPhase(bool fireWindowActive) { bool flag = IsAnyModelWarningEnabled(); bool flag2 = IsModelStateTintEnabled(); bool flag3 = flag && ShouldShowModelWarningByDistanceAndCamera(); bool fireWindowActive2 = fireWindowActive && flag3 && (IsModelFireWindowEnabled() || flag2); bool chaseActive = flag3 && flag2 && IsChaseTintCandidate(nutcracker, lastCombatState); SetModelWarningPhase(NutcrackerModelWarningPhaseSelector.Select(flag, flag2, fireWindowActive2, chaseActive)); } private bool ShouldShowModelWarningByDistanceAndCamera() { //IL_008d: 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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0124: 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_0140: Unknown result type (might be due to invalid IL or missing references) if (!IsAnyModelWarningEnabled()) { return false; } PlayerControllerB val = GameNetworkManager.Instance?.localPlayerController; Camera val2 = (((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null) ? val.gameplayCamera : Camera.main); if ((Object)(object)val != (Object)null && val.isPlayerDead) { return false; } float modelWarningMaxDistance = GetModelWarningMaxDistance(); if (modelWarningMaxDistance > 0f) { Vector3 val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).transform.position : (((Object)(object)val != (Object)null) ? ((Component)val).transform.position : Vector3.zero)); if (val3 != Vector3.zero && Vector3.Distance(val3, ((Component)nutcracker).transform.position) > modelWarningMaxDistance) { return false; } } if (NutcrackerShotConfig.ModelWarningRequireCameraVisible != null && NutcrackerShotConfig.ModelWarningRequireCameraVisible.Value) { if ((Object)(object)val2 == (Object)null) { return false; } Vector3 val4 = val2.WorldToViewportPoint(((Component)nutcracker).transform.position + Vector3.up * 1.4f); if (val4.z <= 0f || val4.x < -0.1f || val4.x > 1.1f || val4.y < -0.1f || val4.y > 1.1f) { return false; } } return true; } private static bool IsChaseTintCandidate(NutcrackerEnemyAI nutcracker, NutcrackerCombatState combatState) { if ((Object)(object)nutcracker == (Object)null || ((EnemyAI)nutcracker).isEnemyDead || ((EnemyAI)nutcracker).currentBehaviourStateIndex != 2) { return false; } if (combatState.ReloadingGun || (Object)(object)nutcracker.gun == (Object)null || nutcracker.gun.shellsLoaded <= 0) { return false; } if (combatState.AimingGun) { return true; } if (nutcracker.lastPlayerSeenMoving != -1 && (Object)(object)((EnemyAI)nutcracker).targetPlayer != (Object)null) { return ((EnemyAI)nutcracker).movingTowardsTargetPlayer; } return false; } private static bool IsUiFireWindowEnabled() { if (NutcrackerShotConfig.EnableUiFireWindow != null) { return NutcrackerShotConfig.EnableUiFireWindow.Value; } return true; } private static bool IsModelFireWindowEnabled() { if (NutcrackerShotConfig.EnableModelOutlineFireWindow != null) { return NutcrackerShotConfig.EnableModelOutlineFireWindow.Value; } return true; } private static bool IsModelStateTintEnabled() { if (NutcrackerShotConfig.EnableModelStateTint != null) { return NutcrackerShotConfig.EnableModelStateTint.Value; } return false; } private static bool IsAnyModelWarningEnabled() { if (!IsModelFireWindowEnabled()) { return IsModelStateTintEnabled(); } return true; } private static float GetFireWindowSeconds() { if (NutcrackerShotConfig.FireWindowSeconds != null) { return Mathf.Clamp(NutcrackerShotConfig.FireWindowSeconds.Value, 0.05f, 2f); } return 0.35f; } private static float GetPreAimMaxDistance() { if (NutcrackerShotConfig.PreAimMaxDistance == null) { return 30f; } float value = NutcrackerShotConfig.PreAimMaxDistance.Value; if (!(value <= 0f)) { return Mathf.Clamp(value, 1f, 100f); } return 0f; } private static float GetModelWarningMaxDistance() { if (NutcrackerShotConfig.ModelWarningMaxDistance != null) { return NutcrackerShotConfig.ModelWarningMaxDistance.Value; } return 45f; } private static bool IsDebugLoggingEnabled() { if (NutcrackerShotConfig.EnableDebugLogs != null) { return NutcrackerShotConfig.EnableDebugLogs.Value; } return false; } } internal static class ColorExtensions { public static Color WithAlpha(this Color color, float alpha) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) color.a = alpha; return color; } } internal sealed class NutcrackerShotMonitor : MonoBehaviour { private float nextScanTime; private int lastObservedCount = -1; private void Awake() { Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } private void Update() { if (!NutcrackerShotConfig.IsModEnabled()) { nextScanTime = Time.time + GetConfigInterval(NutcrackerShotConfig.MonitorIdleScanInterval, 2f); } else { if (Time.time < nextScanTime) { return; } NutcrackerEnemyAI[] array = Object.FindObjectsOfType<NutcrackerEnemyAI>(); nextScanTime = Time.time + GetNextScanInterval(array.Length); if (IsDebugLoggingEnabled() && array.Length != lastObservedCount) { lastObservedCount = array.Length; Plugin.Log.LogInfo((object)$"Nutcracker monitor found {array.Length} NutcrackerEnemyAI instance(s)."); } foreach (NutcrackerEnemyAI val in array) { if (!((Object)(object)val == (Object)null) && !((EnemyAI)val).isEnemyDead && NutcrackerCombatStateReader.TryRead(val, out var combatState)) { NutcrackerShotIndicator nutcrackerShotIndicator = NutcrackerShotIndicator.GetExisting(val); if ((Object)(object)nutcrackerShotIndicator == (Object)null && NutcrackerShotIndicator.ShouldCreateForCombatState(val, combatState)) { nutcrackerShotIndicator = NutcrackerShotIndicator.For(val); } nutcrackerShotIndicator?.ObserveCombatState(combatState); } } } } private static float GetNextScanInterval(int observedCount) { return Mathf.Clamp((observedCount > 0) ? GetConfigInterval(NutcrackerShotConfig.MonitorActiveScanInterval, 0.5f) : GetConfigInterval(NutcrackerShotConfig.MonitorIdleScanInterval, 2f), 0.02f, 5f); } private static float GetConfigInterval(ConfigEntry<float> entry, float fallback) { return entry?.Value ?? fallback; } private static bool IsDebugLoggingEnabled() { if (NutcrackerShotConfig.EnableDebugLogs != null) { return NutcrackerShotConfig.EnableDebugLogs.Value; } return false; } } [BepInPlugin("aueser.lethalcompany.nutcrackerfirewarning", "Nutcracker Fire Warning", "1.0.6")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "aueser.lethalcompany.nutcrackerfirewarning"; public const string PluginName = "Nutcracker Fire Warning"; public const string PluginVersion = "1.0.6"; private Harmony harmony; internal static ManualLogSource Log { get; private set; } private void Awake() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; NutcrackerShotConfig.Bind(((BaseUnityPlugin)this).Config); CreateMonitor(); harmony = new Harmony("aueser.lethalcompany.nutcrackerfirewarning"); harmony.PatchAll(typeof(Plugin).Assembly); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. enabled={2}, UI fire window={3}, model outline={4}, debug={5}.", "Nutcracker Fire Warning", "1.0.6", NutcrackerShotConfig.EnableMod.Value, NutcrackerShotConfig.EnableUiFireWindow.Value, NutcrackerShotConfig.EnableModelOutlineFireWindow.Value, NutcrackerShotConfig.EnableDebugLogs.Value)); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } private static void CreateMonitor() { //IL_0005: Unknown result type (might be due to invali