using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; using UnityEngine.VFX; using Y4NGZCompany.Experience.BloodFX; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("DunGen")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")] [assembly: IgnoresAccessChecksTo("EasyTextEffects")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")] [assembly: IgnoresAccessChecksTo("Unity.XR.Management")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Y4NGZBloodFX")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Blood and gore visual effects for Y4NGZ mods")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+6ae9e9f17528b47cbc796b51a05c6f7380dda81b")] [assembly: AssemblyProduct("Y4NGZBloodFX")] [assembly: AssemblyTitle("Y4NGZBloodFX")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Y4NGZBloodFX { [BepInPlugin("com.y4ngz.bloodfx", "BloodVFX", "1.0.0")] public sealed class BloodFXPlugin : BaseUnityPlugin { public const string PluginGuid = "com.y4ngz.bloodfx"; public const string PluginName = "BloodVFX"; public const string PluginVersion = "1.0.0"; private Harmony _harmony; public static BloodFXPlugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; _harmony = new Harmony("com.y4ngz.bloodfx"); BloodFXBootstrap.Initialize(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger, _harmony); Log.LogInfo((object)"BloodVFX 1.0.0 loaded."); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "Y4NGZBloodFX"; public const string PLUGIN_NAME = "Y4NGZBloodFX"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace Y4NGZCompany.Experience.BloodFX { public static class BloodFXAssets { [Serializable] private sealed class BloodFXManifest { public int version; public BloodFXManifestCategories categories; } [Serializable] private sealed class BloodFXManifestCategories { public string[] burst = new string[0]; public string[] chute = new string[0]; public string[] spray = new string[0]; public string[] wound = new string[0]; public string[] wall = new string[0]; public string[] spots = new string[0]; public string[] body = new string[0]; public string[] puddle = new string[0]; public string[] vfx_spike = new string[0]; } internal sealed class PooledParticle { private readonly GameObject _instance; private readonly ParticleSystem[] _systems; private readonly Burst[][] _originalBursts; private readonly float[][] _originalCounts; private readonly MinMaxGradient[] _originalStartColors; private readonly Vector3 _baseScale; internal PooledParticle(GameObject instance) { //IL_0014: 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_0075: 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_0084: 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_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_00f8: 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_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) _instance = instance; _baseScale = instance.transform.localScale; _systems = instance.GetComponentsInChildren(true); _originalBursts = new Burst[_systems.Length][]; _originalCounts = new float[_systems.Length][]; _originalStartColors = (MinMaxGradient[])(object)new MinMaxGradient[_systems.Length]; for (int i = 0; i < _systems.Length; i++) { ParticleSystem val = _systems[i]; CollisionModule collision = val.collision; ((CollisionModule)(ref collision)).enabled = false; EmissionModule emission = val.emission; Burst[] array = (Burst[])(object)new Burst[((EmissionModule)(ref emission)).burstCount]; ((EmissionModule)(ref emission)).GetBursts(array); _originalBursts[i] = array; float[] array2 = new float[array.Length]; for (int j = 0; j < array.Length; j++) { int num = j; MinMaxCurve count = ((Burst)(ref array[j])).count; array2[num] = ((MinMaxCurve)(ref count)).constant; } _originalCounts[i] = array2; MinMaxGradient[] originalStartColors = _originalStartColors; int num2 = i; MainModule main = val.main; originalStartColors[num2] = ((MainModule)(ref main)).startColor; } Light[] componentsInChildren = instance.GetComponentsInChildren(true); for (int k = 0; k < componentsInChildren.Length; k++) { Object.Destroy((Object)(object)componentsInChildren[k]); } ParticleSystemRenderer[] componentsInChildren2 = instance.GetComponentsInChildren(true); foreach (ParticleSystemRenderer obj in componentsInChildren2) { ((Renderer)obj).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)obj).receiveShadows = false; } } internal void Play(Vector3 position, Quaternion rotation, float emissionMultiplier, Color tint, float scale) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0090: 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_00b4: 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) if ((Object)(object)_instance == (Object)null || _systems.Length == 0) { return; } Transform transform = _instance.transform; transform.SetPositionAndRotation(position, rotation); transform.localScale = _baseScale * Mathf.Max(0.01f, scale); bool tinted = BloodFXTint.IsTinted(tint); for (int i = 0; i < _systems.Length; i++) { ParticleSystem val = _systems[i]; if ((Object)(object)val == (Object)null) { continue; } ApplyStartColor(val, i, tinted, tint); Burst[] array = _originalBursts[i]; if (array.Length != 0) { float[] array2 = _originalCounts[i]; EmissionModule emission = val.emission; for (int j = 0; j < array.Length; j++) { Burst val2 = array[j]; ((Burst)(ref val2)).count = new MinMaxCurve(Mathf.Max(1f, array2[j] * emissionMultiplier)); ((EmissionModule)(ref emission)).SetBurst(j, val2); } } } _instance.SetActive(true); ParticleSystem obj = _systems[0]; obj.Clear(true); obj.Play(true); } private void ApplyStartColor(ParticleSystem system, int index, bool tinted, Color tint) { //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_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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_005a: Unknown result type (might be due to invalid IL or missing references) MainModule main = system.main; MinMaxGradient startColor = _originalStartColors[index]; if (!tinted) { ((MainModule)(ref main)).startColor = startColor; return; } float num = (((int)((MinMaxGradient)(ref startColor)).mode == 0) ? ((MinMaxGradient)(ref startColor)).color.a : 1f); ((MainModule)(ref main)).startColor = new MinMaxGradient(new Color(tint.r, tint.g, tint.b, num)); } } internal const string BundleFileName = "y4ngz-bloodfx.bundle"; internal const string ManifestFileName = "y4ngz-bloodfx.manifest.json"; internal const int BurstPoolSize = 4; internal const int SprayPoolSize = 4; private const string PoolRootName = "Y4NGZCompany_BloodFXPool"; private const string DecalRootName = "Y4NGZCompany_BloodFXDecals"; private static bool _loaded; private static AssetBundle _bundle; private static GameObject[] _burstPrefabs = Array.Empty(); private static GameObject[] _chutePrefabs = Array.Empty(); private static GameObject[] _sprayPrefabs = Array.Empty(); private static GameObject[] _woundPrefabs = Array.Empty(); private static GameObject[] _wallPrefabs = Array.Empty(); private static GameObject[] _spotsPrefabs = Array.Empty(); private static GameObject[] _bodyPrefabs = Array.Empty(); private static GameObject[] _puddlePrefabs = Array.Empty(); private static GameObject[] _vfxSpikePrefabs = Array.Empty(); private static Transform _poolRoot; private static Transform _decalRoot; private static PooledParticle[] _burstPool = Array.Empty(); private static PooledParticle[] _sprayPool = Array.Empty(); private static int _burstCursor; private static int _sprayCursor; public static bool IsAvailable { get { EnsureLoaded(); if ((Object)(object)_bundle != (Object)null) { return _wallPrefabs.Length + _spotsPrefabs.Length > 0; } return false; } } internal static GameObject[] ChutePrefabs { get { EnsureLoaded(); return _chutePrefabs; } } internal static GameObject WallPrefab => Pick(_wallPrefabs); internal static GameObject SpotsPrefab => Pick(_spotsPrefabs); internal static GameObject WoundPrefab => Pick(_woundPrefabs); internal static GameObject BodyPrefab => Pick(_bodyPrefabs); internal static GameObject PuddlePrefab => Pick(_puddlePrefabs); public static bool HasBodyPrefabs => _bodyPrefabs.Length != 0; internal static Transform DecalRoot { get { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_decalRoot == (Object)null) { _decalRoot = new GameObject("Y4NGZCompany_BloodFXDecals").transform; } return _decalRoot; } } internal static PooledParticle TakeBurst() { EnsureLoaded(); if (_burstPool.Length == 0) { return null; } PooledParticle result = _burstPool[_burstCursor]; _burstCursor = (_burstCursor + 1) % _burstPool.Length; return result; } internal static PooledParticle TakeSpray() { EnsureLoaded(); if (_sprayPool.Length == 0) { return null; } PooledParticle result = _sprayPool[_sprayCursor]; _sprayCursor = (_sprayCursor + 1) % _sprayPool.Length; return result; } private static GameObject Pick(GameObject[] pool) { EnsureLoaded(); if (pool.Length != 0) { return pool[Random.Range(0, pool.Length)]; } return null; } private static void EnsureLoaded() { if (_loaded) { return; } _loaded = true; if (!TryLoadManifest(out var manifest, out var reason)) { ManualLogSource log = BloodFXBootstrap.Log; if (log != null) { log.LogWarning((object)("[Y4NGZCompany.BloodFX] Manifest 'y4ngz-bloodfx.manifest.json' unavailable (" + reason + "); blood VFX disabled.")); } return; } _bundle = FindLoadedBundle(manifest); if ((Object)(object)_bundle == (Object)null) { string text = ResolveRuntimeAssetPath("y4ngz-bloodfx.bundle"); if (string.IsNullOrWhiteSpace(text)) { ManualLogSource log2 = BloodFXBootstrap.Log; if (log2 != null) { log2.LogWarning((object)"[Y4NGZCompany.BloodFX] Bundle 'y4ngz-bloodfx.bundle' was not found next to the plugin; blood VFX disabled."); } return; } _bundle = AssetBundle.LoadFromFile(text); if ((Object)(object)_bundle == (Object)null) { ManualLogSource log3 = BloodFXBootstrap.Log; if (log3 != null) { log3.LogWarning((object)("[Y4NGZCompany.BloodFX] AssetBundle.LoadFromFile returned null for '" + text + "'; blood VFX disabled.")); } return; } ManualLogSource log4 = BloodFXBootstrap.Log; if (log4 != null) { log4.LogInfo((object)("[Y4NGZCompany.BloodFX] Loaded blood VFX bundle from '" + text + "'.")); } } BloodFXManifestCategories bloodFXManifestCategories = manifest.categories ?? new BloodFXManifestCategories(); _burstPrefabs = LoadCategory(bloodFXManifestCategories.burst, "burst"); _chutePrefabs = LoadCategory(bloodFXManifestCategories.chute, "chute"); _sprayPrefabs = LoadCategory(bloodFXManifestCategories.spray, "spray"); _woundPrefabs = LoadCategory(bloodFXManifestCategories.wound, "wound"); _wallPrefabs = LoadCategory(bloodFXManifestCategories.wall, "wall"); _spotsPrefabs = LoadCategory(bloodFXManifestCategories.spots, "spots"); _bodyPrefabs = LoadCategory(bloodFXManifestCategories.body, "body"); _puddlePrefabs = LoadCategory(bloodFXManifestCategories.puddle, "puddle"); _vfxSpikePrefabs = LoadCategory(bloodFXManifestCategories.vfx_spike, "vfx_spike"); BuildPools(); BloodFXDecalSurfaces.Probe(); ManualLogSource log5 = BloodFXBootstrap.Log; if (log5 != null) { log5.LogInfo((object)("[Y4NGZCompany.BloodFX] Assets ready: " + $"burst={_burstPrefabs.Length}, chute={_chutePrefabs.Length}, " + $"spray={_sprayPrefabs.Length}, wound={_woundPrefabs.Length}, " + $"wall={_wallPrefabs.Length}, spots={_spotsPrefabs.Length}, " + $"body={_bodyPrefabs.Length}, puddle={_puddlePrefabs.Length}, " + $"vfxSpike={_vfxSpikePrefabs.Length}.")); } if (bloodFXManifestCategories.vfx_spike != null && bloodFXManifestCategories.vfx_spike.Length != 0) { BloodFXVfxGraphSpike.Arm((_vfxSpikePrefabs.Length != 0) ? _vfxSpikePrefabs[0] : null, bloodFXManifestCategories.vfx_spike[0]); } } private static void BuildPools() { //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_0021: Expected O, but got Unknown if (_burstPrefabs.Length != 0 || _sprayPrefabs.Length != 0) { GameObject val = new GameObject("Y4NGZCompany_BloodFXPool"); Object.DontDestroyOnLoad((Object)val); _poolRoot = val.transform; _burstPool = BuildPool(_burstPrefabs, 4); _sprayPool = BuildPool(_sprayPrefabs, 4); } } private static PooledParticle[] BuildPool(GameObject[] prefabs, int count) { if (prefabs.Length == 0) { return Array.Empty(); } PooledParticle[] array = new PooledParticle[count]; for (int i = 0; i < count; i++) { GameObject val = prefabs[i % prefabs.Length]; GameObject val2 = Object.Instantiate(val, _poolRoot, false); ((Object)val2).name = ((Object)val).name + "_Pooled" + i; val2.SetActive(false); array[i] = new PooledParticle(val2); } return array; } private static GameObject[] LoadCategory(string[] names, string categoryKey) { if (names == null || names.Length == 0) { return Array.Empty(); } GameObject[] array = (GameObject[])(object)new GameObject[names.Length]; int num = 0; for (int i = 0; i < names.Length; i++) { GameObject val = LoadAssetByName(_bundle, names[i]); if ((Object)(object)val == (Object)null) { ManualLogSource log = BloodFXBootstrap.Log; if (log != null) { log.LogWarning((object)("[Y4NGZCompany.BloodFX] Manifest category '" + categoryKey + "' lists '" + names[i] + "', which the bundle does not contain.")); } } else { array[num++] = val; } } if (num == array.Length) { return array; } GameObject[] array2 = (GameObject[])(object)new GameObject[num]; Array.Copy(array, array2, num); return array2; } private static AssetBundle FindLoadedBundle(BloodFXManifest manifest) { string text = FirstName(manifest); if (string.IsNullOrWhiteSpace(text)) { return null; } foreach (AssetBundle allLoadedAssetBundle in AssetBundle.GetAllLoadedAssetBundles()) { if ((Object)(object)allLoadedAssetBundle == (Object)null) { continue; } try { if (allLoadedAssetBundle.Contains(text) || BundleHasMatchingAsset(allLoadedAssetBundle, text)) { return allLoadedAssetBundle; } } catch { } } return null; } private static string FirstName(BloodFXManifest manifest) { BloodFXManifestCategories bloodFXManifestCategories = manifest?.categories; if (bloodFXManifestCategories == null) { return null; } string[][] array = new string[8][] { bloodFXManifestCategories.burst, bloodFXManifestCategories.chute, bloodFXManifestCategories.spray, bloodFXManifestCategories.wound, bloodFXManifestCategories.wall, bloodFXManifestCategories.spots, bloodFXManifestCategories.body, bloodFXManifestCategories.puddle }; foreach (string[] array2 in array) { if (array2 != null && array2.Length != 0 && !string.IsNullOrWhiteSpace(array2[0])) { return array2[0]; } } return null; } private static bool TryLoadManifest(out BloodFXManifest manifest, out string reason) { manifest = null; reason = string.Empty; try { string text = ResolveRuntimeAssetPath("y4ngz-bloodfx.manifest.json"); if (string.IsNullOrWhiteSpace(text)) { reason = "file='y4ngz-bloodfx.manifest.json' not found next to the plugin"; return false; } manifest = JsonUtility.FromJson(File.ReadAllText(text)); if (manifest == null) { reason = "manifest_json_returned_null"; return false; } if (manifest.categories == null) { reason = "manifest_missing_categories"; return false; } return true; } catch (Exception ex) { reason = ex.GetType().Name + ": " + ex.Message; return false; } } private static string ResolveRuntimeAssetPath(string fileName) { if (string.IsNullOrWhiteSpace(fileName)) { return null; } string text = null; try { string location = typeof(BloodFXAssets).Assembly.Location; if (!string.IsNullOrWhiteSpace(location)) { text = Path.GetDirectoryName(location); } } catch { text = null; } string[] array = new string[8] { (!string.IsNullOrWhiteSpace(text)) ? Path.Combine(text, fileName) : null, Path.Combine(Paths.PluginPath, "y4ngz-BloodVFX", fileName), Path.Combine(Paths.PluginPath, "BloodVFX", fileName), Path.Combine(Paths.PluginPath, "y4ngz-Y4NGZBloodFX", fileName), Path.Combine(Paths.PluginPath, "Y4NGZBloodFX", fileName), Path.Combine(Paths.PluginPath, "y4ngz-Y4NGZCompany", fileName), Path.Combine(Paths.PluginPath, "Y4NGZCompany", fileName), Path.Combine(Paths.PluginPath, fileName) }; foreach (string text2 in array) { if (!string.IsNullOrWhiteSpace(text2) && File.Exists(text2)) { return text2; } } return null; } private static T LoadAssetByName(AssetBundle bundle, string assetName) where T : Object { if ((Object)(object)bundle == (Object)null || string.IsNullOrWhiteSpace(assetName)) { return default(T); } try { T val = bundle.LoadAsset(assetName); if ((Object)(object)val != (Object)null) { return val; } } catch { } string[] bundleAssetNames = GetBundleAssetNames(bundle); for (int i = 0; i < bundleAssetNames.Length; i++) { if (!AssetNameMatches(bundleAssetNames[i], assetName)) { continue; } try { T val2 = bundle.LoadAsset(bundleAssetNames[i]); if ((Object)(object)val2 != (Object)null) { return val2; } } catch { } } return default(T); } private static bool BundleHasMatchingAsset(AssetBundle bundle, string assetName) { string[] bundleAssetNames = GetBundleAssetNames(bundle); for (int i = 0; i < bundleAssetNames.Length; i++) { if (AssetNameMatches(bundleAssetNames[i], assetName)) { return true; } } return false; } private static string[] GetBundleAssetNames(AssetBundle bundle) { try { return ((Object)(object)bundle != (Object)null) ? bundle.GetAllAssetNames() : Array.Empty(); } catch { return Array.Empty(); } } private static bool AssetNameMatches(string bundleAssetName, string requestedName) { if (string.IsNullOrWhiteSpace(bundleAssetName) || string.IsNullOrWhiteSpace(requestedName)) { return false; } string a = Path.GetFileNameWithoutExtension(bundleAssetName).Replace('\\', '/'); string text = Path.GetFileNameWithoutExtension(requestedName).Replace('\\', '/'); if (!string.Equals(a, text, StringComparison.OrdinalIgnoreCase) && !bundleAssetName.EndsWith("/" + requestedName, StringComparison.OrdinalIgnoreCase)) { return bundleAssetName.EndsWith("/" + text, StringComparison.OrdinalIgnoreCase); } return true; } } public static class BloodFXBodyDecals { public readonly struct Profile { public readonly float Radius; public readonly float MinWidth; public readonly float MaxWidth; public readonly float Depth; public readonly float FrontMargin; public readonly float HeavyScale; public Profile(float radius, float minWidth, float maxWidth, float depth, float frontMargin, float heavyScale) { Radius = radius; MinWidth = minWidth; MaxWidth = maxWidth; Depth = depth; FrontMargin = frontMargin; HeavyScale = heavyScale; } } internal static GameObject Place(long channelKey, Transform anchor, Vector3 direction, in Profile profile, bool final, int cap, string label) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: 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_00bf: 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_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: 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) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) cap = Mathf.Max(0, cap); if (cap == 0 || (Object)(object)anchor == (Object)null || !BloodFXAssets.HasBodyPrefabs) { return null; } GameObject bodyPrefab = BloodFXAssets.BodyPrefab; if ((Object)(object)bodyPrefab == (Object)null) { return null; } Vector3 val = (Vector3)((((Vector3)(ref direction)).sqrMagnitude > 0.0001f) ? new Vector3(0f - direction.x, 0f, 0f - direction.z) : Vector3.zero); if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f) { ((Vector3)(ref val))..ctor(Random.Range(-1f, 1f), 0f, Random.Range(-1f, 1f)); } if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); Vector3 val2 = anchor.position + val * profile.Radius + Vector3.up * Random.Range(-0.16f, 0.12f); Quaternion val3 = BloodFXDirector.ProjectAlong(-val) * Quaternion.AngleAxis(Random.Range(0f, 360f), Vector3.forward); GameObject val4 = Object.Instantiate(bodyPrefab, val2, val3, anchor); float num = (final ? (profile.MaxWidth * profile.HeavyScale) : (Random.Range(profile.MinWidth, profile.MaxWidth) * profile.HeavyScale)); DecalLayerEnum val5 = (DecalLayerEnum)(BloodFXDecalSurfaces.BodyDecalLayerIsolationActive ? 128 : 255); BloodFXDirector.ApplyProjectorScale(val4, num, profile.Depth, profile.FrontMargin, val5); BloodFXDecalRegistry.RegisterBodyDecal(channelKey, val4, cap); BloodFXBootstrap.Trace("body decal " + label + " bone='" + ((Object)anchor).name + "' pos=" + BloodFXDirector.Format(val2) + " prefab='" + ((Object)bodyPrefab).name + "' " + $"size={num:0.###}x{num:0.###}x{profile.Depth:0.###} frontMargin={profile.FrontMargin:0.###} " + $"decalLayer={val5} path={BloodFXDecalSurfaces.ActivePathName} " + $"count={BloodFXDecalRegistry.BodyCount(channelKey)}/{cap} " + BloodFXDirector.DescribeProjector(val4) + "."); return val4; } public static GameObject PlaceAt(long channelKey, Transform anchor, Vector3 surface, Vector3 direction, in Profile profile, bool final, int cap, string label, GameObject prefabOverride = null) { //IL_0056: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_0060: 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_0075: 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) //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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) cap = Mathf.Max(0, cap); if (cap == 0 || (Object)(object)anchor == (Object)null) { return null; } GameObject val = (((Object)(object)prefabOverride != (Object)null) ? prefabOverride : BloodFXAssets.BodyPrefab); if ((Object)(object)val == (Object)null) { return null; } Vector3 val2 = ((((Vector3)(ref direction)).sqrMagnitude > 0.0001f) ? (-((Vector3)(ref direction)).normalized) : (surface - anchor.position)); if (((Vector3)(ref val2)).sqrMagnitude <= 0.0001f) { val2 = anchor.forward; } if (((Vector3)(ref val2)).sqrMagnitude <= 0.0001f) { val2 = Vector3.forward; } ((Vector3)(ref val2)).Normalize(); Quaternion val3 = BloodFXDirector.ProjectAlong(-val2) * Quaternion.AngleAxis(Random.Range(0f, 360f), Vector3.forward); GameObject val4 = Object.Instantiate(val, surface, val3, anchor); float num = (final ? (profile.MaxWidth * profile.HeavyScale) : (Random.Range(profile.MinWidth, profile.MaxWidth) * profile.HeavyScale)); BloodFXDirector.ApplyProjectorScale(val4, num, profile.Depth, profile.FrontMargin, (DecalLayerEnum)255); BloodFXDecalRegistry.RegisterBodyDecal(channelKey, val4, cap); BloodFXBootstrap.Trace("body decal " + label + " bone='" + ((Object)anchor).name + "' pos=" + BloodFXDirector.Format(surface) + " prefab='" + ((Object)val).name + "' " + $"size={num:0.###}x{num:0.###}x{profile.Depth:0.###} frontMargin={profile.FrontMargin:0.###} " + $"decalLayer={(object)(DecalLayerEnum)255} path=creature-contact " + $"count={BloodFXDecalRegistry.BodyCount(channelKey)}/{cap} " + BloodFXDirector.DescribeProjector(val4) + "."); return val4; } } public static class BloodFXBootstrap { private const string ConfigSection = "08 - Blood FX"; internal static ManualLogSource Log { get; private set; } internal static ConfigEntry Enabled { get; private set; } internal static ConfigEntry WorldDecalCap { get; private set; } internal static ConfigEntry BodyDecalCap { get; private set; } internal static ConfigEntry CreatureBodyDecalCap { get; private set; } internal static ConfigEntry FinisherBodyDecalCap { get; private set; } internal static ConfigEntry EnableBundyDamageBlood { get; private set; } internal static ConfigEntry IntensityMultiplier { get; private set; } internal static ConfigEntry DecalFadeSeconds { get; private set; } internal static ConfigEntry MaxEventsPerSecond { get; private set; } internal static ConfigEntry MaxViewerDistance { get; private set; } internal static ConfigEntry SuppressVanillaBlood { get; private set; } internal static ConfigEntry DebugLogging { get; private set; } internal static ConfigEntry EnableDecalsOnRoomMaterials { get; private set; } internal static ConfigEntry IsolateBodyDecalsOnDecalLayer { get; private set; } public static bool BloodFXEnabled => Enabled?.Value ?? false; public static bool BundyDamageBloodEnabled => EnableBundyDamageBlood?.Value ?? true; public static int CreatureBodyDecalCapValue => CreatureBodyDecalCap?.Value ?? 10; public static bool Verbose => DebugLogging?.Value ?? false; public static void Trace(string message) { if (Verbose) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("[Y4NGZCompany.BloodFX] " + message)); } } } internal static void Initialize(ConfigFile config, ManualLogSource logger, Harmony harmony) { Log = logger; TryImportLegacyConfig(config); BindConfig(config); if (!Enabled.Value) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"[Y4NGZCompany.BloodFX] Disabled via config."); } return; } harmony.PatchAll(typeof(BloodFXPatches)); harmony.PatchAll(typeof(BloodFXRoundLifecyclePatches)); harmony.PatchAll(typeof(BloodFXDecalShieldPatches)); ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)("[Y4NGZCompany.BloodFX] Ready: " + $"worldDecalCap={WorldDecalCap.Value}, bodyDecalCap={BodyDecalCap.Value}, " + $"creatureBodyDecalCap={CreatureBodyDecalCap.Value}, " + $"finisherBodyDecalCap={FinisherBodyDecalCap.Value}, " + $"bundyDamageBlood={EnableBundyDamageBlood.Value}, " + $"intensity={IntensityMultiplier.Value}, decalFadeSeconds={DecalFadeSeconds.Value}, " + $"maxEventsPerSecond={MaxEventsPerSecond.Value}, " + $"maxViewerDistance={MaxViewerDistance.Value}, " + $"suppressVanillaBlood={SuppressVanillaBlood.Value}, " + $"debugLogging={DebugLogging.Value}.")); } } private static void TryImportLegacyConfig(ConfigFile config) { string text = "[08 - Blood FX]"; try { string text2 = ((config != null) ? config.ConfigFilePath : null); if (string.IsNullOrEmpty(text2) || (File.Exists(text2) && File.ReadAllText(text2).IndexOf(text, StringComparison.Ordinal) >= 0)) { return; } string path = Path.Combine(Paths.ConfigPath, "com.y4ngz.company.cfg"); if (!File.Exists(path)) { return; } List list = new List(); bool flag = false; string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text3 = array[i].Trim(); if (text3.StartsWith("[", StringComparison.Ordinal) && text3.EndsWith("]", StringComparison.Ordinal)) { flag = string.Equals(text3, text, StringComparison.Ordinal); } else { if (!flag || text3.Length == 0 || text3.StartsWith("#", StringComparison.Ordinal)) { continue; } int num = text3.IndexOf('='); if (num > 0) { string text4 = text3.Substring(0, num).Trim(); string text5 = text3.Substring(num + 1).Trim(); if (text4.Length != 0) { list.Add(text4 + " = " + text5); } } } } if (list.Count == 0) { return; } StringBuilder stringBuilder = new StringBuilder(); if (File.Exists(text2)) { stringBuilder.Append(File.ReadAllText(text2)); stringBuilder.AppendLine(); } else { string directoryName = Path.GetDirectoryName(text2); if (!string.IsNullOrEmpty(directoryName)) { Directory.CreateDirectory(directoryName); } } stringBuilder.AppendLine(text); stringBuilder.AppendLine(); for (int j = 0; j < list.Count; j++) { stringBuilder.AppendLine(list[j]); } File.WriteAllText(text2, stringBuilder.ToString()); config.Reload(); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)($"[Y4NGZCompany.BloodFX] Imported {list.Count} setting(s) from " + "'com.y4ngz.company.cfg' into '" + Path.GetFileName(text2) + "': " + string.Join(", ", list.ToArray()) + ".")); } } catch (Exception ex) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("[Y4NGZCompany.BloodFX] Legacy config import skipped (" + ex.GetType().Name + ": " + ex.Message + "); defaults are in force.")); } } } private static void BindConfig(ConfigFile config) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Expected O, but got Unknown //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Expected O, but got Unknown //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Expected O, but got Unknown //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Expected O, but got Unknown Enabled = config.Bind("08 - Blood FX", "Enabled", true, "Master switch for the blood burst, splatter, and body-blood system."); WorldDecalCap = config.Bind("08 - Blood FX", "World Decal Cap", 48, new ConfigDescription("Most wall and floor blood decals alive at once. Decals last the whole round; past this many the oldest is recycled. Set to 0 to place none.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 256), Array.Empty())); BodyDecalCap = config.Bind("08 - Blood FX", "Body Decal Cap Per Player", 4, new ConfigDescription("Most blood decals carried on one player's body. They survive healing and clear on respawn or round end. Set to 0 to disable body blood.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 12), Array.Empty())); CreatureBodyDecalCap = config.Bind("08 - Blood FX", "Creature Body Decal Cap", 10, new ConfigDescription("Most blood decals carried on one creature body. Creature blood has its own channel so it cannot evict player hit or finisher blood. Set to 0 to disable it.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 24), Array.Empty())); FinisherBodyDecalCap = config.Bind("08 - Blood FX", "Finisher Body Decal Cap", 6, new ConfigDescription("Most blood decals carried in one player's finisher channel. This is separate from ordinary hit blood and clears with the other body channels on respawn or round end. Set to 0 to disable finisher body blood.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 16), Array.Empty())); EnableBundyDamageBlood = config.Bind("08 - Blood FX", "Enable Bundy Damage Blood", true, "Allow damage dealt to Bundy to place replicated blood decals on his body. Set off to keep the general Blood FX system while disabling Bundy damage blood."); IntensityMultiplier = config.Bind("08 - Blood FX", "Intensity Multiplier", 1f, new ConfigDescription("Scales particle emission and the number of decals placed per hit. Damage amount still picks the small/medium/heavy tier.", (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 3f), Array.Empty())); DecalFadeSeconds = config.Bind("08 - Blood FX", "Decal Fade Seconds", 1.2f, new ConfigDescription("How long a recycled blood decal takes to dissolve away. When a cap is reached the oldest decal fades out over this many seconds instead of vanishing mid-view. A fading decal has already left the cap queue, so this never costs you live blood. Set to 0 to remove recycled decals instantly, as before.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty())); MaxEventsPerSecond = config.Bind("08 - Blood FX", "Max Blood Events Per Second", 2f, new ConfigDescription("Most blood events one player can produce per second. Damage-over-time sources (turret bursts, mines, drowning, the chainsaw) otherwise fire up to eight times a second each and churn the whole world decal cap in a couple of seconds. Hits past the limit are skipped entirely — no burst, no splatter, no body blood. Set to 0 for no limit (duplicate-event suppression still applies).", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 20f), Array.Empty())); MaxViewerDistance = config.Bind("08 - Blood FX", "Max Viewer Distance", 100f, new ConfigDescription("Metres from your camera past which a hit places no burst, spray, or wall/floor splatter. Damage runs on every client, so without this a hit across the map spends world decal slots on blood nobody will ever see — evicting the blood in the room you are standing in. Body blood and death puddles ignore this, because both persist and can be walked up to later. Set to 0 to place blood at any distance.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 500f), Array.Empty())); SuppressVanillaBlood = config.Bind("08 - Blood FX", "Suppress Vanilla Blood Pools", true, "Stop the game's own pooled blood splats from spawning so only this system's blood shows. Snow footprints are unaffected."); DebugLogging = config.Bind("08 - Blood FX", "Debug Logging", false, "Write one diagnostic line per blood event (hit, environment splatter, body decal, death) plus a one-time material audit of each player's suit. Hits are rare, so the volume is low; turn it off once the system is dialled in."); EnableDecalsOnRoomMaterials = config.Bind("08 - Blood FX", "Enable Decals On Room Materials", true, "Interior wall and tile materials ship with HDRP's 'Receive Decals' turned off, so blood sprayed onto a wall was placed but never drawn (floors receive, walls did not). When on, the first blood decal to land on a surface flips that material to decal-receiving. Visual-only, applies to static room geometry, and each material is processed once."); IsolateBodyDecalsOnDecalLayer = config.Bind("08 - Blood FX", "Isolate Body Decals On Decal Layer", false, "ESCAPE HATCH, off by default. Puts body blood on HDRP DecalLayer7 and grants that layer only to the wounded player's renderers, so the projector physically cannot paint anything else. Verified broken in the shipped build: the HDRP asset reports supportDecalLayers=true but no shader in the game data carries the DECAL_LAYERS variant, so layered decals draw nothing at all. Held items are kept clean by the item material shield instead. Only turn this on if a future game or mod update ships the missing shader variants."); } } public static class BloodFXCreatureBlood { public static bool IsAvailable { get { ConfigEntry enabled = BloodFXBootstrap.Enabled; if (enabled != null && enabled.Value) { return BloodFXAssets.IsAvailable; } return false; } } public static bool PlayCreatureBleed(int creatureKey, Vector3 point, Vector3 direction, Color tint, int contacts, float scale) { //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_0003: Unknown result type (might be due to invalid IL or missing references) return BloodFXDirector.OnCreatureBled(creatureKey, point, direction, tint, Mathf.Max(1, contacts), Mathf.Max(0.01f, scale)); } } internal static class BloodFXDecalFade { private sealed class FadingDecal { internal readonly GameObject Decal; internal readonly Material[] Materials; internal float Elapsed; internal FadingDecal(GameObject decal, Material[] materials) { Decal = decal; Materials = materials; Elapsed = 0f; } } private sealed class Driver : MonoBehaviour { private void LateUpdate() { Tick(); } } private const float MinDissolveSoftness = 0.25f; private static readonly int DissolveId = Shader.PropertyToID("_Dissolve"); private static readonly int DissolveSoftnessId = Shader.PropertyToID("_DissolveSoftness"); private static readonly List Fading = new List(16); private static Driver _driver; internal static int Count => Fading.Count; internal static void Begin(GameObject decal) { if ((Object)(object)decal == (Object)null) { return; } if ((BloodFXBootstrap.DecalFadeSeconds?.Value ?? 1.2f) <= 0f) { Object.Destroy((Object)(object)decal); return; } Material[] array = CloneDissolvableMaterials(decal); if (array == null) { Object.Destroy((Object)(object)decal); } else if (!EnsureDriver()) { for (int i = 0; i < array.Length; i++) { if ((Object)(object)array[i] != (Object)null) { Object.Destroy((Object)(object)array[i]); } } Object.Destroy((Object)(object)decal); } else { Fading.Add(new FadingDecal(decal, array)); } } internal static void Tick() { if (Fading.Count == 0) { return; } float num = Mathf.Max(0.01f, BloodFXBootstrap.DecalFadeSeconds?.Value ?? 1.2f); float deltaTime = Time.deltaTime; for (int num2 = Fading.Count - 1; num2 >= 0; num2--) { FadingDecal fadingDecal = Fading[num2]; if ((Object)(object)fadingDecal.Decal == (Object)null) { ReleaseMaterials(fadingDecal); Fading.RemoveAt(num2); } else { fadingDecal.Elapsed += deltaTime; float num3 = Mathf.Clamp01(fadingDecal.Elapsed / num); for (int i = 0; i < fadingDecal.Materials.Length; i++) { Material val = fadingDecal.Materials[i]; if ((Object)(object)val != (Object)null) { val.SetFloat(DissolveId, num3); } } if (num3 >= 1f) { Object.Destroy((Object)(object)fadingDecal.Decal); ReleaseMaterials(fadingDecal); Fading.RemoveAt(num2); } } } } internal static void ClearAll() { for (int i = 0; i < Fading.Count; i++) { FadingDecal fadingDecal = Fading[i]; if ((Object)(object)fadingDecal.Decal != (Object)null) { Object.Destroy((Object)(object)fadingDecal.Decal); } ReleaseMaterials(fadingDecal); } Fading.Clear(); } private static Material[] CloneDissolvableMaterials(GameObject decal) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown DecalProjector[] componentsInChildren = decal.GetComponentsInChildren(true); if (componentsInChildren == null || componentsInChildren.Length == 0) { return null; } List list = null; foreach (DecalProjector val in componentsInChildren) { Material val2 = (((Object)(object)val != (Object)null) ? val.material : null); if (!((Object)(object)val2 == (Object)null) && val2.HasProperty(DissolveId)) { Material val3 = new Material(val2); val3.SetFloat(DissolveId, 0f); if (val3.HasProperty(DissolveSoftnessId) && val3.GetFloat(DissolveSoftnessId) < 0.25f) { val3.SetFloat(DissolveSoftnessId, 0.25f); } val.material = val3; if (list == null) { list = new List(componentsInChildren.Length); } list.Add(val3); } } return list?.ToArray(); } private static void ReleaseMaterials(FadingDecal entry) { for (int i = 0; i < entry.Materials.Length; i++) { Material val = entry.Materials[i]; if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } private static bool EnsureDriver() { if ((Object)(object)_driver != (Object)null) { return true; } Transform decalRoot = BloodFXAssets.DecalRoot; if ((Object)(object)decalRoot == (Object)null) { return false; } _driver = ((Component)decalRoot).gameObject.AddComponent(); return (Object)(object)_driver != (Object)null; } } public enum BodyChannel { Hits, Finisher, Creature } public static class BloodFXDecalRegistry { private const int BodyChannelCount = 3; private static readonly Queue WorldDecals = new Queue(64); private static readonly Dictionary> BodyDecals = new Dictionary>(8); private static readonly Dictionary> BodyChannelDecals = new Dictionary>(8); internal static int WorldCount => WorldDecals.Count; internal static void RegisterWorldDecal(GameObject decal) { if ((Object)(object)decal == (Object)null) { return; } int num = Mathf.Max(0, BloodFXBootstrap.WorldDecalCap?.Value ?? 48); if (num == 0) { Object.Destroy((Object)(object)decal); return; } WorldDecals.Enqueue(decal); while (WorldDecals.Count > num) { GameObject val = WorldDecals.Dequeue(); if ((Object)(object)val != (Object)null) { BloodFXDecalFade.Begin(val); } } } public static long ChannelFor(int ownerKey, BodyChannel channel) { if (channel == BodyChannel.Hits) { return ownerKey; } return (long)(0x4000000000000000L | ((ulong)(uint)ownerKey << 8) | (byte)channel); } internal static void RegisterBodyDecal(int playerKey, GameObject decal) { if ((Object)(object)decal == (Object)null) { return; } int num = Mathf.Max(0, BloodFXBootstrap.BodyDecalCap?.Value ?? 4); if (num == 0) { Object.Destroy((Object)(object)decal); return; } if (!BodyDecals.TryGetValue(playerKey, out var value)) { value = new Queue(num + 1); BodyDecals[playerKey] = value; } value.Enqueue(decal); while (value.Count > num) { GameObject val = value.Dequeue(); if ((Object)(object)val != (Object)null) { BloodFXDecalFade.Begin(val); } } } internal static void RegisterBodyDecal(long channelKey, GameObject decal, int cap) { if ((Object)(object)decal == (Object)null) { return; } cap = Mathf.Max(0, cap); if (cap == 0) { Object.Destroy((Object)(object)decal); return; } if (!BodyChannelDecals.TryGetValue(channelKey, out var value)) { value = new Queue(cap + 1); BodyChannelDecals[channelKey] = value; } value.Enqueue(decal); while (value.Count > cap) { GameObject val = value.Dequeue(); if ((Object)(object)val != (Object)null) { BloodFXDecalFade.Begin(val); } } } internal static int BodyCount(int playerKey) { if (!BodyDecals.TryGetValue(playerKey, out var value)) { return 0; } return value.Count; } public static int BodyCount(long channelKey) { if (!BodyChannelDecals.TryGetValue(channelKey, out var value)) { return 0; } return value.Count; } internal static void ClearBody(int playerKey) { if (!BodyDecals.TryGetValue(playerKey, out var value)) { return; } while (value.Count > 0) { GameObject val = value.Dequeue(); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } BodyDecals.Remove(playerKey); } internal static void ClearSupplementalBodyChannels(int ownerKey) { for (int i = 1; i < 3; i++) { ClearBodyChannel(ChannelFor(ownerKey, (BodyChannel)i)); } } public static void ClearBodyChannel(long channelKey) { if (!BodyChannelDecals.TryGetValue(channelKey, out var value)) { return; } while (value.Count > 0) { GameObject val = value.Dequeue(); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } BodyChannelDecals.Remove(channelKey); } internal static void ClearWorld() { while (WorldDecals.Count > 0) { GameObject val = WorldDecals.Dequeue(); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } internal static void ClearAll() { ClearWorld(); BloodFXDecalFade.ClearAll(); foreach (KeyValuePair> bodyDecal in BodyDecals) { Queue value = bodyDecal.Value; while (value.Count > 0) { GameObject val = value.Dequeue(); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } } BodyDecals.Clear(); foreach (KeyValuePair> bodyChannelDecal in BodyChannelDecals) { Queue value2 = bodyChannelDecal.Value; while (value2.Count > 0) { GameObject val2 = value2.Dequeue(); if ((Object)(object)val2 != (Object)null) { Object.Destroy((Object)(object)val2); } } } BodyChannelDecals.Clear(); } } public static class BloodFXDecalSurfaces { public enum DecalReception { OpaqueDecalBand, Transparent, Blocked } private const uint BodyDecalLayerBit = 128u; internal const DecalLayerEnum BodyDecalLayer = (DecalLayerEnum)128; private const int QueueOpaque = 2000; private const int QueueOpaqueDecal = 2225; private const int QueueOpaqueAlphaTest = 2450; private const int QueueOpaqueAlphaTestDecal = 2475; private const int QueueOpaqueMax = 2500; private static readonly int SupportDecalsId = Shader.PropertyToID("_SupportDecals"); private const string DisableDecalsKeyword = "_DISABLE_DECALS"; private static readonly HashSet ShieldedMaterials = new HashSet(); private static readonly HashSet ReceptiveMaterials = new HashSet(); private static readonly HashSet AuditedPlayers = new HashSet(); private static readonly HashSet DecalLayeredPlayers = new HashSet(); private static bool _probed; private static bool _decalLayersSupported; private static bool _itemPrefabsSwept; private static int _shieldedMaterialCount; private static int _receptiveMaterialCount; internal static bool DecalLayersSupported { get { Probe(); return _decalLayersSupported; } } internal static bool BodyDecalLayerIsolationActive { get { ConfigEntry isolateBodyDecalsOnDecalLayer = BloodFXBootstrap.IsolateBodyDecalsOnDecalLayer; if (isolateBodyDecalsOnDecalLayer != null && isolateBodyDecalsOnDecalLayer.Value) { return DecalLayersSupported; } return false; } } internal static string ActivePathName { get { if (!BodyDecalLayerIsolationActive) { return "materials"; } return "decalLayers+materials"; } } internal static void Probe() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (_probed) { return; } _probed = true; RenderPipelineAsset currentRenderPipeline = GraphicsSettings.currentRenderPipeline; HDRenderPipelineAsset val = (HDRenderPipelineAsset)(object)((currentRenderPipeline is HDRenderPipelineAsset) ? currentRenderPipeline : null); if ((Object)(object)val == (Object)null) { ManualLogSource log = BloodFXBootstrap.Log; if (log != null) { log.LogWarning((object)"[Y4NGZCompany.BloodFX] HDRP asset probe: GraphicsSettings.currentRenderPipeline is not an HDRenderPipelineAsset; assuming decal layers unsupported (material path only)."); } return; } RenderPipelineSettings currentPlatformRenderPipelineSettings = val.currentPlatformRenderPipelineSettings; _decalLayersSupported = currentPlatformRenderPipelineSettings.supportDecals && currentPlatformRenderPipelineSettings.supportDecalLayers; ManualLogSource log2 = BloodFXBootstrap.Log; if (log2 != null) { log2.LogInfo((object)("[Y4NGZCompany.BloodFX] HDRP asset probe: " + $"asset='{((Object)val).name}', supportDecals={currentPlatformRenderPipelineSettings.supportDecals}, " + $"supportDecalLayers={currentPlatformRenderPipelineSettings.supportDecalLayers}, " + $"defaultRenderingLayerMask={GraphicsSettings.defaultRenderingLayerMask}, " + "anti-bleed path='" + (_decalLayersSupported ? "decalLayers+materials" : "materials") + "'.")); } } private static bool ShieldMaterial(Material material) { if ((Object)(object)material == (Object)null) { return false; } if (!ShieldedMaterials.Add(((Object)material).GetInstanceID())) { return false; } bool flag = false; if (material.HasProperty(SupportDecalsId) && material.GetFloat(SupportDecalsId) > 0f) { material.SetFloat(SupportDecalsId, 0f); flag = true; } if (!material.IsKeywordEnabled("_DISABLE_DECALS")) { material.EnableKeyword("_DISABLE_DECALS"); flag = true; } int renderQueue = material.renderQueue; if (renderQueue >= 2475 && renderQueue <= 2500) { material.renderQueue = Mathf.Clamp(2450 + (renderQueue - 2475), 2450, 2474); flag = true; } else if (renderQueue >= 2225 && renderQueue < 2450) { material.renderQueue = 2000 + (renderQueue - 2225); flag = true; } if (flag) { _shieldedMaterialCount++; } return flag; } internal static int ShieldHierarchy(GameObject root) { if ((Object)(object)root == (Object)null) { return 0; } int num = 0; Renderer[] componentsInChildren = root.GetComponentsInChildren(true); foreach (Renderer val in componentsInChildren) { if ((Object)(object)val == (Object)null) { continue; } Material[] sharedMaterials = val.sharedMaterials; if (sharedMaterials == null) { continue; } for (int j = 0; j < sharedMaterials.Length; j++) { if (ShieldMaterial(sharedMaterials[j])) { num++; } } } return num; } internal static void SweepItemPrefabs() { if (_itemPrefabsSwept) { return; } StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.allItemsList == (Object)null || instance.allItemsList.itemsList == null) { return; } _itemPrefabsSwept = true; int num = 0; int num2 = 0; List itemsList = instance.allItemsList.itemsList; for (int i = 0; i < itemsList.Count; i++) { Item val = itemsList[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)val.spawnPrefab == (Object)null)) { num++; num2 += ShieldHierarchy(val.spawnPrefab); } } ManualLogSource log = BloodFXBootstrap.Log; if (log != null) { log.LogInfo((object)($"[Y4NGZCompany.BloodFX] Item decal shield: swept {num} item prefabs, " + $"{num2} materials marked no-decals (total shielded={_shieldedMaterialCount}).")); } } internal static void EnsureSurfaceReceivesDecals(Collider collider, StringBuilder verbose) { if ((Object)(object)collider == (Object)null) { return; } Renderer val = ResolveSurfaceRenderer(collider); if ((Object)(object)val == (Object)null) { verbose?.Append(" surfaceMat="); return; } Material[] sharedMaterials = val.sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) { verbose?.Append(" surfaceMat="); return; } bool flag = BloodFXBootstrap.EnableDecalsOnRoomMaterials?.Value ?? true; foreach (Material val2 in sharedMaterials) { if (!((Object)(object)val2 == (Object)null)) { if (verbose != null) { AppendMaterialAudit(verbose, " surfaceMat", val2); } if (flag) { MakeMaterialReceiveDecals(val2, verbose); } } } } private static Renderer ResolveSurfaceRenderer(Collider collider) { Renderer component = ((Component)collider).GetComponent(); if ((Object)(object)component != (Object)null) { return component; } component = ((Component)collider).GetComponentInParent(); if ((Object)(object)component != (Object)null) { return component; } return ((Component)collider).GetComponentInChildren(); } private static void MakeMaterialReceiveDecals(Material material, StringBuilder verbose) { if (ShieldedMaterials.Contains(((Object)material).GetInstanceID())) { verbose?.Append(" [skipped: item-shielded]"); } else if (ReceptiveMaterials.Add(((Object)material).GetInstanceID()) && ApplyDecalReception(material)) { _receptiveMaterialCount++; verbose?.Append(" [FIXED -> receives decals, queue=").Append(material.renderQueue).Append(']'); BloodFXBootstrap.Trace("room material '" + ((Object)material).name + "' (shader '" + (((Object)(object)material.shader != (Object)null) ? ((Object)material.shader).name : "") + "') " + $"switched to decal-receiving; queue={material.renderQueue}, total fixed={_receptiveMaterialCount}."); } } private static bool ApplyDecalReception(Material material) { bool result = false; if (material.HasProperty(SupportDecalsId) && material.GetFloat(SupportDecalsId) <= 0f) { material.SetFloat(SupportDecalsId, 1f); result = true; } if (material.IsKeywordEnabled("_DISABLE_DECALS")) { material.DisableKeyword("_DISABLE_DECALS"); result = true; } int renderQueue = material.renderQueue; if (renderQueue >= 2000 && renderQueue < 2225) { material.renderQueue = 2225 + (renderQueue - 2000); result = true; } else if (renderQueue >= 2450 && renderQueue < 2475) { material.renderQueue = Mathf.Min(2475 + (renderQueue - 2450), 2500); result = true; } return result; } internal static DecalReception ClassifyDecalReception(Material material, out int queue) { queue = 0; if ((Object)(object)material == (Object)null) { return DecalReception.Blocked; } queue = material.renderQueue; if (material.HasProperty(SupportDecalsId) && material.GetFloat(SupportDecalsId) <= 0f) { return DecalReception.Blocked; } if (material.IsKeywordEnabled("_DISABLE_DECALS")) { return DecalReception.Blocked; } if (queue > 2500) { return DecalReception.Transparent; } if (queue < 2225) { return DecalReception.Blocked; } return DecalReception.OpaqueDecalBand; } public static DecalReception MakeInstanceMaterialReceiveDecals(Material material, StringBuilder verbose) { if ((Object)(object)material == (Object)null) { verbose?.Append(" "); return DecalReception.Blocked; } bool flag = ApplyDecalReception(material); int queue; DecalReception decalReception = ClassifyDecalReception(material, out queue); if (verbose != null) { AppendMaterialAudit(verbose, " |", material); verbose.Append(flag ? " [FIXED]" : " [already set]"); switch (decalReception) { case DecalReception.Transparent: verbose.Append(" [UNPROVEN: transparent, queue=").Append(queue).Append(" is above the ") .Append(2225) .Append("..") .Append(2500) .Append(" opaque decal band and cannot be moved into it]"); break; case DecalReception.Blocked: verbose.Append(" [BLOCKED: queue=").Append(queue).Append(" outside ") .Append(2225) .Append("..") .Append(2500) .Append(", or decals still disabled on the material]"); break; } } return decalReception; } internal static void EnsurePlayerDecalLayer(PlayerControllerB player) { if (!((Object)(object)player == (Object)null) && BodyDecalLayerIsolationActive && DecalLayeredPlayers.Add(((Object)player).GetInstanceID())) { ApplyLayerBit((Renderer)(object)player.thisPlayerModel); ApplyLayerBit((Renderer)(object)player.thisPlayerModelLOD1); ApplyLayerBit((Renderer)(object)player.thisPlayerModelLOD2); BloodFXBootstrap.Trace("decal layer: player='" + SafeName(player) + "' body renderers now include DecalLayer7 (bit 0x80)."); } } private static void ApplyLayerBit(Renderer renderer) { if (!((Object)(object)renderer == (Object)null)) { renderer.renderingLayerMask |= 0x80; } } internal static void ResetPlayerState() { AuditedPlayers.Clear(); DecalLayeredPlayers.Clear(); } internal static void AuditPlayerMaterials(PlayerControllerB player) { if (!((Object)(object)player == (Object)null) && BloodFXBootstrap.Verbose && AuditedPlayers.Add(((Object)player).GetInstanceID())) { StringBuilder stringBuilder = new StringBuilder(512); stringBuilder.Append("material audit: player='").Append(SafeName(player)).Append("' "); AppendRendererAudit(stringBuilder, "thisPlayerModel", (Renderer)(object)player.thisPlayerModel); AppendRendererAudit(stringBuilder, "LOD1", (Renderer)(object)player.thisPlayerModelLOD1); AppendRendererAudit(stringBuilder, "LOD2", (Renderer)(object)player.thisPlayerModelLOD2); AppendRendererAudit(stringBuilder, "arms", (Renderer)(object)player.thisPlayerModelArms); BloodFXBootstrap.Trace(stringBuilder.ToString()); } } private static void AppendRendererAudit(StringBuilder builder, string label, Renderer renderer) { builder.Append('[').Append(label).Append(": "); if ((Object)(object)renderer == (Object)null) { builder.Append("null] "); return; } builder.Append("renderingLayerMask=0x").Append(renderer.renderingLayerMask.ToString("X")); Material[] sharedMaterials = renderer.sharedMaterials; if (sharedMaterials == null || sharedMaterials.Length == 0) { builder.Append(" no materials] "); return; } for (int i = 0; i < sharedMaterials.Length; i++) { AppendMaterialAudit(builder, " |", sharedMaterials[i]); } builder.Append("] "); } private static void AppendMaterialAudit(StringBuilder builder, string label, Material material) { builder.Append(label); if ((Object)(object)material == (Object)null) { builder.Append(" "); return; } bool flag = material.HasProperty(SupportDecalsId); float num = (flag ? material.GetFloat(SupportDecalsId) : (-1f)); bool value = material.IsKeywordEnabled("_DISABLE_DECALS"); int renderQueue = material.renderQueue; bool flag2 = renderQueue <= 2500; bool flag3 = renderQueue >= 2225 && renderQueue <= 2500; int queue; bool value2 = ClassifyDecalReception(material, out queue) != DecalReception.Blocked; builder.Append(" '").Append(((Object)material).name).Append("' shader='") .Append(((Object)(object)material.shader != (Object)null) ? ((Object)material.shader).name : "") .Append("' _SupportDecals=") .Append(flag ? num.ToString("0.##") : "n/a") .Append(" DECALS_OFF=") .Append(value) .Append(" queue=") .Append(renderQueue) .Append((!flag2) ? "(transparent)" : (flag3 ? "(decal range)" : "(no-decal range)")) .Append(" receivesDecals=") .Append(value2); } internal static string SafeName(PlayerControllerB player) { if ((Object)(object)player == (Object)null) { return ""; } if (!string.IsNullOrEmpty(player.playerUsername)) { return player.playerUsername; } return "player" + player.playerClientId; } } [HarmonyPatch] internal static class BloodFXDecalShieldPatches { [HarmonyPatch(typeof(StartOfRound), "Start")] [HarmonyPostfix] private static void StartOfRoundStartPostfix() { BloodFXDecalSurfaces.Probe(); BloodFXDecalSurfaces.SweepItemPrefabs(); } [HarmonyPatch(typeof(GrabbableObject), "GrabItemOnClient")] [HarmonyPostfix] private static void GrabItemOnClientPostfix(GrabbableObject __instance) { if (!((Object)(object)__instance == (Object)null)) { int num = BloodFXDecalSurfaces.ShieldHierarchy(((Component)__instance).gameObject); if (num > 0) { BloodFXBootstrap.Trace($"item shield: '{((Object)__instance).name}' -> {num} materials marked no-decals."); } } } [HarmonyPatch(typeof(GrabbableObject), "EquipItem")] [HarmonyPostfix] private static void EquipItemPostfix(GrabbableObject __instance) { if (!((Object)(object)__instance == (Object)null)) { BloodFXDecalSurfaces.ShieldHierarchy(((Component)__instance).gameObject); } } } public static class BloodFXDirector { private enum Tier { Small, Medium, Heavy } public const float DuplicateSuppressionSeconds = 0.12f; private const float SplatterMaxDistance = 2.5f; private const float SurfaceOffset = 0.01f; private const float BodyDecalRadius = 0.26f; private const float BodyDecalDepth = 0.24f; private const float BodyDecalFrontMargin = 0.04f; private const float BodyDecalMinWidth = 0.28f; private const float BodyDecalMaxWidth = 0.42f; private const float BodyDecalHeavyScale = 1.15f; private const float WorldDecalFrontMargin = 0.03f; private const float DuplicateDeathSeconds = 0.5f; private static readonly Dictionary LastHitTime = new Dictionary(8); private static readonly Dictionary LastDeathTime = new Dictionary(8); private static readonly Dictionary LastCreatureBurst = new Dictionary(8); private static readonly Dictionary LastCreatureSpray = new Dictionary(8); private static readonly StringBuilder Diagnostics = new StringBuilder(512); private static RaycastHit _hit; private static bool _loggedWorldProjector; private const int HeavyContactCount = 3; internal static Vector3 PendingHitDirection { get; set; } public static bool FinisherGoreReady { get { ConfigEntry enabled = BloodFXBootstrap.Enabled; if (enabled != null && enabled.Value) { return BloodFXAssets.IsAvailable; } return false; } } internal static void ResetTransientState() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) LastHitTime.Clear(); LastDeathTime.Clear(); LastCreatureBurst.Clear(); LastCreatureSpray.Clear(); PendingHitDirection = Vector3.zero; _loggedWorldProjector = false; BloodFXFinisherGore.StopAll("transient state reset"); BloodFXDecalSurfaces.ResetPlayerState(); } internal static void OnPlayerDamaged(PlayerControllerB player, int damage, Vector3 hitDirection, string clientPath) { //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: 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_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0144: 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_0229: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null || damage <= 0) { return; } ConfigEntry enabled = BloodFXBootstrap.Enabled; if (enabled == null || !enabled.Value || !BloodFXAssets.IsAvailable) { return; } int instanceID = ((Object)player).GetInstanceID(); float realtimeSinceStartup = Time.realtimeSinceStartup; bool flag = ((Vector3)(ref hitDirection)).sqrMagnitude > 0.0001f; float num = ResolveMinimumHitInterval(); if (LastHitTime.TryGetValue(instanceID, out var value) && realtimeSinceStartup - value < num) { string arg = ((realtimeSinceStartup - value < 0.12f) ? "duplicate" : "rate limited"); BloodFXBootstrap.Trace($"hit SUPPRESSED ({arg} {realtimeSinceStartup - value:0.000}s < {num:0.000}s) " + $"player='{BloodFXDecalSurfaces.SafeName(player)}' dmg={damage} path={clientPath}."); return; } LastHitTime[instanceID] = realtimeSinceStartup; Transform val = ResolveTorsoAnchor(player); if ((Object)(object)val == (Object)null) { BloodFXBootstrap.Trace("hit ABORTED (no torso anchor) player='" + BloodFXDecalSurfaces.SafeName(player) + "' " + $"dmg={damage} path={clientPath}."); return; } float intensity = Mathf.Max(0.01f, BloodFXBootstrap.IntensityMultiplier?.Value ?? 1f); Tier tier = GetTier(damage); Vector3 position = val.position; Vector3 direction = (flag ? ((Vector3)(ref hitDirection)).normalized : Vector3.zero); float distance; bool flag2 = IsWithinViewerRange(position, out distance); BloodFXBootstrap.Trace($"hit player='{BloodFXDecalSurfaces.SafeName(player)}' dmg={damage} tier={tier} " + "dirKnown=" + (flag ? "y" : "n") + " path=" + clientPath + " anchor='" + ((Object)val).name + "' viewerDist=" + FormatDistance(distance) + " nearby=" + (flag2 ? "y" : "n") + "."); if (flag2) { PlayBurst(position, direction, tier, intensity, Color.clear, 1f); SprayEnvironment(((Component)player).transform, position, direction, tier, intensity, Color.clear); } AddBodyDecal(player, val, direction, tier); } internal static void OnPlayerKilled(PlayerControllerB player, Vector3 bodyVelocity, bool spawnBody) { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: 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_0198: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)player == (Object)null) { return; } ConfigEntry enabled = BloodFXBootstrap.Enabled; if (enabled == null || !enabled.Value || !BloodFXAssets.IsAvailable) { return; } int instanceID = ((Object)player).GetInstanceID(); float realtimeSinceStartup = Time.realtimeSinceStartup; if (LastDeathTime.TryGetValue(instanceID, out var value) && realtimeSinceStartup - value < 0.5f) { BloodFXBootstrap.Trace($"death SUPPRESSED (duplicate {realtimeSinceStartup - value:0.000}s) player='{BloodFXDecalSurfaces.SafeName(player)}'."); return; } LastDeathTime[instanceID] = realtimeSinceStartup; LastHitTime[instanceID] = realtimeSinceStartup; Transform val = ResolveTorsoAnchor(player); bool flag = (Object)(object)val != (Object)null; Vector3 val2 = (flag ? val.position : (((Component)player).transform.position + Vector3.up)); float intensity = Mathf.Max(0.01f, BloodFXBootstrap.IntensityMultiplier?.Value ?? 1f); Vector3 direction = ((((Vector3)(ref bodyVelocity)).sqrMagnitude > 0.0001f) ? ((Vector3)(ref bodyVelocity)).normalized : Vector3.zero); float distance; bool flag2 = IsWithinViewerRange(val2, out distance); int num = 0; if (flag2) { PlayBurst(val2, direction, Tier.Heavy, intensity, Color.clear, 1f); num = SprayEnvironment(((Component)player).transform, val2, direction, Tier.Heavy, intensity, Color.clear); } if ((Object)(object)val != (Object)null) { AddBodyDecal(player, val, direction, Tier.Heavy); } Vector3 point; bool spawned; bool flag3 = SpawnDeathPuddle(player, out point, out spawned); BloodFXBootstrap.Trace($"death player='{BloodFXDecalSurfaces.SafeName(player)}' spawnBody={spawnBody} " + "burst=" + (flag2 ? "heavy" : "skipped(far)") + " origin=" + Format(val2) + " originSource=" + (flag ? ("spine:" + ((Object)val).name) : "transform+up") + " viewerDist=" + FormatDistance(distance) + " nearby=" + (flag2 ? "y" : "n") + " " + string.Format("splatterDecals={0} puddle={1} ", num, spawned ? "spawned" : "none") + $"puddlePos={Format(point)} puddleRaycastHitFloor={flag3}."); } internal static bool OnCreatureBled(int creatureKey, Vector3 origin, Vector3 direction, Color tint, int contacts, float scale) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: 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_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_0129: 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_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) ConfigEntry enabled = BloodFXBootstrap.Enabled; if (enabled == null || !enabled.Value || !BloodFXAssets.IsAvailable) { return false; } if (!IsWithinViewerRange(origin, out var distance)) { BloodFXBootstrap.Trace($"creature bleed SKIPPED (far {FormatDistance(distance)}) key={creatureKey}."); return false; } float realtimeSinceStartup = Time.realtimeSinceStartup; float value; bool flag = !LastCreatureBurst.TryGetValue(creatureKey, out value) || realtimeSinceStartup - value >= 0.12f; float num = ResolveMinimumHitInterval(); float value2; bool flag2 = !LastCreatureSpray.TryGetValue(creatureKey, out value2) || realtimeSinceStartup - value2 >= num; if (!flag && !flag2) { BloodFXBootstrap.Trace($"creature bleed SUPPRESSED (both gates) key={creatureKey}."); return false; } float intensity = Mathf.Max(0.01f, BloodFXBootstrap.IntensityMultiplier?.Value ?? 1f); Tier tier = ((contacts < 3) ? Tier.Medium : Tier.Heavy); Vector3 direction2 = ((((Vector3)(ref direction)).sqrMagnitude > 0.0001f) ? ((Vector3)(ref direction)).normalized : Vector3.zero); if (flag) { LastCreatureBurst[creatureKey] = realtimeSinceStartup; PlayBurst(origin, direction2, tier, intensity, tint, scale); } int num2 = 0; if (flag2) { LastCreatureSpray[creatureKey] = realtimeSinceStartup; num2 = SprayEnvironment(null, origin, direction2, tier, intensity, tint); } BloodFXBootstrap.Trace($"creature bleed key={creatureKey} tier={tier} contacts={contacts} scale={scale:0.##} " + $"tint=({tint.r:0.##},{tint.g:0.##},{tint.b:0.##},{tint.a:0.##}) " + string.Format("burst={0} spray={1} decals={2} ", flag ? "y" : "gated", flag2 ? "y" : "gated", num2) + "origin=" + Format(origin) + " viewerDist=" + FormatDistance(distance) + "."); if (!flag) { return num2 > 0; } return true; } public static bool BeginFinisherGore(int key, Transform sourceRoot, BloodFXWoundPose pose, in BloodFXFinisherTargets targets, float scale, string label) { if (!FinisherGoreReady) { BloodFXBootstrap.Trace($"finisher gore UNAVAILABLE key={key} label='{label}'."); return false; } return BloodFXFinisherGore.Begin(key, sourceRoot, pose, in targets, scale, label); } public static void EndFinisherGore(int key, string reason) { BloodFXFinisherGore.End(key, reason); } internal static void EmitFinisherPulse(Transform sourceRoot, Vector3 origin, Vector3 direction, float scale, bool placeDecals, bool emitFallbackParticles, in BloodFXFinisherTargets targets) { //IL_0008: 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_0067: 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_003b: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (!FinisherGoreReady) { return; } float distance; bool flag = IsWithinViewerRange(origin, out distance); float intensity = Mathf.Max(0.01f, BloodFXBootstrap.IntensityMultiplier?.Value ?? 1f); int num = 0; if (flag) { if (emitFallbackParticles) { PlayBurst(origin, direction, Tier.Heavy, intensity, Color.clear, scale); } if (placeDecals) { num = SprayEnvironment(sourceRoot, origin, direction, Tier.Medium, intensity, Color.clear); } } int num2 = (placeDecals ? PlaceFinisherBodyDecal(in targets, origin, direction) : 0); BloodFXBootstrap.Trace($"finisher gore pulse origin={Format(origin)} dir={Format(direction)} scale={scale:0.##} " + "particles=" + ((!flag) ? "far" : (emitFallbackParticles ? "burst-spray-fallback" : "dedicated-chute")) + " environmentDecals=" + (placeDecals ? num.ToString() : "budgeted-out") + " bodyDecals=" + (placeDecals ? num2.ToString() : "budgeted-out") + " viewerDist=" + FormatDistance(distance) + "."); } private static int PlaceFinisherBodyDecal(in BloodFXFinisherTargets targets, Vector3 origin, Vector3 outwardDirection) { //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_0095: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB victim = targets.Victim; Transform bodyAnchor = targets.BodyAnchor; if ((Object)(object)victim == (Object)null || (Object)(object)bodyAnchor == (Object)null || !BloodFXAssets.HasBodyPrefabs) { return 0; } int num = Mathf.Max(0, BloodFXBootstrap.FinisherBodyDecalCap?.Value ?? 6); if (num == 0) { return 0; } BloodFXDecalSurfaces.AuditPlayerMaterials(victim); BloodFXDecalSurfaces.EnsurePlayerDecalLayer(victim); long channelKey = BloodFXDecalRegistry.ChannelFor(((Object)victim).GetInstanceID(), BodyChannel.Finisher); bool final = BloodFXDecalRegistry.BodyCount(channelKey) >= num - 1; BloodFXBodyDecals.Profile profile = new BloodFXBodyDecals.Profile(0.26f, 0.28f, 0.42f, 0.24f, 0.04f, 1.15f); return ((Object)(object)BloodFXBodyDecals.PlaceAt(channelKey, bodyAnchor, origin, -outwardDirection, in profile, final, num, $"player='{BloodFXDecalSurfaces.SafeName(victim)}' source=finisher bodyIndex={targets.BodyIndex}", BloodFXAssets.WoundPrefab) != (Object)null) ? 1 : 0; } private static float ResolveMinimumHitInterval() { float num = BloodFXBootstrap.MaxEventsPerSecond?.Value ?? 0f; if (num <= 0f) { return 0.12f; } return Mathf.Max(0.12f, 1f / num); } private static bool IsWithinViewerRange(Vector3 origin, out float distance) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) distance = -1f; float num = BloodFXBootstrap.MaxViewerDistance?.Value ?? 0f; if (num <= 0f) { return true; } Transform val = ResolveViewer(); if ((Object)(object)val == (Object)null) { return true; } distance = Vector3.Distance(val.position, origin); return distance <= num; } private static Transform ResolveViewer() { StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance != (Object)null) { if ((Object)(object)instance.activeCamera != (Object)null) { return ((Component)instance.activeCamera).transform; } PlayerControllerB localPlayerController = instance.localPlayerController; if ((Object)(object)localPlayerController != (Object)null) { if (!((Object)(object)localPlayerController.gameplayCamera != (Object)null)) { return ((Component)localPlayerController).transform; } return ((Component)localPlayerController.gameplayCamera).transform; } } Camera main = Camera.main; if (!((Object)(object)main != (Object)null)) { return null; } return ((Component)main).transform; } private static void PlayBurst(Vector3 origin, Vector3 direction, Tier tier, float intensity, Color tint, float scale) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: 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_003d: 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_003f: 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_004a: Unknown result type (might be due to invalid IL or missing references) float emissionMultiplier = TierEmission(tier) * intensity; BloodFXAssets.TakeBurst()?.Play(origin, Quaternion.identity, emissionMultiplier, tint, scale); if (!(((Vector3)(ref direction)).sqrMagnitude <= 0.0001f)) { BloodFXAssets.TakeSpray()?.Play(origin, Quaternion.LookRotation(direction, Vector3.up), emissionMultiplier, tint, scale); } } private static int SprayEnvironment(Transform sourceRoot, Vector3 origin, Vector3 direction, Tier tier, float intensity, Color tint) { //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) //IL_009b: 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_0085: 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_00a2: 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_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) if (Mathf.Max(0, BloodFXBootstrap.WorldDecalCap?.Value ?? 48) == 0) { return 0; } int num = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMaskAndDefault : (-1)); int num2 = TierRayCount(tier); int num3 = Mathf.Max(1, Mathf.RoundToInt((float)TierDecalBudget(tier) * intensity)); int num4 = 0; int num5 = 0; int num6 = 0; bool verbose = BloodFXBootstrap.Verbose; if (verbose) { Diagnostics.Length = 0; } for (int i = 0; i < num2; i++) { if (num4 >= num3) { break; } Vector3 val = ((i < TierFloorRays(tier)) ? Vector3.Slerp(Vector3.down, Random.onUnitSphere, 0.25f) : BiasedHemisphere(direction)); if (!Physics.Raycast(origin, val, ref _hit, 2.5f, num, (QueryTriggerInteraction)1)) { continue; } num5++; if (IsRejectedSurface(((RaycastHit)(ref _hit)).collider, sourceRoot, out var reason)) { num6++; if (verbose) { Diagnostics.Append(" {rejected '").Append(((Object)(object)((RaycastHit)(ref _hit)).collider != (Object)null) ? ((Object)((RaycastHit)(ref _hit)).collider).name : "").Append("' reason=") .Append(reason) .Append('}'); } continue; } bool flag = ((RaycastHit)(ref _hit)).normal.y > 0.5f; if (SpawnSurfaceDecal(((RaycastHit)(ref _hit)).point, ((RaycastHit)(ref _hit)).normal, tier, tint)) { num4++; Collider collider = ((RaycastHit)(ref _hit)).collider; if (verbose) { int num7 = (((Object)(object)collider != (Object)null) ? ((Component)collider).gameObject.layer : (-1)); Diagnostics.Append(" {'").Append(((Object)(object)collider != (Object)null) ? ((Object)collider).name : "").Append("' layer=") .Append(num7) .Append('(') .Append((num7 >= 0) ? LayerMask.LayerToName(num7) : "n/a") .Append(')') .Append(" surface=") .Append(flag ? "floor" : "wall") .Append(" n=") .Append(Format(((RaycastHit)(ref _hit)).normal)); } BloodFXDecalSurfaces.EnsureSurfaceReceivesDecals(collider, verbose ? Diagnostics : null); if (verbose) { Diagnostics.Append('}'); } } } if (verbose) { BloodFXBootstrap.Trace($"splatter tier={tier} rays={num2} rayHits={num5} rejected={num6} decals={num4}" + ((Diagnostics.Length > 0) ? (" ->" + Diagnostics) : " -> none")); } return num4; } private static bool IsRejectedSurface(Collider collider, Transform sourceRoot, out string reason) { if ((Object)(object)collider == (Object)null) { reason = "null_collider"; return true; } Transform transform = ((Component)collider).transform; if ((Object)(object)sourceRoot != (Object)null && transform.IsChildOf(sourceRoot)) { reason = "hit_source"; return true; } if ((Object)(object)((Component)collider).GetComponentInParent() != (Object)null) { reason = "grabbable_item"; return true; } if ((Object)(object)((Component)collider).GetComponentInParent() != (Object)null) { reason = "other_player"; return true; } if ((Object)(object)((Component)collider).GetComponentInParent() != (Object)null) { reason = "enemy"; return true; } reason = null; return false; } private static Vector3 BiasedHemisphere(Vector3 direction) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_002c: 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_0033: 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) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Random.onUnitSphere; if (((Vector3)(ref direction)).sqrMagnitude <= 0.0001f) { return val; } if (Vector3.Dot(val, direction) < 0f) { val = Vector3.Reflect(val, direction); } Vector3 val2 = Vector3.Slerp(val, direction, 0.35f); return ((Vector3)(ref val2)).normalized; } private static bool SpawnSurfaceDecal(Vector3 point, Vector3 normal, Tier tier, Color tint) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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_0061: 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_0072: 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_0079: 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_0083: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) bool flag = Random.value < TierSpotsWeight(tier); GameObject val = (flag ? BloodFXAssets.SpotsPrefab : BloodFXAssets.WallPrefab); val = (((Object)(object)val != (Object)null) ? val : (flag ? BloodFXAssets.WallPrefab : BloodFXAssets.SpotsPrefab)); if ((Object)(object)val == (Object)null) { return false; } Quaternion val2 = ProjectAlong(-normal); val2 *= Quaternion.AngleAxis(Random.Range(0f, 360f), Vector3.forward); GameObject val3 = Object.Instantiate(val, point + normal * 0.01f, val2, BloodFXAssets.DecalRoot); float num = (flag ? Random.Range(0.4f, 1.4f) : Random.Range(0.7f, 1.8f)); ApplyProjectorScale(val3, num, num * 0.5f, 0.03f, (DecalLayerEnum)255); BloodFXTint.ApplyDecalTint(val3, tint); if (!_loggedWorldProjector && BloodFXBootstrap.Verbose) { _loggedWorldProjector = true; BloodFXBootstrap.Trace("first world decal " + DescribeProjector(val3) + "."); } BloodFXDecalRegistry.RegisterWorldDecal(val3); return true; } private static bool SpawnDeathPuddle(PlayerControllerB player, out Vector3 point, out bool spawned) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001f: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_00f2: 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_009e: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) spawned = false; point = (((Object)(object)player != (Object)null) ? ((Component)player).transform.position : Vector3.zero); GameObject puddlePrefab = BloodFXAssets.PuddlePrefab; if ((Object)(object)puddlePrefab == (Object)null || (Object)(object)player == (Object)null) { return false; } int num = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMaskAndDefault : (-1)); Vector3 val = ((Component)player).transform.position + Vector3.up * 0.6f; Vector3 val2 = Vector3.up; bool result = false; if (Physics.Raycast(val, Vector3.down, ref _hit, 4f, num, (QueryTriggerInteraction)1)) { point = ((RaycastHit)(ref _hit)).point; val2 = ((RaycastHit)(ref _hit)).normal; result = true; } Quaternion val3 = ProjectAlong(-val2) * Quaternion.AngleAxis(Random.Range(0f, 360f), Vector3.forward); GameObject obj = Object.Instantiate(puddlePrefab, point + val2 * 0.01f, val3, BloodFXAssets.DecalRoot); ApplyProjectorScale(obj, Random.Range(1.4f, 2.2f), 0.6f, 0.03f, (DecalLayerEnum)255); BloodFXDecalRegistry.RegisterWorldDecal(obj); spawned = true; return result; } private static void AddBodyDecal(PlayerControllerB player, Transform anchor, Vector3 direction, Tier tier) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: 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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0111: 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_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0139: 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_013e: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0187: 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_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Max(0, BloodFXBootstrap.BodyDecalCap?.Value ?? 4); if (num == 0 || !BloodFXAssets.HasBodyPrefabs) { return; } int instanceID = ((Object)player).GetInstanceID(); bool flag = BloodFXDecalRegistry.BodyCount(instanceID) >= num - 1; GameObject bodyPrefab = BloodFXAssets.BodyPrefab; if (!((Object)(object)bodyPrefab == (Object)null)) { BloodFXDecalSurfaces.AuditPlayerMaterials(player); BloodFXDecalSurfaces.EnsurePlayerDecalLayer(player); Vector3 val = (Vector3)((((Vector3)(ref direction)).sqrMagnitude > 0.0001f) ? new Vector3(0f - direction.x, 0f, 0f - direction.z) : Vector3.zero); if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f) { ((Vector3)(ref val))..ctor(Random.Range(-1f, 1f), 0f, Random.Range(-1f, 1f)); } if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); Vector3 val2 = anchor.position + val * 0.26f + Vector3.up * Random.Range(-0.16f, 0.12f); Quaternion val3 = ProjectAlong(-val) * Quaternion.AngleAxis(Random.Range(0f, 360f), Vector3.forward); GameObject val4 = Object.Instantiate(bodyPrefab, val2, val3, anchor); float num2 = (flag ? 0.48299998f : (Random.Range(0.28f, 0.42f) * ((tier == Tier.Heavy) ? 1.15f : 1f))); DecalLayerEnum val5 = (DecalLayerEnum)(BloodFXDecalSurfaces.BodyDecalLayerIsolationActive ? 128 : 255); ApplyProjectorScale(val4, num2, 0.24f, 0.04f, val5); BloodFXDecalRegistry.RegisterBodyDecal(instanceID, val4); BloodFXBootstrap.Trace("body decal player='" + BloodFXDecalSurfaces.SafeName(player) + "' bone='" + ((Object)anchor).name + "' pos=" + Format(val2) + " prefab='" + ((Object)bodyPrefab).name + "' " + $"size={num2:0.###}x{num2:0.###}x{0.24f:0.###} frontMargin={0.04f:0.###} " + $"decalLayer={val5} path={BloodFXDecalSurfaces.ActivePathName} " + $"count={BloodFXDecalRegistry.BodyCount(instanceID)}/{num} " + DescribeProjector(val4) + "."); } } public static GameObject PlaceGateProbeDecal(int ownerKey, Transform anchor, Vector3 outward, float size, float standoff, float depth, string label) { //IL_006a: 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_0094: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_0086: 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_0118: 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) if ((Object)(object)anchor == (Object)null || !BloodFXAssets.HasBodyPrefabs) { return null; } GameObject bodyPrefab = BloodFXAssets.BodyPrefab; if ((Object)(object)bodyPrefab == (Object)null) { return null; } int num = Mathf.Max(0, BloodFXBootstrap.CreatureBodyDecalCap?.Value ?? 10); if (num == 0) { ManualLogSource log = BloodFXBootstrap.Log; if (log != null) { log.LogWarning((object)("[Y4NGZCompany.BloodFX] gate probe decal '" + label + "' refused: 'Creature Body Decal Cap' is 0, so any decal placed would be destroyed on registration.")); } return null; } long channelKey = BloodFXDecalRegistry.ChannelFor(ownerKey, BodyChannel.Creature); Vector3 val = outward; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); Vector3 val2 = anchor.position + val * standoff; GameObject val3 = Object.Instantiate(bodyPrefab, val2, ProjectAlong(-val), anchor); ApplyProjectorScale(val3, size, depth, 0.04f, (DecalLayerEnum)255); BloodFXDecalRegistry.RegisterBodyDecal(channelKey, val3, num); ManualLogSource log2 = BloodFXBootstrap.Log; if (log2 != null) { log2.LogInfo((object)("[Y4NGZCompany.BloodFX] gate probe decal '" + label + "' bone='" + ((Object)anchor).name + "' pos=" + Format(val2) + " facing=" + Format(val) + " prefab='" + ((Object)bodyPrefab).name + "' " + $"size={size:0.###} standoff={standoff:0.###} depth={depth:0.###} " + $"count={BloodFXDecalRegistry.BodyCount(channelKey)} " + DescribeProjector(val3) + ".")); } return val3; } internal static Quaternion ProjectAlong(Vector3 projectionAxis) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: 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) Vector3 val = ((((Vector3)(ref projectionAxis)).sqrMagnitude > 0.0001f) ? ((Vector3)(ref projectionAxis)).normalized : Vector3.down); Vector3 val2 = ((Mathf.Abs(Vector3.Dot(val, Vector3.up)) > 0.95f) ? Vector3.forward : Vector3.up); return Quaternion.LookRotation(val, val2); } internal static void ApplyProjectorScale(GameObject instance, float width, float depth, float frontMargin, DecalLayerEnum decalLayerMask) { //IL_003c: 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_0078: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)instance == (Object)null)) { DecalProjector val = instance.GetComponent(); if ((Object)(object)val == (Object)null) { val = instance.GetComponentInChildren(true); } if (!((Object)(object)val == (Object)null)) { float num = Mathf.Max(0.05f, depth); val.size = new Vector3(width, width, num); val.pivot = new Vector3(0f, 0f, num * 0.5f - Mathf.Min(frontMargin, num * 0.5f)); val.scaleMode = (DecalScaleMode)0; val.decalLayerMask = decalLayerMask; val.startAngleFade = 180f; val.endAngleFade = 180f; val.fadeFactor = 1f; } } } private static Transform ResolveTorsoAnchor(PlayerControllerB player) { if ((Object)(object)player == (Object)null) { return null; } if ((Object)(object)player.upperSpine != (Object)null) { return player.upperSpine; } if ((Object)(object)player.lowerSpine != (Object)null) { return player.lowerSpine; } if ((Object)(object)player.playerBodyAnimator != (Object)null) { return ((Component)player.playerBodyAnimator).transform; } return ((Component)player).transform; } public static string Format(Vector3 value) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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) return $"({value.x:0.##},{value.y:0.##},{value.z:0.##})"; } private static string FormatDistance(float distance) { if (!(distance < 0f)) { return distance.ToString("0.#") + "m"; } return "n/a"; } internal static string DescribeProjector(GameObject instance) { //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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_011e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)instance == (Object)null) { return "projector="; } DecalProjector val = instance.GetComponent(); if ((Object)(object)val == (Object)null) { val = instance.GetComponentInChildren(true); } if ((Object)(object)val == (Object)null) { return "projector="; } Material material = val.material; return $"projector[valid={val.IsValid()} " + "mat='" + (((Object)(object)material != (Object)null) ? ((Object)material).name : "") + "' shader='" + (((Object)(object)material != (Object)null && (Object)(object)material.shader != (Object)null) ? ((Object)material.shader).name : "") + "' size=" + Format(val.size) + " pivot=" + Format(val.pivot) + " " + $"drawDist={val.drawDistance:0.#} fade={val.fadeFactor:0.##} " + $"scaleMode={val.scaleMode} decalLayers={val.decalLayerMask} " + $"enabled={((Behaviour)val).enabled && ((Component)val).gameObject.activeInHierarchy}]"; } private static Tier GetTier(int damage) { if (damage < 15) { return Tier.Small; } if (damage > 40) { return Tier.Heavy; } return Tier.Medium; } private static float TierEmission(Tier tier) { return tier switch { Tier.Small => 0.6f, Tier.Medium => 1f, _ => 1.8f, }; } private static int TierRayCount(Tier tier) { return tier switch { Tier.Small => 6, Tier.Medium => 8, _ => 10, }; } private static int TierFloorRays(Tier tier) { if (tier != Tier.Heavy) { return 2; } return 3; } private static int TierDecalBudget(Tier tier) { return tier switch { Tier.Small => 2, Tier.Medium => 4, _ => 7, }; } private static float TierSpotsWeight(Tier tier) { return tier switch { Tier.Small => 0.85f, Tier.Medium => 0.5f, _ => 0.2f, }; } } public delegate bool BloodFXWoundPose(out Vector3 origin, out Vector3 direction); public readonly struct BloodFXFinisherTargets { public readonly PlayerControllerB Victim; public readonly Transform BodyAnchor; public readonly int BodyIndex; public BloodFXFinisherTargets(PlayerControllerB victim, Transform bodyAnchor, int bodyIndex) { Victim = victim; BodyAnchor = bodyAnchor; BodyIndex = bodyIndex; } } internal sealed class BloodFXFinisherGore : MonoBehaviour { private const float PulseIntervalSeconds = 0.3f; private const float DecalIntervalSeconds = 0.85f; private const int DecalPulseBudget = 3; private const float MaxDurationSeconds = 8f; private const string DriverObjectName = "Y4NGZCompany_BloodFXFinisherGore"; private static readonly Dictionary Active = new Dictionary(2); private static readonly List ScratchKeys = new List(2); private int _key; private string _label; private Transform _sourceRoot; private BloodFXWoundPose _pose; private BloodFXFinisherTargets _targets; private float _scale; private Vector3 _origin; private Vector3 _direction; private float _nextPulseAt; private float _nextDecalAt; private float _expireAt; private int _decalsRemaining; private int _pulses; private bool _ending; private bool _usingDedicatedChute; internal static bool Begin(int key, Transform sourceRoot, BloodFXWoundPose pose, in BloodFXFinisherTargets targets, float scale, string label) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0087: 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_008f: 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_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) if (pose == null) { return false; } if (!pose(out var origin, out var direction)) { BloodFXBootstrap.Trace($"finisher gore ABORTED (no initial pose) key={key} label='{label}'."); return false; } End(key, "restarted"); GameObject val = new GameObject("Y4NGZCompany_BloodFXFinisherGore"); Object.DontDestroyOnLoad((Object)val); BloodFXFinisherGore bloodFXFinisherGore = val.AddComponent(); bloodFXFinisherGore._key = key; bloodFXFinisherGore._label = label; bloodFXFinisherGore._sourceRoot = sourceRoot; bloodFXFinisherGore._pose = pose; bloodFXFinisherGore._targets = targets; bloodFXFinisherGore._scale = Mathf.Max(0.05f, scale); bloodFXFinisherGore._origin = origin; bloodFXFinisherGore._direction = Normalize(direction); bloodFXFinisherGore._expireAt = Time.time + 8f; bloodFXFinisherGore._nextPulseAt = Time.time + 0.3f; bloodFXFinisherGore._nextDecalAt = Time.time + 0.85f; bloodFXFinisherGore._decalsRemaining = Mathf.Max(0, 2); bloodFXFinisherGore.UpdateDedicatedChutePose(); bloodFXFinisherGore._usingDedicatedChute = bloodFXFinisherGore.TryStartDedicatedChute(); Active[key] = bloodFXFinisherGore; bloodFXFinisherGore.Pulse(placeDecals: true); BloodFXBootstrap.Trace($"finisher gore START key={key} label='{label}' scale={bloodFXFinisherGore._scale:0.##} " + $"origin=({origin.x:0.##},{origin.y:0.##},{origin.z:0.##}) " + $"dir=({bloodFXFinisherGore._direction.x:0.##},{bloodFXFinisherGore._direction.y:0.##},{bloodFXFinisherGore._direction.z:0.##}) " + "particles=" + (bloodFXFinisherGore._usingDedicatedChute ? "dedicated-chute" : "burst-spray-fallback") + "."); return true; } internal static void End(int key, string reason) { if (Active.Count != 0 && Active.TryGetValue(key, out var value)) { Active.Remove(key); if ((Object)(object)value != (Object)null) { value.Teardown(reason); } } } internal static void StopAll(string reason) { if (Active.Count == 0) { return; } ScratchKeys.Clear(); foreach (KeyValuePair item in Active) { ScratchKeys.Add(item.Key); } for (int i = 0; i < ScratchKeys.Count; i++) { End(ScratchKeys[i], reason); } ScratchKeys.Clear(); Active.Clear(); } private void Update() { //IL_006b: 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) //IL_0075: 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_0093: 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) if (_ending) { return; } float time = Time.time; if (time >= _expireAt) { Active.Remove(_key); Teardown("max duration"); return; } if (_pose != null) { if (!_pose(out var origin, out var direction)) { Active.Remove(_key); Teardown("pose lost"); return; } if (IsFinite(origin)) { _origin = origin; } if (((Vector3)(ref direction)).sqrMagnitude > 0.0001f && IsFinite(direction)) { _direction = ((Vector3)(ref direction)).normalized; } } if (_usingDedicatedChute) { UpdateDedicatedChutePose(); } if (!(time < _nextPulseAt)) { _nextPulseAt = time + 0.3f; bool flag = _decalsRemaining > 0 && time >= _nextDecalAt; if (flag) { _decalsRemaining--; _nextDecalAt = time + 0.85f; } Pulse(flag); } } private void Pulse(bool placeDecals) { //IL_0015: 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) _pulses++; BloodFXDirector.EmitFinisherPulse(_sourceRoot, _origin, _direction, _scale, placeDecals, !_usingDedicatedChute, in _targets); } private bool TryStartDedicatedChute() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) GameObject[] chutePrefabs = BloodFXAssets.ChutePrefabs; if (chutePrefabs == null || chutePrefabs.Length == 0) { return false; } int num = 0; foreach (GameObject val in chutePrefabs) { if ((Object)(object)val == (Object)null) { continue; } GameObject val2 = Object.Instantiate(val, ((Component)this).transform, false); ((Object)val2).name = ((Object)val).name + "_FinisherChute"; val2.SetActive(false); Transform transform = val2.transform; transform.localPosition = Vector3.zero; transform.localRotation = Quaternion.identity; transform.localScale = val.transform.localScale; ParticleSystem[] componentsInChildren = val2.GetComponentsInChildren(true); if (componentsInChildren.Length == 0) { Object.Destroy((Object)(object)val2); continue; } foreach (ParticleSystem obj in componentsInChildren) { MainModule main = obj.main; ((MainModule)(ref main)).loop = true; ((MainModule)(ref main)).stopAction = (ParticleSystemStopAction)0; ((MainModule)(ref main)).playOnAwake = true; CollisionModule collision = obj.collision; ((CollisionModule)(ref collision)).enabled = false; obj.Clear(false); } val2.SetActive(true); componentsInChildren[0].Play(true); num++; } if (num > 0) { BloodFXBootstrap.Trace($"finisher dedicated chute START prefabs={num} key={_key}."); } return num > 0; } private void UpdateDedicatedChutePose() { //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_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_003d: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Normalize(_direction); Vector3 val2 = ((Mathf.Abs(Vector3.Dot(val, Vector3.up)) > 0.95f) ? Vector3.forward : Vector3.up); ((Component)this).transform.SetPositionAndRotation(_origin, Quaternion.LookRotation(val, val2)); ((Component)this).transform.localScale = Vector3.one * _scale; } private void Teardown(string reason) { if (!_ending) { _ending = true; BloodFXBootstrap.Trace($"finisher gore END key={_key} label='{_label}' pulses={_pulses} reason={reason}."); Object.Destroy((Object)(object)((Component)this).gameObject); } } private static Vector3 Normalize(Vector3 value) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!(((Vector3)(ref value)).sqrMagnitude > 0.0001f) || !IsFinite(value)) { return Vector3.forward; } return ((Vector3)(ref value)).normalized; } private static bool IsFinite(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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_0027: 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_0041: Unknown result type (might be due to invalid IL or missing references) if (!float.IsNaN(value.x) && !float.IsInfinity(value.x) && !float.IsNaN(value.y) && !float.IsInfinity(value.y) && !float.IsNaN(value.z)) { return !float.IsInfinity(value.z); } return false; } } [HarmonyPatch] internal static class BloodFXPatches { [HarmonyPatch(typeof(PlayerControllerB), "DamagePlayer")] [HarmonyPrefix] private static void DamagePlayerPrefix(PlayerControllerB __instance, out int __state) { __state = (((Object)(object)__instance != (Object)null) ? __instance.health : 0); } [HarmonyPatch(typeof(PlayerControllerB), "DamagePlayer")] [HarmonyPostfix] private static void DamagePlayerPostfix(PlayerControllerB __instance, int damageNumber, Vector3 force, int __state) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0035: 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_0029: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance == (Object)null) && __instance.health < __state) { Vector3 hitDirection = force; if (((Vector3)(ref hitDirection)).sqrMagnitude <= 0.0001f) { hitDirection = BloodFXDirector.PendingHitDirection; BloodFXDirector.PendingHitDirection = Vector3.zero; } BloodFXDirector.OnPlayerDamaged(__instance, damageNumber, hitDirection, "owner"); } } [HarmonyPatch(typeof(PlayerControllerB), "DamageOnOtherClients")] [HarmonyPostfix] private static void DamageOnOtherClientsPostfix(PlayerControllerB __instance, int damageNumber) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)__instance == (Object)null) && !((NetworkBehaviour)__instance).IsOwner) { BloodFXDirector.OnPlayerDamaged(__instance, damageNumber, Vector3.zero, "observer"); } } [HarmonyPatch(typeof(PlayerControllerB), "DamagePlayerFromOtherClientClientRpc")] [HarmonyPrefix] private static void DamageFromOtherClientPrefix(Vector3 hitDirection) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) BloodFXDirector.PendingHitDirection = hitDirection; } [HarmonyPatch(typeof(PlayerControllerB), "DamagePlayerFromOtherClientClientRpc")] [HarmonyPostfix] private static void DamageFromOtherClientPostfix(PlayerControllerB __instance, int damageAmount, Vector3 hitDirection) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) BloodFXDirector.OnPlayerDamaged(__instance, damageAmount, hitDirection, "fromOtherClient"); BloodFXDirector.PendingHitDirection = Vector3.zero; } [HarmonyPatch(typeof(PlayerControllerB), "KillPlayerClientRpc")] [HarmonyPostfix] private static void KillPlayerClientRpcPostfix(int playerId, bool spawnBody, Vector3 bodyVelocity) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) StartOfRound instance = StartOfRound.Instance; if (!((Object)(object)instance == (Object)null) && instance.allPlayerScripts != null && playerId >= 0 && playerId < instance.allPlayerScripts.Length) { BloodFXDirector.OnPlayerKilled(instance.allPlayerScripts[playerId], bodyVelocity, spawnBody); } } [HarmonyPatch(typeof(PlayerControllerB), "DropBlood")] [HarmonyPrefix] private static void DropBloodPrefix(ref bool leaveBlood) { ConfigEntry enabled = BloodFXBootstrap.Enabled; if (enabled != null && enabled.Value) { ConfigEntry suppressVanillaBlood = BloodFXBootstrap.SuppressVanillaBlood; if (suppressVanillaBlood == null || suppressVanillaBlood.Value) { leaveBlood = false; } } } [HarmonyPatch(typeof(PlayerControllerB), "ResetPlayerBloodObjects")] [HarmonyPostfix] private static void ResetPlayerBloodObjectsPostfix(PlayerControllerB __instance, bool resetBodyBlood) { if (!((Object)(object)__instance == (Object)null) && resetBodyBlood) { BloodFXDecalRegistry.ClearBody(((Object)__instance).GetInstanceID()); BloodFXDecalRegistry.ClearSupplementalBodyChannels(((Object)__instance).GetInstanceID()); } } } [HarmonyPatch] internal static class BloodFXRoundLifecyclePatches { [HarmonyPatch(typeof(StartOfRound), "ShipLeave")] [HarmonyPostfix] private static void OnShipLeave() { ClearRound(); } [HarmonyPatch(typeof(StartOfRound), "ShipLeaveAutomatically")] [HarmonyPostfix] private static void OnShipLeaveAutomatically() { ClearRound(); } [HarmonyPatch(typeof(StartOfRound), "ReviveDeadPlayers")] [HarmonyPostfix] private static void OnReviveDeadPlayers() { ClearRound(); } [HarmonyPatch(typeof(GameNetworkManager), "Disconnect")] [HarmonyPostfix] private static void OnDisconnect() { ClearRound(); } private static void ClearRound() { BloodFXDecalRegistry.ClearAll(); BloodFXDirector.ResetTransientState(); } } internal static class BloodFXTint { private const string MainColorProperty = "_MainColor"; private const string SecondaryColorProperty = "_SecondaryColor"; private const string SpecularColorProperty = "_SpecularColor"; private const float SecondaryFactor = 0.51f; private const float SpecularWhiteMix = 0.7f; private static readonly Dictionary Instances = new Dictionary(8); internal static bool IsTinted(Color tint) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return tint.a > 0f; } internal static void ApplyDecalTint(GameObject instance, Color tint) { //IL_0009: 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) if ((Object)(object)instance == (Object)null || !IsTinted(tint)) { return; } DecalProjector val = instance.GetComponent(); if ((Object)(object)val == (Object)null) { val = instance.GetComponentInChildren(true); } if (!((Object)(object)val == (Object)null)) { Material val2 = Resolve(val.material, tint); if ((Object)(object)val2 != (Object)null) { val.material = val2; } } } private static Material Resolve(Material source, Color tint) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_003b: 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_005a: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_00c0: 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_00dc: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)source == (Object)null || !IsTinted(tint)) { return source; } long key = Key(source, tint); if (Instances.TryGetValue(key, out var value) && (Object)(object)value != (Object)null) { return value; } Material val = new Material(source) { name = ((Object)source).name + "_Y4NGZTint", hideFlags = (HideFlags)61 }; SetHue(val, "_MainColor", tint); SetHue(val, "_SecondaryColor", tint * 0.51f); SetHue(val, "_SpecularColor", Color.Lerp(tint, Color.white, 0.7f)); Instances[key] = val; BloodFXBootstrap.Trace("tint material created source='" + ((Object)source).name + "' " + $"tint=({tint.r:0.##},{tint.g:0.##},{tint.b:0.##}) cached={Instances.Count}."); return val; } private static void SetHue(Material material, string property, Color hue) { //IL_000c: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (material.HasProperty(property)) { float a = material.GetColor(property).a; material.SetColor(property, new Color(hue.r, hue.g, hue.b, a)); } } private static long Key(Material source, Color tint) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) int num = (Quantise(tint.r) << 16) | (Quantise(tint.g) << 8) | Quantise(tint.b); return (long)(((ulong)(uint)((Object)source).GetInstanceID() << 32) | (uint)num); } private static int Quantise(float channel) { return Mathf.Clamp(Mathf.RoundToInt(channel * 255f), 0, 255); } } internal sealed class BloodFXVfxGraphSpike : MonoBehaviour { private const float RuntimeReportDelaySeconds = 1.5f; private const float VisibleLifetimeSeconds = 10f; private const string DriverName = "Y4NGZCompany_VFXGraphSpike"; private static bool _armed; private GameObject _prefab; private string _manifestName; private GameObject _instance; private VisualEffect _effect; private float _reportAt; private float _destroyAt; private bool _spawned; private bool _reported; internal static void Arm(GameObject prefab, string manifestName) { //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown if (!_armed) { _armed = true; VisualEffect val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponentInChildren(true) : null); ManualLogSource log = BloodFXBootstrap.Log; if (log != null) { log.LogWarning((object)("[Y4NGZCompany.BloodFX.VFXGraphSpike] manifest='" + manifestName + "' LoadAsset=" + (((Object)(object)prefab != (Object)null) ? "NON_NULL" : "NULL") + " VisualEffectComponent=" + (((Object)(object)val != (Object)null) ? "present" : "missing") + " VisualEffectAsset=" + (((Object)(object)val != (Object)null && (Object)(object)val.visualEffectAsset != (Object)null) ? ((Object)val.visualEffectAsset).name : "") + ".")); } if (!((Object)(object)prefab == (Object)null) && !((Object)(object)val == (Object)null) && !((Object)(object)val.visualEffectAsset == (Object)null)) { GameObject val2 = new GameObject("Y4NGZCompany_VFXGraphSpike"); Object.DontDestroyOnLoad((Object)val2); BloodFXVfxGraphSpike bloodFXVfxGraphSpike = val2.AddComponent(); bloodFXVfxGraphSpike._prefab = prefab; bloodFXVfxGraphSpike._manifestName = manifestName; } } } private void Update() { if (!_spawned) { TrySpawnForLocalCamera(); return; } float unscaledTime = Time.unscaledTime; if (!_reported && unscaledTime >= _reportAt) { _reported = true; bool flag = (Object)(object)_effect != (Object)null; ManualLogSource log = BloodFXBootstrap.Log; if (log != null) { log.LogWarning((object)("[Y4NGZCompany.BloodFX.VFXGraphSpike] " + $"RUNTIME manifest='{_manifestName}' componentAlive={flag} " + $"enabled={flag && ((Behaviour)_effect).enabled} " + "culled=" + (flag ? _effect.culled.ToString() : "") + " aliveParticleCount=" + (flag ? _effect.aliveParticleCount.ToString() : "") + ". Human verdict: look 1.5m in front of the local camera for the arterial VFX.")); } } if (!(unscaledTime < _destroyAt)) { if ((Object)(object)_instance != (Object)null) { Object.Destroy((Object)(object)_instance); } Object.Destroy((Object)(object)((Component)this).gameObject); } } private void TrySpawnForLocalCamera() { //IL_0087: 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_009c: 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) StartOfRound instance = StartOfRound.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.localPlayerController == (Object)null) { return; } Camera gameplayCamera = instance.localPlayerController.gameplayCamera; if ((Object)(object)gameplayCamera == (Object)null) { return; } _instance = Object.Instantiate(_prefab, ((Component)gameplayCamera).transform, false); ((Object)_instance).name = ((Object)_prefab).name + "_RuntimeSpike"; Transform transform = _instance.transform; transform.localPosition = new Vector3(0f, -0.25f, 1.5f); transform.localRotation = Quaternion.identity; transform.localScale = Vector3.one * 0.5f; _instance.SetActive(true); _effect = _instance.GetComponentInChildren(true); if ((Object)(object)_effect == (Object)null || (Object)(object)_effect.visualEffectAsset == (Object)null) { ManualLogSource log = BloodFXBootstrap.Log; if (log != null) { log.LogWarning((object)"[Y4NGZCompany.BloodFX.VFXGraphSpike] RUNTIME wrapper instantiated but its VisualEffect dependency is missing."); } Object.Destroy((Object)(object)_instance); Object.Destroy((Object)(object)((Component)this).gameObject); return; } ((Behaviour)_effect).enabled = true; _effect.pause = false; _effect.playRate = 1f; _effect.Reinit(); _effect.Play(); _spawned = true; _reportAt = Time.unscaledTime + 1.5f; _destroyAt = Time.unscaledTime + 10f; ManualLogSource log2 = BloodFXBootstrap.Log; if (log2 != null) { log2.LogWarning((object)"[Y4NGZCompany.BloodFX.VFXGraphSpike] RUNTIME START: arterial graph placed 1.5m in front of the local camera for 10 seconds."); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }