using System; using System.Collections.Generic; using System.Diagnostics; 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 HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering.Universal; using UnityEngine.SceneManagement; using UnityEngine.VFX; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("BloodColor")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("BloodColor")] [assembly: AssemblyTitle("BloodColor")] [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 BloodColor { public enum TintMode { Replace, Multiply, Vanilla } internal class BloodConfig { public const string DefaultHex = "B01C1C"; private const int DefaultR = 176; private const int DefaultG = 28; private const int DefaultB = 28; public ConfigEntry Hex; public ConfigEntry R; public ConfigEntry G; public ConfigEntry B; public ConfigEntry Mode; public ConfigEntry Opacity; public ConfigEntry Intensity; public ConfigEntry EnableBlood; public ConfigEntry DoParticles; public ConfigEntry DoDecals; public ConfigEntry LogEffects; public Color Tint = Color.red; private bool _syncing; public float AlphaFactor => ((Mode.Value == TintMode.Vanilla) ? 1f : Tint.a) * Opacity.Value; public event Action Changed; public BloodConfig(ConfigFile file) { //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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Expected O, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown Hex = file.Bind("Blood", "Color", "B01C1C", "Blood colour as a hex code, like B01C1C. Paste one from any colour picker. RGB, RRGGBB or RRGGBBAA, # optional, and names like 'red' or 'lime' work too.\nThis and the R/G/B sliders below are the same setting - change either one and the other follows.\nHeads up: you can't type letters into this box from the in-game Mod Menu. Use the sliders there, or edit this file."); R = file.Bind("Blood", "R", 176, new ConfigDescription("Red, 0-255. Moves with the Color setting.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 255), Array.Empty())); G = file.Bind("Blood", "G", 28, new ConfigDescription("Green, 0-255. Moves with the Color setting.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 255), Array.Empty())); B = file.Bind("Blood", "B", 28, new ConfigDescription("Blue, 0-255. Moves with the Color setting.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 255), Array.Empty())); Mode = file.Bind("Blood", "Mode", TintMode.Replace, "Replace = use your colour as-is.\nMultiply = tint the original, which keeps the built-in shading but can only darken.\nVanilla = don't recolour. Opacity, Intensity and EnableBlood still work."); Opacity = file.Bind("Blood", "Opacity", 1f, new ConfigDescription("How visible blood is. 1 is normal, 0 is invisible.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); Intensity = file.Bind("Blood", "Intensity", 1f, new ConfigDescription("Brightness. Above 1 goes HDR, which is how you get blood that glows. A hex code can't describe anything brighter than 1 on its own, hence this.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 8f), Array.Empty())); EnableBlood = file.Bind("Blood", "EnableBlood", true, "Turn this off for no blood at all. Separate from the game's own Blood option - either one being off means no blood."); DoParticles = file.Bind("Targets", "Particles", true, "Recolour the blood spray."); DoDecals = file.Bind("Targets", "Decals", true, "Recolour the splatter left on the ground."); LogEffects = file.Bind("Advanced", "LogEffects", false, "Dump the blood effects the mod found to the BepInEx log - shaders, materials, VFX properties. Turn this on before reporting a problem."); PullFromHex(); file.SettingChanged += delegate(object _, SettingChangedEventArgs e) { if (!_syncing) { if ((object)e.ChangedSetting == Hex) { PullFromHex(); } else if ((object)e.ChangedSetting == R || (object)e.ChangedSetting == G || (object)e.ChangedSetting == B) { PullFromSliders(); } this.Changed?.Invoke(); } }; } private void PullFromHex() { //IL_0046: 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_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (!TryParse(Hex.Value, out var color)) { Plugin.Log.LogWarning((object)("Couldn't read colour '" + Hex.Value + "', using B01C1C instead.")); TryParse("B01C1C", out color); } Tint = color; _syncing = true; R.Value = To255(color.r); G.Value = To255(color.g); B.Value = To255(color.b); _syncing = false; } private void PullFromSliders() { //IL_0042: 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_0079: 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) Tint = new Color((float)R.Value / 255f, (float)G.Value / 255f, (float)B.Value / 255f, Tint.a); _syncing = true; Hex.Value = ((Tint.a >= 1f) ? ColorUtility.ToHtmlStringRGB(Tint) : ColorUtility.ToHtmlStringRGBA(Tint)); _syncing = false; } private static int To255(float v) { return Mathf.Clamp(Mathf.RoundToInt(v * 255f), 0, 255); } public static bool TryParse(string raw, out Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) color = Color.red; if (string.IsNullOrEmpty(raw)) { return false; } string text = raw.Trim(); if (text.Length == 0) { return false; } if (text[0] != '#' && !char.IsLetter(text[0])) { text = "#" + text; } return ColorUtility.TryParseHtmlString(text, ref color); } public Color CombineRgb(Color original) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_0047: 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_006b: 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_008a: 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) Color val = default(Color); switch (Mode.Value) { case TintMode.Replace: ((Color)(ref val))..ctor(Tint.r, Tint.g, Tint.b, original.a); break; case TintMode.Multiply: ((Color)(ref val))..ctor(original.r * Tint.r, original.g * Tint.g, original.b * Tint.b, original.a); break; default: val = original; break; } float value = Intensity.Value; return new Color(val.r * value, val.g * value, val.b * value, original.a); } public Color Combine(Color original) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) Color result = CombineRgb(original); result.a = Mathf.Clamp01(original.a * AlphaFactor); return result; } } internal static class BloodTinter { private class ParticleTarget { public ParticleSystem System; public string Owner; public MinMaxGradient Original; public ParticleSystemRenderer Renderer; public int ColorProp = -1; public Color OriginalMaterialColor; } private class DecalTarget { public string Name; public List Projectors; public Material Material; public int ColorProp = -1; public Color OriginalColor; public float OriginalFade; } private class VfxTarget { public VisualEffect Effect; public string Property; public bool IsGradient; public Color OriginalColor; public Gradient OriginalGradient; } private class Original { public Material Material; public float Fade; } public static readonly string[] ParticleNames = new string[3] { "Blood", "BloodSmall", "BloodDirectional" }; public const string DecalPrefix = "BloodDecal"; private static readonly string[] DecalNames = new string[3] { "BloodDecal1", "BloodDecal2", "BloodDecal3" }; public const string VfxName = "SmallBloodExplosion"; private static readonly string[] ColorProps = new string[5] { "_BaseColor", "_Color", "_TintColor", "_MainColor", "_UnlitColor" }; private static readonly List Particles = new List(); private static readonly List Decals = new List(); private static readonly Dictionary Pristine = new Dictionary(); private static VfxTarget Vfx; private static readonly MaterialPropertyBlock Block = new MaterialPropertyBlock(); private static bool _warnedVfx; private static bool _warnedDecal; private static BloodConfig Cfg => Plugin.Cfg; public static int FindParticles() { //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_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) List list = new List(Particles); Particles.Clear(); Dictionary particles = ParticleManager._particles; if (particles == null || particles.Count == 0) { return 0; } string[] particleNames = ParticleNames; foreach (string text in particleNames) { if (!particles.TryGetValue(text, out var value) || (Object)(object)value?.ParticleSystem == (Object)null) { continue; } ParticleSystem[] componentsInChildren = ((Component)value.ParticleSystem).GetComponentsInChildren(true); foreach (ParticleSystem ps in componentsInChildren) { ParticleTarget particleTarget = list.Find((ParticleTarget x) => x.System == ps); if (particleTarget == null) { ParticleTarget obj = new ParticleTarget { System = ps, Owner = text }; MainModule main = ps.main; obj.Original = Copy(((MainModule)(ref main)).startColor); obj.Renderer = ((Component)ps).GetComponent(); particleTarget = obj; Material val = (Object.op_Implicit((Object)(object)particleTarget.Renderer) ? ((Renderer)particleTarget.Renderer).sharedMaterial : null); if ((Object)(object)val != (Object)null) { particleTarget.ColorProp = FindColorProp(val); if (particleTarget.ColorProp >= 0) { particleTarget.OriginalMaterialColor = val.GetColor(particleTarget.ColorProp); } } } Particles.Add(particleTarget); } } return Particles.Count; } public static int FindDecals() { //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) List list = new List(Decals); Decals.Clear(); Dictionary> decals = DecalManager._decals; if (decals == null || decals.Count == 0) { DestroyMaterials(list); return 0; } string[] decalNames = DecalNames; foreach (string text in decalNames) { if (!decals.TryGetValue(text, out var pool) || pool == null || pool.Count == 0) { continue; } DecalTarget decalTarget = list.Find((DecalTarget x) => x.Projectors == pool); if (decalTarget != null) { Decals.Add(decalTarget); continue; } DecalProjector val = pool[0]; if ((Object)(object)val == (Object)null) { continue; } if (!Pristine.TryGetValue(text, out var value) || (Object)(object)value.Material == (Object)null) { if ((Object)(object)val.material == (Object)null) { continue; } value = new Original { Material = val.material, Fade = val.fadeFactor }; Pristine[text] = value; } DecalTarget decalTarget2 = new DecalTarget { Name = text, Projectors = pool, Material = new Material(value.Material) { name = ((Object)value.Material).name + " (BloodColor)" }, OriginalFade = value.Fade }; decalTarget2.ColorProp = FindColorProp(decalTarget2.Material); if (decalTarget2.ColorProp >= 0) { decalTarget2.OriginalColor = decalTarget2.Material.GetColor(decalTarget2.ColorProp); } else if (!_warnedDecal) { _warnedDecal = true; ManualLogSource log = Plugin.Log; string[] obj = new string[5] { "Decal ", text, " uses shader '", null, null }; Shader shader = decalTarget2.Material.shader; obj[3] = ((shader != null) ? ((Object)shader).name : null); obj[4] = "' and I can't find a colour property on it. Turn on LogEffects and send me the shader name."; log.LogWarning((object)string.Concat(obj)); } foreach (DecalProjector item in pool) { if ((Object)(object)item != (Object)null) { item.material = decalTarget2.Material; } } Decals.Add(decalTarget2); } list.RemoveAll((DecalTarget x) => Decals.Contains(x)); DestroyMaterials(list); return Decals.Count; } public static int FindVfx() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) VfxTarget vfx = Vfx; Vfx = null; Dictionary allVfx = VFXManager._allVfx; if (allVfx == null || !allVfx.TryGetValue("SmallBloodExplosion", out var value) || (Object)(object)value == (Object)null) { return 0; } if (vfx != null && vfx.Effect == value) { Vfx = vfx; return 1; } foreach (string item in ExposedNames(value)) { if (value.HasVector4(item)) { Vfx = new VfxTarget { Effect = value, Property = item, OriginalColor = Color.op_Implicit(value.GetVector4(item)) }; break; } if (value.HasGradient(item)) { Vfx = new VfxTarget { Effect = value, Property = item, IsGradient = true, OriginalGradient = value.GetGradient(item) }; break; } } if (Vfx == null) { if (!_warnedVfx) { _warnedVfx = true; Plugin.Log.LogDebug((object)"SmallBloodExplosion exposes no colour property; gib burst left alone."); } return 0; } Plugin.Log.LogInfo((object)("SmallBloodExplosion: using property '" + Vfx.Property + "'.")); return 1; } public static void ResetVfxWarning() { _warnedVfx = false; } private static List ExposedNames(VisualEffect effect) { //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_005d: 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_0075: 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) List list = new List(); VisualEffectAsset visualEffectAsset = effect.visualEffectAsset; if ((Object)(object)visualEffectAsset == (Object)null) { return list; } List list2 = new List(); try { visualEffectAsset.GetExposedProperties(list2); } catch (Exception ex) { Plugin.Log.LogDebug((object)("Couldn't read exposed properties off SmallBloodExplosion: " + ex.Message)); return list; } foreach (VFXExposedProperty item in list2) { if (item.type == typeof(Vector4) || item.type == typeof(Color) || item.type == typeof(Gradient)) { list.Add(item.name); } } return list; } public static void Apply() { if (Cfg.DoParticles.Value) { ApplyParticles(); } if (Cfg.DoDecals.Value) { ApplyDecals(); } if (Vfx != null && (Object)(object)Vfx.Effect != (Object)null) { ApplyVfx(); } } private static void ApplyParticles() { //IL_002f: 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_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_0064: 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_006e: 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) foreach (ParticleTarget particle in Particles) { if (!((Object)(object)particle.System == (Object)null)) { MainModule main = particle.System.main; ((MainModule)(ref main)).startColor = BuildStartColor(particle.Original); if (!((Object)(object)particle.Renderer == (Object)null) && particle.ColorProp >= 0) { Color val = Cfg.CombineRgb(particle.OriginalMaterialColor); val.a = particle.OriginalMaterialColor.a; ((Renderer)particle.Renderer).GetPropertyBlock(Block); Block.SetColor(particle.ColorProp, val); ((Renderer)particle.Renderer).SetPropertyBlock(Block); } } } } private static void ApplyDecals() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) foreach (DecalTarget decal in Decals) { if ((Object)(object)decal.Material != (Object)null && decal.ColorProp >= 0) { Color val = Cfg.Combine(decal.OriginalColor); val.a = decal.OriginalColor.a; decal.Material.SetColor(decal.ColorProp, val); } float fadeFactor = Mathf.Clamp01(decal.OriginalFade * Cfg.AlphaFactor); foreach (DecalProjector projector in decal.Projectors) { if ((Object)(object)projector != (Object)null) { projector.fadeFactor = fadeFactor; } } } } private static void ApplyVfx() { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (Vfx.IsGradient) { Vfx.Effect.SetGradient(Vfx.Property, TintGradient(Vfx.OriginalGradient)); } else { Vfx.Effect.SetVector4(Vfx.Property, Color.op_Implicit(Cfg.Combine(Vfx.OriginalColor))); } } private static MinMaxGradient BuildStartColor(MinMaxGradient src) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected I4, but got Unknown //IL_0068: 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_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_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_00e4: 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) bool flag = Cfg.Mode.Value == TintMode.Vanilla; float alphaFactor = Cfg.AlphaFactor; float scale = (flag ? 1f : (1f / Mathf.Max(0.0001f, MaxLuminance(src)))); ParticleSystemGradientMode mode = ((MinMaxGradient)(ref src)).mode; switch ((int)mode) { case 0: return new MinMaxGradient(Flatten(((MinMaxGradient)(ref src)).color, flag, scale, alphaFactor)); case 2: return new MinMaxGradient(Flatten(((MinMaxGradient)(ref src)).colorMin, flag, scale, alphaFactor), Flatten(((MinMaxGradient)(ref src)).colorMax, flag, scale, alphaFactor)); case 1: case 4: { MinMaxGradient result = default(MinMaxGradient); ((MinMaxGradient)(ref result))..ctor(FlattenGradient(((MinMaxGradient)(ref src)).gradient, flag, scale, alphaFactor)); ((MinMaxGradient)(ref result)).mode = ((MinMaxGradient)(ref src)).mode; return result; } case 3: return new MinMaxGradient(FlattenGradient(((MinMaxGradient)(ref src)).gradientMin, flag, scale, alphaFactor), FlattenGradient(((MinMaxGradient)(ref src)).gradientMax, flag, scale, alphaFactor)); default: return src; } } private static Color Flatten(Color c, bool vanilla, float scale, float alpha) { //IL_0000: 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_0011: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Clamp01(c.a * alpha); if (vanilla) { return new Color(c.r, c.g, c.b, num); } float num2 = Mathf.Clamp01(((Color)(ref c)).grayscale * scale); return new Color(num2, num2, num2, num); } private static Gradient FlattenGradient(Gradient src, bool vanilla, float scale, float alpha) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: 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_00b5: 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_00c8: Expected O, but got Unknown if (src == null) { return null; } GradientColorKey[] colorKeys = src.colorKeys; GradientColorKey[] array = (GradientColorKey[])(object)new GradientColorKey[colorKeys.Length]; for (int i = 0; i < colorKeys.Length; i++) { Color val = Flatten(colorKeys[i].color, vanilla, scale, 1f); array[i] = new GradientColorKey(val, colorKeys[i].time); } GradientAlphaKey[] alphaKeys = src.alphaKeys; GradientAlphaKey[] array2 = (GradientAlphaKey[])(object)new GradientAlphaKey[alphaKeys.Length]; for (int j = 0; j < alphaKeys.Length; j++) { array2[j] = new GradientAlphaKey(Mathf.Clamp01(alphaKeys[j].alpha * alpha), alphaKeys[j].time); } Gradient val2 = new Gradient { mode = src.mode }; val2.SetKeys(array, array2); return val2; } private static float MaxLuminance(MinMaxGradient g) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected I4, but got Unknown //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) //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_0045: 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) ParticleSystemGradientMode mode = ((MinMaxGradient)(ref g)).mode; Color val; switch ((int)mode) { case 0: val = ((MinMaxGradient)(ref g)).color; return ((Color)(ref val)).grayscale; case 2: { val = ((MinMaxGradient)(ref g)).colorMin; float grayscale = ((Color)(ref val)).grayscale; val = ((MinMaxGradient)(ref g)).colorMax; return Mathf.Max(grayscale, ((Color)(ref val)).grayscale); } case 1: case 4: return MaxLuminance(((MinMaxGradient)(ref g)).gradient); case 3: return Mathf.Max(MaxLuminance(((MinMaxGradient)(ref g)).gradientMin), MaxLuminance(((MinMaxGradient)(ref g)).gradientMax)); default: return 1f; } } private static float MaxLuminance(Gradient g) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (g == null) { return 1f; } float num = 0f; GradientColorKey[] colorKeys = g.colorKeys; for (int i = 0; i < colorKeys.Length; i++) { GradientColorKey val = colorKeys[i]; num = Mathf.Max(num, ((Color)(ref val.color)).grayscale); } return num; } private static Gradient TintGradient(Gradient src) { //IL_002a: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //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_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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown if (src == null) { return null; } GradientColorKey[] colorKeys = src.colorKeys; GradientColorKey[] array = (GradientColorKey[])(object)new GradientColorKey[colorKeys.Length]; for (int i = 0; i < colorKeys.Length; i++) { array[i] = new GradientColorKey(Cfg.CombineRgb(colorKeys[i].color), colorKeys[i].time); } GradientAlphaKey[] alphaKeys = src.alphaKeys; GradientAlphaKey[] array2 = (GradientAlphaKey[])(object)new GradientAlphaKey[alphaKeys.Length]; float alphaFactor = Cfg.AlphaFactor; for (int j = 0; j < alphaKeys.Length; j++) { array2[j] = new GradientAlphaKey(Mathf.Clamp01(alphaKeys[j].alpha * alphaFactor), alphaKeys[j].time); } Gradient val = new Gradient { mode = src.mode }; val.SetKeys(array, array2); return val; } private static MinMaxGradient Copy(MinMaxGradient src) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected I4, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_005d: Unknown result type (might be due to invalid IL or missing references) ParticleSystemGradientMode mode = ((MinMaxGradient)(ref src)).mode; switch (mode - 1) { case 0: case 3: { MinMaxGradient result = default(MinMaxGradient); ((MinMaxGradient)(ref result))..ctor(Copy(((MinMaxGradient)(ref src)).gradient)); ((MinMaxGradient)(ref result)).mode = ((MinMaxGradient)(ref src)).mode; return result; } case 2: return new MinMaxGradient(Copy(((MinMaxGradient)(ref src)).gradientMin), Copy(((MinMaxGradient)(ref src)).gradientMax)); default: return src; } } private static Gradient Copy(Gradient src) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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_0029: Expected O, but got Unknown if (src == null) { return null; } Gradient val = new Gradient { mode = src.mode }; val.SetKeys(src.colorKeys, src.alphaKeys); return val; } private static int FindColorProp(Material mat) { string[] colorProps = ColorProps; for (int i = 0; i < colorProps.Length; i++) { int num = Shader.PropertyToID(colorProps[i]); if (mat.HasProperty(num)) { return num; } } return -1; } public static void Clear() { Particles.Clear(); DestroyMaterials(Decals); Decals.Clear(); Pristine.Clear(); Vfx = null; } private static void DestroyMaterials(IEnumerable targets) { foreach (DecalTarget target in targets) { if ((Object)(object)target.Material != (Object)null) { Object.Destroy((Object)(object)target.Material); } } } public static void LogEffects() { //IL_00af: 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) StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("---- BloodColor: what I found ----"); stringBuilder.AppendLine($"Particles ({Particles.Count}):"); foreach (ParticleTarget particle in Particles) { if (!((Object)(object)particle.System == (Object)null)) { Material val = (Object.op_Implicit((Object)(object)particle.Renderer) ? ((Renderer)particle.Renderer).sharedMaterial : null); stringBuilder.AppendLine(" [" + particle.Owner + "] " + PathOf(((Component)particle.System).transform)); stringBuilder.AppendLine($" startColor : {((MinMaxGradient)(ref particle.Original)).mode} {DescribeStartColor(particle.Original)}"); stringBuilder.AppendLine(" material : " + (Object.op_Implicit((Object)(object)val) ? ((Object)val).name : "none")); stringBuilder.AppendLine(" shader : " + ((Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.shader)) ? ((Object)val.shader).name : "none")); stringBuilder.AppendLine(" mat colour : " + MaterialColor(val)); } } stringBuilder.AppendLine($"Decals ({Decals.Count}):"); foreach (DecalTarget decal in Decals) { stringBuilder.AppendLine($" [{decal.Name}] {decal.Projectors.Count} projectors"); stringBuilder.AppendLine(" shader : " + ((Object.op_Implicit((Object)(object)decal.Material) && Object.op_Implicit((Object)(object)decal.Material.shader)) ? ((Object)decal.Material.shader).name : "none")); stringBuilder.AppendLine(" colour : " + ((decal.ColorProp >= 0) ? ((object)Unsafe.As(ref decal.OriginalColor)/*cast due to .constrained prefix*/).ToString() : "no property found")); stringBuilder.AppendLine($" fade : {decal.OriginalFade}"); } stringBuilder.AppendLine("VFX SmallBloodExplosion:"); stringBuilder.AppendLine((Vfx == null) ? " no colour property found" : (" using '" + Vfx.Property + "'")); stringBuilder.Append(VfxProperties()); Plugin.Log.LogInfo((object)stringBuilder.ToString()); } private static string DescribeStartColor(MinMaxGradient g) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected I4, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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) ParticleSystemGradientMode mode = ((MinMaxGradient)(ref g)).mode; switch ((int)mode) { case 0: return ((object)((MinMaxGradient)(ref g)).color/*cast due to .constrained prefix*/).ToString(); case 2: return $"{((MinMaxGradient)(ref g)).colorMin} .. {((MinMaxGradient)(ref g)).colorMax}"; case 1: case 4: { Gradient gradient = ((MinMaxGradient)(ref g)).gradient; return $"{((gradient != null) ? gradient.colorKeys.Length : 0)} colour key(s)"; } case 3: { Gradient gradientMin = ((MinMaxGradient)(ref g)).gradientMin; object arg = ((gradientMin != null) ? gradientMin.colorKeys.Length : 0); Gradient gradientMax = ((MinMaxGradient)(ref g)).gradientMax; return $"{arg} + {((gradientMax != null) ? gradientMax.colorKeys.Length : 0)} key(s)"; } default: return string.Empty; } } private static string MaterialColor(Material mat) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mat == (Object)null) { return "none"; } string[] colorProps = ColorProps; foreach (string text in colorProps) { int num = Shader.PropertyToID(text); if (mat.HasProperty(num)) { return $"{text} = {mat.GetColor(num)}"; } } return "no known colour property"; } private static string VfxProperties() { //IL_00ee: 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_00fb: 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) Dictionary allVfx = VFXManager._allVfx; if (allVfx == null || !allVfx.TryGetValue("SmallBloodExplosion", out var value) || (Object)(object)value == (Object)null) { return " (not in this scene)\n"; } StringBuilder stringBuilder = new StringBuilder(); VisualEffectAsset visualEffectAsset = value.visualEffectAsset; stringBuilder.AppendLine(" asset : " + (((Object)(object)visualEffectAsset != (Object)null) ? ((Object)visualEffectAsset).name : "none")); if ((Object)(object)visualEffectAsset == (Object)null) { return stringBuilder.ToString(); } List list = new List(); try { visualEffectAsset.GetExposedProperties(list); } catch (Exception ex) { stringBuilder.AppendLine(" (couldn't read exposed properties: " + ex.Message + ")"); return stringBuilder.ToString(); } if (list.Count == 0) { stringBuilder.AppendLine(" exposes nothing at all - this effect can't be recoloured"); return stringBuilder.ToString(); } stringBuilder.AppendLine($" exposes ({list.Count}):"); foreach (VFXExposedProperty item in list) { stringBuilder.AppendLine(" " + item.name + " : " + item.type?.Name); } return stringBuilder.ToString(); } private static string PathOf(Transform t) { StringBuilder stringBuilder = new StringBuilder(((Object)t).name); Transform parent = t.parent; while ((Object)(object)parent != (Object)null) { stringBuilder.Insert(0, ((Object)parent).name + "/"); parent = parent.parent; } return stringBuilder.ToString(); } } [HarmonyPatch(typeof(ParticleManager), "Awake")] internal static class ParticleManagerAwake { private static void Postfix() { Plugin.QueueRefresh(); } } [HarmonyPatch(typeof(DecalManager), "Awake")] internal static class DecalManagerAwake { private static void Postfix() { Plugin.QueueRefresh(); } } [HarmonyPatch(typeof(VFXManager), "Awake")] internal static class VfxManagerAwake { private static void Postfix() { Plugin.QueueRefresh(); } } [HarmonyPatch(typeof(ParticleManager), "Play", new Type[] { typeof(string), typeof(Vector3), typeof(Vector3) })] internal static class ParticleManagerPlay { private static bool Prefix(string type) { if (!Plugin.BloodOff) { return true; } string[] particleNames = BloodTinter.ParticleNames; foreach (string b in particleNames) { if (string.Equals(type, b, StringComparison.Ordinal)) { return false; } } return true; } } [HarmonyPatch(typeof(DecalManager), "SpawnDecal")] internal static class DecalManagerSpawnDecal { private static bool Prefix(string decalName) { if (Plugin.BloodOff && decalName != null) { return !decalName.StartsWith("BloodDecal", StringComparison.Ordinal); } return true; } } [HarmonyPatch(typeof(VFXManager), "Play", new Type[] { typeof(string), typeof(Vector3), typeof(Vector3) })] internal static class VfxManagerPlay { private static bool Prefix(string type) { if (Plugin.BloodOff) { return !string.Equals(type, "SmallBloodExplosion", StringComparison.Ordinal); } return true; } } [BepInPlugin("com.zoe.howtofish.bloodcolor", "BloodColor", "1.0.0")] public class Plugin : BaseUnityPlugin { public const string Guid = "com.zoe.howtofish.bloodcolor"; public const string Name = "BloodColor"; public const string Version = "1.0.0"; internal static ManualLogSource Log; internal static BloodConfig Cfg; internal static bool BloodOff; private Harmony _harmony; private static bool _refreshQueued; private bool _loggedFailure; private void Awake() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Cfg = new BloodConfig(((BaseUnityPlugin)this).Config); BloodOff = !Cfg.EnableBlood.Value; Cfg.Changed += OnConfigChanged; try { _harmony = new Harmony("com.zoe.howtofish.bloodcolor"); _harmony.PatchAll(typeof(Plugin).Assembly); } catch (Exception ex) { Log.LogError((object)("Couldn't apply patches, BloodColor is doing nothing this session. Probably a game update moved something, see Patches.cs.\n" + ex)); return; } SceneManager.sceneLoaded += OnSceneLoaded; QueueRefresh(); Log.LogInfo((object)string.Format("{0} {1} loaded. Colour {2}, mode {3}.", "BloodColor", "1.0.0", Cfg.Hex.Value, Cfg.Mode.Value)); } private void OnDestroy() { SceneManager.sceneLoaded -= OnSceneLoaded; if (Cfg != null) { Cfg.Changed -= OnConfigChanged; } BloodTinter.Clear(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } internal static void QueueRefresh() { _refreshQueued = true; } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { QueueRefresh(); } private void Update() { if (!_refreshQueued) { return; } _refreshQueued = false; try { Refresh(); } catch (Exception arg) { if (!_loggedFailure) { _loggedFailure = true; Log.LogError((object)$"Refresh failed, not going to report this again: {arg}"); } } } private void Refresh() { int num = BloodTinter.FindParticles(); int num2 = BloodTinter.FindDecals(); int num3 = BloodTinter.FindVfx(); if (num + num2 + num3 != 0) { BloodTinter.Apply(); string arg = ((num3 > 0) ? $", {num3} vfx" : string.Empty); Log.LogInfo((object)$"Recoloured {num} particle systems, {num2} decal pools{arg}."); if (Cfg.LogEffects.Value) { BloodTinter.LogEffects(); } } } private void OnConfigChanged() { BloodOff = !Cfg.EnableBlood.Value; try { BloodTinter.Apply(); if (Cfg.LogEffects.Value) { BloodTinter.LogEffects(); } Log.LogInfo((object)($"Config changed: {Cfg.Hex.Value}, {Cfg.Mode.Value}, opacity {Cfg.Opacity.Value}, " + string.Format("intensity {0}, blood {1}.", Cfg.Intensity.Value, Cfg.EnableBlood.Value ? "on" : "off"))); } catch (Exception arg) { Log.LogError((object)$"Couldn't apply that config change: {arg}"); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }